initial commit

This commit is contained in:
longpanda
2020-04-05 00:07:50 +08:00
parent 2090c6fa97
commit 05a1b863a6
487 changed files with 114253 additions and 0 deletions

View File

@@ -0,0 +1,162 @@
ACKNOWLEDGEMENTS
Thanks to everyone who have downloaded Squashfs. I appreciate people
using it, and any feedback you have.
The following have provided useful feedback, which has guided
some of the extra features in squashfs. This is a randomly ordered
(roughly in chronological order) list, which is updated when
I remember...
Acknowledgements for Squashfs 4.3
---------------------------------
Thanks to Bruno Wolff III and Andy Lutomirski for useful feedback
during the long development process of Squashfs 4.3.
Acknowledgements for Squashfs 4.2
---------------------------------
Thanks to Lasse Collin (http://tukaani.org/xz/) for mainlining XZ
decompression support.
Acknowledgements for Squashfs 4.1
---------------------------------
Thanks to Chan Jeong <chan.jeong@lge.com> and LG for the patches to support LZO
compression.
Acknowledgements for Squashfs 4.0
---------------------------------
Thanks to Tim Bird and CELF (Consumer Electronics Linux Forum) for helping
fund mainstreaming of Squashfs into the 2.6.29 kernel and the
changes to the Squashfs tools to support the new 4.0 file system layout.
Acknowledgements for Squashfs-3.3
------------------------------------
Peter Korsgaard and others sent patches updating Squashfs to changes in the
VFS interface for 2.6.22/2.6.23/2.6.24-rc1. Peter also sent some small patches
for the Squashfs kernel code.
Vito Di Leo sent a patch extending Mksquashfs to support regex filters.
While his patched worked, it unfortunately made it easy to make Mksquashfs
perform unpredictably with poorly choosen regex expressions. It, however,
encouraged myself to add support for wildcard pattern matching and regex
filters in a different way.
Acknowledgements for Squashfs-3.2-r2
------------------------------------
Junjiro Okajima discovered a couple of SMP issues, thanks.
Junjiro Okajima and Tomas Matejicek have produced some good LZMA patches
for Squashfs.
Acknowledgements for Squashfs-3.2
---------------------------------
Peter Korsgaard sent a patch updating Squashfs to changes in the VFS interface
in Linux 2.6.20.
Acknowledgements for Squashfs-3.1
---------------------------------
Kenneth Duda and Ed Swierk of Arastra Inc. identified numerous bugs with
Squashfs, and provided patches which were the basis for some of the
fixes. In particular they identified the fragment rounding bug, the
NFS bug, the initrd bug, and helped identify the 4K stack overflow bug.
Scott James Remnant (Ubuntu) also identified the fragment rounding bug,
and he also provided a patch.
Ming Zhang identified the Lseek bug in Mksquashfs. His tests on the
performance of Mksquashfs on SMP systems encouraged the rewrite of
Mksquashfs.
Peter Korsgaard, Daniel Olivera and Zilvinas Valinskas noticed
Squashfs 3.0 didn't compile on Linux-2.6.18-rc[1-4] due to changes
in the Linux VFS interfaces, and provided patches.
Tomas Matejicek (SLAX) suggested the -force option on Unsquashfs, and noticed
Unsquashfs didn't return the correct exit status.
Yann Le Doare reported a kernel oops and provided a Qemu image that led
to the identification of the simultaneously accessing multiply mounted Squashfs
filesystems bug.
Older acknowledgements
----------------------
Mark Robson - pointed out early on that initrds didn't work
Adam Warner - pointed out that greater than 2GB filesystems didn't work.
John Sutton - raised the problem when archiving the entire filesystem
(/) there was no way to prevent /proc being archived. This prompted
exclude files.
Martin Mueller (LinuxTV) - noticed that the filesystem length in the
superblock doesn't match the output filesystem length. This is due to
padding to a 4K boundary. This prompted the addition of the -nopad option.
He also reported a problem where 32K block filesystems hung when used as
initrds.
Arkadiusz Patyk (Polish Linux Distribution - PLD) reported a problem where 32K
block filesystems hung when used as a root filesystem mounted as a loopback
device.
David Fox (Lindows) noticed that the exit codes returned by Mksquashfs were
wrong. He also noticed that a lot of time was spent in the duplicate scan
routine.
Cameron Rich complained that Squashfs did not support FIFOs or sockets.
Steve Chadsey and Thomas Weissmuller noticed that files larger than the
available memory could not be compressed by Mksquashfs.
"Ptwahyu" and "Hoan" (I have no full names and I don't like giving people's
email addresses), noticed that Mksquashfs 1.3 SEGV'd occasionally. Even though
I had already noticed this bug, it is useful to be informed by other people.
Don Elwell, Murray Jensen and Cameron Rich, have all sent in patches. Thanks,
I have not had time to do anything about them yet...
Drew Scott Daniels has been a good advocate for Squashfs.
Erik Andersen has made some nice suggestions, unfortunately, I have
not had time to implement anything.
Artemiy I. Pavlov has written a useful LDP mini-howto for Squashfs
(http://linuxdoc.artemio.net/squashfs).
Yves Combe reported the Apple G5 bug, when using Squashfs for
his PPC Knoppix-mib livecd project.
Jaco Greeff (mklivecd project, and maintainer of the Mandrake
squashfs-tools package) suggested the new mksquashfs -ef option, and the
standalone build for mksquashfs.
Mike Schaudies made a donation.
Arkadiusz Patyk from the Polish Linux Distribution reported that Squashfs
didn't work on amd64 machines. He gave me an account on a PLD amd64 machine
which allowed myself to track down these bugs.
Miles Roper, Peter Kjellerstedt and Willy Tarreau reported that release 2.1 did
not compile with gcc < 3.x.
Marcel J.E. Mol reported lack of kernel memory issues when using Squashfs
on small memory embedded systems. This prompted the addition of the embedded
system kernel configuration options.
Era Scarecrow noticed that Mksquashfs had not been updated to reflect that
smaller than 4K blocks are no longer supported.
Kenichi Shima reported the Kconfig file had not been updated to 2.2.
Aaron Ten Clay made a donation!
Tomas Matejicek (SLAX) made a donation!

View File

@@ -0,0 +1,664 @@
SQUASHFS CHANGE LOG
4.4 29 AUG 2019 Reproducible builds, new compressors,
CVE fixes, security hardening and new options
for Mksquashfs/Unsquashfs.
1. Overall improvements:
1.1 Mksquashfs now generates reproducible images by default.
1.2 Mkfs time and file timestamps can also be specified.
1.3 Support for the Zstandard (ZSTD) compression algorithm.
1.4 CVE-2015-4645 and CVE-2015-4646 have been fixed.
2. Mksquashfs improvements and major bug fixes:
2.1 Pseudo files now support symbolic links.
2.2 New -mkfs-time option.
2.3 New -all-time option.
2.4 New -root-mode option.
2.5 New -quiet option.
2.6 New -noId option.
2.7 New -offset option.
2.8 Update lz4 wrapper to use new functions introduced
in 1.7.0.
2.9 Bug fix, don't allow "/" pseudo filenames.
2.10 Bug fix, allow quoting of pseudo files, to
better handle filenames with spaces.
2.11 Fix compilation with glibc 2.25+.
3. Unsquashfs improvements and major bug fixes:
3.1 CVE-2015-4645 and CVE-2015-4646 have been fixed.
3.2 Unsquashfs has been further hardened against corrupted
filestems.
3.3 Unsquashfs is now more strict about error handling.
3.4 New -ignore-errors option.
3.5 New -strict-errors option.
3.6 New -lln[umeric] option.
3.7 New -lc option.
3.8 New -llc option.
3.9 New -mkfs-time option.
3.10 New -UTC option.
3.11 New -offset option.
3.12 New -quiet option.
3.13 Update lz4 wrapper to use new functions introduced
in 1.7.0.
3.14 Bug fix, fatal and non-fatal errors now set the exit
code to 1.
3.15 Bug fix, fix time setting for symlinks.
3.16 Bug fix, try to set sticky-bit when running as a
user process.
3.17 Fix compilation with glibc 2.25+.
4.3 12 MAY 2014 New compressor options, new Mksquashfs/Unsquashfs
functionality, duplicate checking optimisations,
stability improvements (option/file parsing,
buffer/memory overflow checks, filesystem hardening
on corrupted filesystems), CVE fixes.
Too many changes to do the traditional custom changelog. But, this
is now unnecessary, so instead list most significant 15% of commits
from git changelog in chronological order.
- unsquashfs: add checks for corrupted data in opendir functions
- unsquashfs: completely empty filesystems incorrectly generate an error
- unsquashfs: fix open file limit
- mksquashfs: Use linked list to store directory entries rather
- mksquashfs: Remove qsort and add a bottom up linked list merge sort
- mksquashfs: optimise lookup_inode2() for dirs
- pseudo: fix handling of modify pseudo files
- pseudo: fix handling of directory pseudo files
- xattr: Fix ERROR() so that it is synchronised with the progress bar
- mksquashfs/sort: Fix INFO() so that it is synced with the progress bar
- mksquashfs: Add -progress to force progress bar when using -info
- error.h: consolidate the various error macros into one header file
- mksquashfs: fix stack overflow in write_fragment_table()
- mksquashfs: move list allocation from off the stack
- unsquashfs: fix oversight in directory permission setting
- mksquashfs: dynamically allocate recovery_file
- mksquashfs: dynamically allocate buffer in subpathname()
- mksquashfs: dynamically allocate buffer in pathname()
- unsquashfs: fix CVE-2012-4024
- unsquashfs: fix CVE-2012-4025
- mksquashfs: fix potential stack overflow in get_component()
- mksquashfs: add parse_number() helper for numeric command line options
- mksquasfs: check return value of fstat() in reader_read_file()
- mksquashfs: dynamically allocate filename in old_add_exclude()
- unsquashfs: dynamically allocate pathname in dir_scan()
- unsquashfs: dynamically allocate pathname in pre_scan()
- sort: dynamically allocate filename in add_sort_list()
- mksquashfs: fix dir_scan() exit if lstat of source directory fails
- pseudo: fix memory leak in read_pseudo_def() if exec_file() fails
- pseudo: dynamically allocate path in dump_pseudo()
- mksquashfs: dynamically allocate path in display_path2()
- mksquashfs: dynamically allocate b_buffer in getbase()
- pseudo: fix potential stack overflow in get_component()
- pseudo: avoid buffer overflow in read_pseudo_def() using sscanf()
- pseudo: dynamically allocate filename in exec_file()
- pseudo: avoid buffer overflow in read_sort_file() using fscanf()
- sort: tighten up sort file parsing
- unsquashfs: fix name under-allocation in process_extract_files()
- unsquashfs: avoid buffer overflow in print_filename() using sprintf()
- Fix some limits in the file parsing routines
- pseudo: Rewrite pseudo file processing
- read_fs: fix small memory leaks in read_filesystem()
- mksquashfs: fix fclose leak in reader_read_file() on I/O error
- mksquashfs: fix frag struct leak in write_file_{process|blocks|frag}
- unsquashfs_xattr: fix memory leak in write_xattr()
- read_xattrs: fix xattr free in get_xattr() in error path
- unsquashfs: add -user-xattrs option to only extract user.xxx xattrs
- unsquashfs: add code to only print "not superuser" error message once
- unsquashfs: check for integer overflow in user input
- mksquashfs: check for integer overflow in user input
- mksquashfs: fix "new" variable leak in dir_scan1()
- read_fs: prevent buffer {over|under}flow in read_block() with
corrupted filesystems
- read_fs: check metadata blocks are expected size in scan_inode_table()
- read_fs: check the root inode block is found in scan_inode_table()
- read_fs: Further harden scan_inode_table() against corrupted
filesystems
- unsquashfs: prevent buffer {over|under}flow in read_block() with
corrupted filesystems
- read_xattrs: harden xattr data reading against corrupted filesystems
- unsquash-[23]: harden frag table reading against corrupted filesystems
- unsquash-4.c: harden uid/gid & frag table reading against corruption
- unsquashfs: harden inode/directory table reading against corruption
- mksquashfs: improve out of space in output filesystem handling
- mksquashfs: flag lseek error in writer as probable out of space
- mksquashfs: flag lseek error in write_destination as probable out of
space
- mksquashfs: print file being squashed when ^\ (SIGQUIT) typed
- mksquashfs: make EXIT_MKSQUASHFS() etc restore via new restore thread
- mksquashfs: fix recursive restore failure check
- info: dump queue and cache status if ^\ hit twice within one second
- mksquashfs: fix rare race condition in "locked fragment" queueing
- lz4: add experimental support for lz4 compression
- lz4: add support for lz4 "high compression"
- lzo_wrapper: new implementation with compression options
- gzip_wrapper: add compression options
- mksquashfs: redo -comp <compressor> parsing
- mksquashfs: display compressor options when -X option isn't recognised
- mksquashfs: add -Xhelp option
- mksquashfs/unsquashfs: fix mtime signedness
- Mksquashfs: optimise duplicate checking when appending
- Mksquashfs: introduce additional per CPU fragment process threads
- Mksquashfs: significantly optimise fragment duplicate checking
- read_fs: scan_inode_table(), fix memory leak on filesystem corruption
- pseudo: add_pseudo(), fix use of freed variable
- mksquashfs/unsquashfs: exclude/extract/pseudo files, fix handling of
leaf name
- mksquashfs: rewrite default queue size so it's based on physical mem
- mksquashfs: add a new -mem <mbytes> option
- mksquashfs: fix limit on the number of dynamic pseudo files
- mksquashfs: make -mem take a normal byte value, optionally with a
K, M or G
4.2 28 FEB 2011 XZ compression, and compression options support
1. Filesystem improvements:
1.1 Added XZ compression
1.2 Added compression options support
2. Miscellaneous improvements/bug fixes
1.1 Add missing NO_XATTR filesystem flag to indicate no-xattrs
option was specified and no xattrs should be stored when
appending.
1.2 Add suppport in Unquashfs -stat option for displaying
NO_XATTR flag.
1.3 Remove checkdata entry from Unsquashfs -stat option if a 4.0
filesystem - checkdata is no longer supported.
1.4 Fix appending bug when appending to an empty filesystem - this
would be incorrectly treated as an error.
1.5 Use glibc sys/xattr.h include rather than using attr/xattr.h
which isn't present by default on some distributions.
1.6 Unsquashfs, fix block calculation error with regular files when
file size is between 2^32-block_size+1 and 2^32-1.
1.7 Unsquashfs, fix sparse file writing when holes are larger than
2^31-1.
1.8 Add external CFLAGS and LDFLAGS support to Makefile, and allow
build options to be specified on command line. Also don't
over-write passed in CFLAGS definition.
4.1 19 SEPT 2010 Major filesystem and tools improvements
1. Filesystem improvements:
1.1 Extended attribute support
1.2 New compression framework
1.3 Support for LZO compression
1.4 Support for LZMA compression (not yet in mainline)
2. Mksquashfs improvements:
1.1 Enhanced pseudo file support
1.2 New options for choosing compression algorithm used
1.3 New options for controlling extended attributes
1.4 Fix misalignment issues with memcpy etc. seen on ARM
1.5 Fix floating point error in progress_bar when max == 0
1.6 Removed use of get_nproc() call unavailable in ulibc
1.7 Reorganised help text
3. Unsquashfs improvements:
1.1 New options for controlling extended attributes
1.2 Fix misalignment issues with memcpy etc. seen on ARM
1.3 Fix floating point error in progress_bar when max == 0
1.4 Removed use of get_nproc() call unavailable in ulibc
4.0 5 APR 2009 Major filesystems improvements
1. Kernel code improvements:
1.1 Fixed little endian layout adopted. All swapping macros
removed, and in-line swapping added for big-endian
architectures.
1.2 Kernel code substantially improved and restructured.
1.3 Kernel code split into separate files along functional lines.
1.4 Vmalloc usage removed, and code changed to use separately
allocated 4K buffers
2. Unsquashfs improvements:
2.1 Support for 4.0 filesystems added.
2.2 Swapping macros rewritten.
2.3 Unsquashfs code restructured and split into separate files.
3. Mksquashfs improvements:
3.1 Swapping macros rewritten. Fixed little-endian layout allows
code to be optimised and only added at compile time for
big endian systems.
3.2 Support for pseudo files added.
3.4 26 AUG 2008 Performance improvements to Unsquashfs, Mksquashfs
and the kernel code. Plus many small bug fixes.
1. Kernel code improvements:
1.1 Internal Squashfs kernel metadata and fragment cache
implementations have been merged and optimised. Spinlocks are
now used, locks are held for smaller periods and wakeups have
been minimised. Small race condition fixed where if two or
more processes tried to read the same cache block
simultaneously they would both read and decompress it. 10-20%+
speed improvement has been seen on tests.
1.2 NFS export code rewritten following VFS changes in
linux-2.6.24.
1.3 New patches for linux-2.6.25, linux-2.6.26, and linux-2.6.27.
Fixed patch for linux-2.6.24.
1.4 Fixed small buffer_head leak in squashfs_read_data when
handling badly corrupted filesystems.
1.5 Fixed bug in get_dir_index_using_offset.
2. Unsquashfs improvements:
2.1 Unsquashfs has been parallelised. Filesystem reading, writing
and decompression is now multi-threaded. Up to 40% speed
improvement seen on tests.
2.2 Unsquashfs now has a progress bar. Use -no-progress to
disable it.
2.3 Fixed small bug where unistd.h wasn't being included on
some distributions, leading to lseek being used rather than
lseek64 - which meant on these distributions Unsquashfs
couldn't unsquash filesystems larger than 4GB.
3. Mksquashfs improvements:
3.1 Removed some small remaining parallelisation bottlenecks.
Depending on source filesystem, up to 10%+ speed improvement.
3.2 Progress bar improved, and moved to separate thread.
3.3 Sparse file handling bug in Mksquashfs 3.3 fixed.
3.4 Two rare appending restore bugs fixed (when ^C hit twice).
3.3 1 NOV 2007 Increase in block size, sparse file support,
Mksquashfs and Unsquashfs extended to use
pattern matching in exclude/extract files, plus
many more improvements and bug fixes.
1. Filesystem improvements:
1.1. Maximum block size has been increased to 1Mbyte, and the
default block size has been increased to 128 Kbytes.
This improves compression.
1.2. Sparse files are now supported. Sparse files are files
which have large areas of unallocated data commonly called
holes. These files are now detected by Squashfs and stored
more efficiently. This improves compression and read
performance for sparse files.
2. Mksquashfs improvements:
2.1. Exclude files have been extended to use wildcard pattern
matching and regular expressions. Support has also been
added for non-anchored excludes, which means it is
now possible to specify excludes which match anywhere
in the filesystem (i.e. leaf files), rather than always
having to specify exclude files starting from the root
directory (anchored excludes).
2.2. Recovery files are now created when appending to existing
Squashfs filesystems. This allows the original filesystem
to be recovered if Mksquashfs aborts unexpectedly
(i.e. power failure).
3. Unsquashfs improvements:
3.1. Multiple extract files can now be specified on the
command line, and the files/directories to be extracted can
now also be given in a file.
3.2. Extract files have been extended to use wildcard pattern
matching and regular expressions.
3.3. Filename printing has been enhanced and Unquashfs can
now display filenames with file attributes
('ls -l' style output).
3.4. A -stat option has been added which displays the filesystem
superblock information.
3.5. Unsquashfs now supports 1.x filesystems.
4. Miscellaneous improvements/bug fixes:
4.1. Squashfs kernel code improved to use SetPageError in
squashfs_readpage() if I/O error occurs.
4.2. Fixed Squashfs kernel code bug preventing file
seeking beyond 2GB.
4.3. Mksquashfs now detects file size changes between
first phase directory scan and second phase filesystem create.
It also deals better with file I/O errors.
3.2-r2 15 JAN 2007 Kernel patch update and progress bar bug fix
1. Kernel patches 2.6.19/2.6.20 have been updated to use
const structures and mutexes rather than older semaphores.
2. Minor SMP bug fixes.
3. Progress bar broken on x86-64. Fixed.
3.2 2 JAN 2007 NFS support, improvements to the Squashfs-tools, major
bug fixes, lots of small improvements/bug fixes, and new
kernel patches.
Improvements:
1. Squashfs filesystems can now be exported via NFS.
2. Unsquashfs now supports 2.x filesystems.
3. Mksquashfs now displays a progress bar.
4. Squashfs kernel code has been hardened against accidently or
maliciously corrupted Squashfs filesystems.
Bug fixes:
5. Race condition occurring on S390 in readpage() fixed.
6. Odd behaviour of MIPS memcpy in read_data() routine worked-around.
7. Missing cache_flush in Squashfs symlink_readpage() added.
3.1-r2 30 AUG 2006 Mksquashfs -sort bug fix
A code optimisation after testing unfortunately
broke sorting in Mksquashfs. This has been fixed.
3.1 19 AUG 2006 This release has some major improvements to
the squashfs-tools, a couple of major bug
fixes, lots of small improvements/bug fixes,
and new kernel patches.
1. Mksquashfs has been rewritten to be multi-threaded. It
has the following improvements
1.1. Parallel compression. By default as many compression and
fragment compression threads are created as there are available
processors. This significantly speeds up performance on SMP
systems.
1.2. File input and filesystem output is peformed in parallel on
separate threads to maximise I/O performance. Even on single
processor systems this speeds up performance by at least 10%.
1.3. Appending has been significantly improved, and files within the
filesystem being appended to are no longer scanned and
checksummed. This significantly improves append time for large
filesystems.
1.4. File duplicate checking has been optimised, and split into two
separate phases. Only files which are considered possible
duplicates after the first phase are checksummed and cached in
memory.
1.5 The use of swap memory was found to significantly impact
performance. The amount of memory used to cache files is now a
command line option, by default this is 512 Mbytes.
2. Unsquashfs has the following improvements
2.1 Unsquashfs now allows you to specify the filename or the
directory within the Squashfs filesystem that is to be
extracted, rather than always extracting the entire filesystem.
2.2 A new -force option has been added which forces Unsquashfs to
output to the destination directory even if files and directories
already exist in the destination directory. This allows you to
update an already existing directory tree, or to Unsquashfs to
a partially filled directory tree. Without the -force option
Unsquashfs will refuse to output.
3. The following major bug fixes have been made
3.1 A fragment table rounding bug has been fixed in Mksquashfs.
Previously if the number of fragments in the filesystem
were a multiple of 512, Mksquashfs would generate an
incorrect filesystem.
3.2 A rare SMP bug which occurred when simultaneously acccessing
multiply mounted Squashfs filesystems has been fixed.
4. Miscellaneous improvements/bug fixes
4.1 Kernel code stack usage has been reduced. This is to ensure
Squashfs works with 4K stacks.
4.2 Readdir (Squashfs kernel code) has been fixed to always
return 0, rather than the number of directories read. Squashfs
should now interact better with NFS.
4.3 Lseek bug in Mksquashfs when appending to larger than 4GB
filesystems fixed.
4.4 Squashfs 2.x initrds can now been mounted.
4.5 Unsquashfs exit status fixed.
4.6 New patches for linux-2.6.18 and linux-2.4.33.
3.0 15 MAR 2006 Major filesystem improvements
1. Filesystems are no longer limited to 4 GB. In
theory 2^64 or 4 exabytes is now supported.
2. Files are no longer limited to 4 GB. In theory the maximum
file size is 4 exabytes.
3. Metadata (inode table and directory tables) are no longer
restricted to 16 Mbytes.
4. Hardlinks are now suppported.
5. Nlink counts are now supported.
6. Readdir now returns '.' and '..' entries.
7. Special support for files larger than 256 MB has been added to
the Squashfs kernel code for faster read access.
8. Inode numbers are now stored within the inode rather than being
computed from inode location on disk (this is not so much an
improvement, but a change forced by the previously listed
improvements).
2.2-r2 8 SEPT 2005 Second release of 2.2, this release fixes a couple
of small bugs, a couple of small documentation
mistakes, and adds a patch for kernel 2.6.13.
1. Mksquashfs now deletes the output filesystem image file if an
error occurs whilst generating the filesystem. Previously on
error the image file was left empty or partially written.
2. Updated mksquashfs so that it doesn't allow you to generate
filesystems with block sizes smaller than 4K. Squashfs hasn't
supported block sizes less than 4K since 2.0-alpha.
3. Mksquashfs now ignores missing files/directories in sort files.
This was the original behaviour before 2.2.
4. Fixed small mistake in fs/Kconfig where the version was still
listed as 2.0.
5. Updated ACKNOWLEDGEMENTS file.
2.2 3 JUL 2005 This release has some small improvements, bug fixes
and patches for new kernels.
1. Sort routine re-worked and debugged from release 2.1. It now allows
you to give Mkisofs style sort files and checks for filenames that
don't match anything. Sort priority has also been changed to
conform to Mkisofs usage, highest priority files are now placed
at the start of the filesystem (this means they will be on the
inside of a CD or DVD).
2. New Configure options for embedded systems (memory constrained
systems). See INSTALL file for further details.
3. Directory index bug fixed where chars were treated as signed on
some architectures. A file would not be found in the rare case
that the filename started with a chracter greater than 127.
4. Bug introduced into the read_data() routine when sped up to use data
block queueing fixed. If the second or later block resulted in an
I/O error this was not checked.
5. Append bug introduced in 2.1 fixed. The code to compute the new
compressed and uncompressed directory parts after appending was
wrong.
6. Metadata block length read routine altered to not perform a
misaligned short read. This was to fix reading on an ARM7 running
uCLinux without a misaligned read interrupt handler.
7. Checkdata bug introduced in 2.1 fixed.
2.1-r2 15 DEC 2004 Code changed so it can be compiled with gcc 2.x
1. In some of the code added for release 2.1 I unknowingly used some
gcc extensions only supported by 3.x compilers. I have received
a couple of reports that the 2.1 release doesn't build on 2.x and so
people are clearly still using gcc 2.x. The code has been
rewritten to remove these extensions.
2.1 10 DEC 2004 Significantly improved directory handling plus numerous
other smaller improvements
1. Fast indexed directories implemented. These speed up directory
operations (ls, file lookup etc.) significantly for directories
larger than 8 KB.
2. All directories are now sorted in alphabetical order. This again
speeds up directory operations, and in some cases it also results in
a small compression improvement (greater data similarity between
files with alphabetically similar names).
3. Maximum directory size increased from 512 KB to 128 MB.
4. Duplicate fragment checking and appending optimised in mksquashfs,
depending on filesystem, this is now up to 25% faster.
5. Mksquashfs help information reformatted and reorganised.
6. The Squashfs version and release date is now printed at kernel
boot-time or module insertion. This addition will hopefully help
to reduce the growing problem where the Squashfs version supported
by a kernel is unknown and the kernel source is unavailable.
7. New PERFORMANCE.README file.
8. New -2.0 mksquashfs option.
9. CHANGES file reorganised.
10. README file reorganised, clarified and updated to include the 2.0
mksquashfs options.
11. New patch for Linux 2.6.9.
12. New patch for Linux 2.4.28.
2.0r2 29 AUG 2004 Workaround for kernel bug in kernels 2.6.8 and newer
added
1. New patch for kernel 2.6.8.1. This includes a workaround for a
kernel bug introduced in 2.6.7bk14, which is present in all later
versions of the kernel.
If you're using a 2.6.8 kernel or later then you must use this
2.6.8.1 patch. If you've experienced hangs or oopses using Squashfs
with a 2.6.8 or later kernel then you've hit this bug, and this
patch will fix it.
It is worth mentioning that this kernel bug potentially affects
other filesystems. If you receive odd results with other
filesystems you may be experiencing this bug with that filesystem.
I submitted a patch but this has not yet gone into the
kernel, hopefully the bug will be fixed in later kernels.
2.0 13 JULY 2004 A couple of new options, and some bug fixes
1. New mksquashfs -all-root, -root-owned, -force-uid, and -force-gid
options. These allow the uids/gids of files in the generated
filesystem to be specified, overriding the uids/gids in the
source filesystem.
2. Initrds are now supported for kernels 2.6.x.
3. amd64 bug fixes. If you use an amd64, please read the README-AMD64
file.
4. Check-data and gid bug fixes. With 2.0-alpha when mounting 1.x
filesystems in certain cases file gids were corrupted.
5. New patch for Linux 2.6.7.
2.0-ALPHA 21 MAY 2004 Filesystem changes and compression improvements
1. Squashfs 2.0 has added the concept of fragment blocks.
Files smaller than the file block size and optionally the
remainder of files that do not fit fully into a block (i.e. the
last 32K in a 96K file) are packed into shared fragments and
compressed together. This achieves on average 5 - 20% better
compression than Squashfs 1.x.
2. The maximum block size has been increased to 64K (in the ALPHA
version of Squashfs 2.0).
3. The maximum number of UIDs has been increased to 256 (from 48 in
1.x).
4. The maximum number of GIDs has been increased to 256 (from 15 in
1.x).
5. Removal of sleep_on() function call in 2.6.x patch, to allow Squashfs
to work on the Fedora rc2 kernel.
6. Numerous small bug fixes have been made.
1.3r3 18 JAN 2004 Third release of 1.3, this adds a new mksquashfs option,
some bug fixes, and extra patches for new kernels
1. New mksquashfs -ef exclude option. This option reads the exclude
dirs/files from an exclude file, one exclude dir/file per line. This
avoids the command line size limit when using the -e exclude option,
2. When appending to existing filesystems, if mksquashfs experiences a
fatal error (e.g. out of space when adding to the destination), the
original filesystem is restored,
3. Mksquashfs now builds standalone, without the kernel needing to be
patched.
4. Bug fix in the kernel squashfs filesystem, where the pages being
filled were not kmapped. This seems to only have caused problems
on an Apple G5,
5. New patch for Linux 2.4.24,
6. New patch for Linux 2.6.1, this replaces the patch for 2.6.0-test7.
1.3r2 14 OCT 2003 Second release of 1.3, bug fixes and extra patches for
new kernels
1. Bug fix in routine that adds files to the filesystem being
generated in mksquashfs. This bug was introduced in 1.3
(not enough testing...) when I rewrote it to handle files larger
than available memory. This bug caused a SEGV, so if you've ever
got that, it is now fixed,
2. Long running bug where ls -s and du reported wrong block size
fixed. I'm pretty sure this used to work many kernel versions ago
(2.4.7) but it broke somewhere along the line since then,
3. New patch for Linux 2.4.22,
4. New patch for 2.6.0-test7, this replaces the patch for 2.6.0-test1.
1.3 29 JUL 2003 FIFO/Socket support added plus optimisations and
improvements
1. FIFOs and Socket inodes are now supported,
2. Mksquashfs can now compress files larger than available
memory,
3. File duplicate check routine optimised,
4. Exit codes fixed in Mksquashfs,
5. Patch for Linux 2.4.21,
6. Patch for Linux 2.6.0-test1. Hopefully, this will work for
the next few releases of 2.6.0-testx, otherwise, I'll be
releasing a lot of updates to the 2.6.0 patch...
1.2 13 MAR 2003 Append feature and new mksquashfs options added
Mksquashfs can now add to existing squashfs filesystems. Three extra
options "-noappend", "-keep-as-directory", and "root-becomes"
have been added.
The append option with file duplicate detection, means squashfs can be
used as a simple versioning archiving filesystem. A squashfs
filesystem can be created with for example the linux-2.4.19 source.
Appending the linux-2.4.20 source will create a filesystem with the
two source trees, but only the changed files will take extra room,
the unchanged files will be detected as duplicates.
See the README file for usage changes.
1.1b 16 JAN 2003 Bug fix release
Fixed readpage deadlock bug. This was a rare deadlock bug that
happened when pushing pages into the page cache when using greater
than 4K blocks. I never got this bug when I tested the filesystem,
but two people emailed me on the same day about the problem!
I fixed it by using a page cache function that wasn't there when
I originally did the work, which was nice :-)
1.1 8 JAN 2003 Added features
1. Kernel squashfs can now mount different byte order filesystems.
2. Additional features added to mksquashfs. Mksquashfs now supports
exclude files and multiple source files/directories can be
specified. A nopad option has also been added, which
informs mksquashfs not to pad filesystems to a multiple of 4K.
See README for mksquashfs usage changes.
3. Greater than 2GB filesystems bug fix. Filesystems greater than 2GB
can now be created.
1.0c 14 NOV 2002 Bug fix release
Fixed bugs with initrds and device nodes
1.0 23 OCT 2002 Initial release

View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -0,0 +1,44 @@
INSTALLING SQUASHFS
1. Kernel support
-----------------
This release is for 2.6.29 and newer kernels. Kernel patching is not necessary.
Extended attribute support requires 2.6.35 or newer kernels. But
file systems with extended attributes can be mounted on 2.6.29 and
newer kernels (the extended attributes will be ignored with a warning).
LZO compression support requires 2.6.36 or newer kernels.
XZ compression support requires 2.6.38 or newer kernels.
LZ4 compression support requires 3.11 or newer kernels.
ZSTD compression support requires 4.14 or newer kernels.
2. Building squashfs tools
--------------------------
The squashfs-tools directory contains the source code for the Mksquashfs and
Unsquashfs programs. These can be compiled by typing make (or sudo make
install to install in /usr/local/bin) within that directory.
2.1 Compressors supported
By default the Makefile is configured to build Mksquashfs and Unsquashfs
with GZIP suppport. Read the Makefile in squashfs-tools for instructions on
building LZO, LZ4, XZ and ZSTD compression support.
2.2 Extended attribute support
By default the Makefile is configured to build Mksquashfs and Unsquashfs
with extended attribute support. Read the Makefile in squashfs-tools for
instructions on how to disable extended attribute support, if not supported
by your distribution/C library, or if it is not needed.
2.3 Reproducible builds
By default the Makefile is configured to build Mksquashfs with reproducible
output as default. Read the Makefile in squashfs-tools for instructions
on how to disable reproducible output as default if desired.

View File

@@ -0,0 +1,15 @@
GitHub
This is now the main development repository for Squashfs-Tools.
There are older repositories on Sourceforge and kernel.org. These
are currently out of date, but should be updated in the near future.
The kernel directories are obsolete, all kernel code is now in
mainline at www.kernel.org.
kernel-2.4: If you still need Squashfs support in the 2.4 kernel
then use the squashfs 3.2-r2 release. This is the last release
that has a Mksquashfs which generates filesystems mountable with
Squashfs patched 2.4 kernels. This release has patches for 2.4
kernels (but they have not been updated since the 3.1 release).

View File

@@ -0,0 +1,316 @@
SQUASHFS 4.4 - A squashed read-only filesystem for Linux
Copyright 2002-2019 Phillip Lougher <phillip@squashfs.org.uk>
Released under the GPL licence (version 2 or later).
Welcome to Squashfs 4.4. This is the first release in over 5 years, and
there are substantial improvements: reproducible builds, new compressors,
CVE fixes, security hardening and new options for Mksquashfs/Unsquashfs.
Please see the INSTALL file for instructions on installing the tools,
and the USAGE file for documentation on how to use the tools.
Summary of changes (and sections below)
---------------------------------------
1. Mksquashfs now generates reproducible images by default. Mkfs time and
file timestamps can also be specified.
2. Support for the Zstandard (ZSTD) compression algorithm has been added.
3. Pseudo files now support symbolic links.
4. CVE-2015-4645 and CVE-2015-4646 have been fixed.
5. Unsquashfs has been further hardened against corrupted filestems.
6. Unsquashfs is now more strict about error handling.
7. Miscellaneous new options and major bug fixes for Mksquashfs.
8. Miscellaneous new options and major bug fixes for Unsquashfs.
9. Squashfs-tools 4.4 is compatible with all earlier 4.x filesystems
and releases.
1. Introducing reproducible builds
----------------------------------
Ever since Mksquashfs was parallelised back in 2006, there
has been a certain randomness in how fragments and multi-block
files are ordered in the output filesystem even if the input
remains the same.
This is because the multiple parallel threads can be scheduled
differently between Mksquashfs runs. For example, the thread
given fragment 10 to compress may finish before the thread
given fragment 9 to compress on one run (writing fragment 10
to the output filesystem before fragment 9), but, on the next
run it could be vice-versa. There are many different scheduling
scenarios here, all of which can have a knock on effect causing
different scheduling and ordering later in the filesystem too.
Mkquashfs doesn't care about the ordering of fragments and
multi-block files within the filesystem, as this does not
affect the correctness of the filesystem.
In fact not caring about the ordering, as it doesn't matter, allows
Mksquashfs to run as fast as possible, maximising CPU and I/O
performance.
But, in the last couple of years, Squashfs has become used in
scenarios (cloud etc) where this randomness is causing problems.
Specifically this appears to be where downloaders, installers etc.
try to work out the differences between Squashfs filesystem
updates to minimise the amount of data that needs to transferred
to update an image.
Additionally, in the last couple of years has arisen the notion
of reproducible builds, that is the same source and build
environment etc should be able to (re-)generate identical
output. This is usually for verification and security, allowing
binaries/distributions to be checked for malicious activity.
See https://reproducible-builds.org/ for more information.
Mksquashfs now generates reproducible images by default.
Images generated by Mksquashfs will be ordered identically to
previous runs if the same input has been supplied, and the
same options used.
1.1.1 Dealing with timestamps
Timestamps embedded in the filesystem will stiil cause differences.
Each new run of Mksquashfs will produce a different mkfs (make filesystem)
timestamp in the super-block. Moreover if any file timestamps have changed
(even if the content hasn't), this will produce a difference.
To prevent timestamps from producing differences, the following
new Mksquashfs options have been added.
1.1.2 -mkfs-time <time>
This option takes a positive time value (which is the number
of seconds since the epoch of 1970-01-01 00:00:00 UTC), and sets
the file system timestamp to that.
Squashfs uses an unsigned 32-bit integer to store time, and the
time given should be in that range.
Obviously you can use the date command to convert dates into
this value, i.e.
% mksquashfs source source.sqsh -mkfs-time $(date +%s -d "Jan 1 2019 19:00")
1.1.3 -all-time <time>
This option takes a positive time value (which is the number
of seconds since the epoch of 1970-01-01 00:00:00 UTC), and sets
the timestamp on all files to that (but not the mkfs time).
1.1.4 environment variable SOURCE_DATE_EPOCH
As an alternative to the above command line options, you can
set the environment variable SOURCE_DATE_EPOCH to a time value.
This value will be used to set the mkfs time. Also any
file timestamps which are after SOURCE_DATE_EPOCH will be
clamped to SOURCE_DATE_EPOCH.
See https://reproducible-builds.org/docs/source-date-epoch/
for more information.
Note: both SOURCE_DATE_EPOCH and the command line options cannot
be used at the same time. They are different ways to do the same thing,
and both have FORCE sematics which mean they can't be over-ridden
elsewhere (otherwise it would defeat the purpose).
1.1.5 -not-reproducible
This option tells Mksquashfs that the files do not have to be
strictly ordered. This will make Mksquashfs behave like version 4.3.
2. Zstandard (ZSTD) compression added
-------------------------------------
This is named zstd. It supports the following
compression options.
zstd
-Xcompression-level <compression-level>
<compression-level> should be 1 .. 22 (default 15)
3. Pseudo file symbolic link support added
------------------------------------------
Pseudo definition
Filename s mode uid gid symlink
uid and gid can be either specified as a decimal number, or by name.
Note mode is ignored, as symlinks always have "rwxrwxrwx" permissions.
For example:
symlink s 0 root root example
creates a symlink "symlink" to file "example" with root uid/gid.
4. CVE-2015-2015-4645 and CVE-2015-4646
---------------------------------------
These CVEs were raised due to Unsquashfs having variable overflow and
stack overflow in a number of vulnerable functions.
All instances of variable overflow and stack overflow have been
removed.
5. Unsquashfs hardened against corrupted filestems
--------------------------------------------------
The filesystem super-block values and filesystem metadata tables
are further sanity checked. More importantly, these values are now
checked for validity against other values in the metadata tables, and
these values must match.
6. Unsquashfs is now more strict about error handling
-----------------------------------------------------
Unsquashfs splits errors into two categories: fatal errors and non-fatal
errors. In this release a significant number of errors that were previously
non-fatal have been hardened to fatal.
Fatal errors are those which cause Unsquashfs to abort instantly.
These are generally due to failure to read the filesystem (corruption),
and/or failure to write files to the output filesystem, due to I/O error
or out of space. Generally anything which is unexpected is a fatal error.
Non-fatal errors are generally where support is lacking in the
output filesystem, and it can be considered to be an expected failure.
This includes the inability to write extended attributes (xattrs) to
a filesystem that doesn't support them, the inability to create files on
filesystem that doesn't support them (i.e. symbolic links on VFAT), and the
inability to execute privileged operations as a user-process.
The user may well know the filesystem cannot support certain operations
and would prefer Unsquashfs to ignore then without aborting.
Two new options have been added:
6.1. -ignore-errors
This makes Unsquashfs behave like previous versions, and treats more
errors as non-fatal.
6.2. -strict-errors
This makes Unsquashfs treat every error as fatal, and it will abort
instantly.
7. Miscellaneous new options and major bug fixes for Mksquashfs
---------------------------------------------------------------
7.1. -root-mode <mode>
This sets the permissions of the root directory to the octal <mode>.
This is mostly intended for when multiple sources are specified on
the command line. In this instance Mksquashfs produces a dummy top level
directory with permissions 0777 (rwxrwxrwx). This option allows the
permissions to be changed. But the option can also be used when a single
source is specified.
7.2. -quiet
This suppresses all output from Mksquashfs, except the progress bar.
The progress bar can disabled with -no-progress to produce completely
silent output.
This new option is useful for scripts.
7.3. -noId
This is similar to the pre-existing -noI option, except, it specifies that
only the Id table (uids and gids) should be uncompressed. This option was
added to enable a use-case where uids and gids need to be updated after
filesystem generation.
7.4. -offset <offset>
This option skips <offset> bytes at the beginning of the output filesystem.
Optionally a suffix of K, M or G can be given to specify Kbytes, Mbytes or
Gbytes respectively.
7.5. Update lz4 wrapper to use new functions introduced in 1.7.0
7.5. Bug fix, don't allow "/" pseudo filenames
7.6. Bug fix, allow quoting of pseudo files, to better handle filenames with
spaces
7.7. Fix compilation with glibc 2.25+
8. Miscellaneous new options and major bug fixes for Unsquashfs
---------------------------------------------------------------
8.1. -lln[umeric]
This is similar to the "-lls" option but displays uids and gids numerically.
8.2. -lc option
This is similar to the "-ls" option except it only displays files and empty
directories.
8.3. -llc option
As "-llc" option but displays file attributes.
8.4. -offset <offset>
This option skips <offset> bytes at the beginning of the input filesystem.
Optionally a suffix of K, M or G can be given to specify Kbytes, Mbytes or
Gbytes respectively.
8.5. -quiet
This suppresses all output from Unsquashfs, except the progress bar.
The progress bar can disabled with -no-progress to produce completely
silent output.
This new option is useful for scripts.
8.6. -UTC
This option makes Unsquashfs display all times in the UTC time zone rather
than using the default local time zone.
8.7. Update lz4 wrapper to use new functions introduced in 1.7.0
8.8. Bug fix, fatal and non-fatal errors now set the exit code to 1
8.9. Bug fix, fix time setting for symlinks
8.10. Bug fix, try to set sticky-bit when running as a user process
8.11. Fix compilation with glibc 2.25+
9. Compatiblity
---------------
Mksquashfs 4.4 generates 4.0 filesystems. These filesystems are fully
compatible/interchangable with filesystems generated by Mksquashfs 4.x and are
mountable on 2.6.29 and later kernels.

View File

@@ -0,0 +1,15 @@
Help sponsor Squashfs development!
Maintaining and improving Squashfs is a lot of work, but Squashfs is one of
the only widely used Linux file systems that has no company backing. Squashfs
development is funded soley by the author, partially supported by donations
from companies and individuals that want to improve Squashfs for themselves
and others.
There's lots of exciting new improvements to Squashfs in the pipeline, and
if your company is a serious user of Squashfs, please consider accelerating
development of Squashfs by donating.
Donatations can be made from the Squashfs sourceforge homepage, or if you
prefer by contacting the author privately.

View File

@@ -0,0 +1,177 @@
* Obsolete *
This file is now largely obsolete, considering
it refers to Squashfs 2.1 (which was released in 2006)
and older releases.
GENERAL INFORMATION ON PERFORMANCE TESTS
----------------------------------------
The following performance tests were based on two file sets: the
liveCD filesystem from the Ubuntu liveCD (Warty release), and the
liveCD filesystem from the Damn Small Linux liveCD (release 0.8.4).
The Ubuntu liveCD filesystem was used to test filesystem performance
from CDROM and hard disk for Zisofs, Cloop, Squashfs 2.0 and Squashfs2.1.
CRAMFS filesystem performance could not be tested for this filesystem
bacause it exceeds the maximum supported size of CRAMFS. To test
CRAMFS performance against Squashfs, the liveCD filesystem from
Damn Small Linux was used.
NOTE: the usual warnings apply to these results, they are provided for
illustrative purposes only, and due to different hardware and/or file data, you
may obtain different results. As such the results are provided "as is" without
any warranty (either express or implied) and you assume all risks as to their
quality and accuracy.
1. Ubuntu liveCD performance tests
ext3 uncompressed size 1.4 GB
Zisofs compressed size 589.81 MB
Cloop compressed size 471.89 MB
Squashfs2.0 compressed size 448.58 MB
Squashfs2.1 compressed size 448.58 MB
1.1 Performance tests from CDROM
1.1.1 Directory Lookup performance
Time taken to perform "ls -lR --color=alawys | cat > /dev/null" on filesystem
mounted from CDROM
Zisofs 49.88 seconds (User 2.60 secs, Sys 11.19 secs)
Cloop 20.80 seconds (User 2.71 secs, Sys 13.50 secs)
Squashfs2.0 16.56 seconds (User 2.42 secs, Sys 10.37 secs)
Squashfs2.1 10.14 seconds (User 2.48 secs, Sys 4.44 secs)
1.1.2 Sequential I/O performance
Time taken to perform "tar cf - | cat > /dev/null" on filesystem mounted
from CDROM
Zisofs 27 minutes 28.54 seconds (User 3.00 secs, Sys 1 min 4.80 secs)
Cloop 5 minutes 55.72 seconds (User 2.90 secs, Sys 3 min 37.90 secs)
Squashfs2.0 5 minutes 20.87 seconds (User 2.33 secs, Sys 56.98 secs)
Squashfs2.1 5 minutes 15.46 seconds (user 2.28 secs, Sys 51.12 secs)
1.1.3 Random I/O performance
Random access pattern generated by "find /mnt -type f -printf "%s %p\n" | sort
-g | awk '{ printf $2 }' > /tmp/sort
Time taken to perform "cpio -o --quiet -H newc < /tmp/sort > /dev/null"
on filesystem mounted from CDROM
Zisofs 101 minutes 29.65 seconds (User 5.33 secs, Sys 1 min 17.20 secs)
Cloop 35 minutes 27.51 seconds (user 5.93 secs, Sys 4 mins 30.23 secs)
Squashfs2.0 21 minutes 53.05 seconds (user 5.71 secs, Sys 2 mins 36.59 secs)
Squashfs2.1 21 minutes 46.99 seconds (User 5.80 secs, Sys 2 mins 31.88 secs)
1.2 Performance tests from Hard disk
1.2.1 Directory Lookup performance
Time taken to perform "ls -lR --color=alawys | cat > /dev/null" on filesystem
mounted from Hard disk
Zisofs 17.29 seconds (User 2.62 secs, Sys 11.08 secs)
Cloop 16.46 seconds (User 2.63 secs, Sys 13.41 secs)
Squashfs2.0 13.75 seconds (User 2.44 secs, Sys 11.00 secs)
Squashfs2.1 6.94 seconds (User 2.44 secs, Sys 4.48 secs)
1.2.2 Sequential I/O performance
Time taken to perform "tar cf - | cat > /dev/null" on filesystem mounted
from Hard disk
Zisofs 1 minute 21.47 seconds (User 2.73 secs, Sys 54.44 secs)
Cloop 1 minute 34.06 seconds (user 2.85 secs, Sys 1 min 12.13 secs)
Squashfs2.0 1 minute 21.22 seconds (User 2.42 secs, Sys 56.21 secs)
Squashfs2.1 1 minute 15.46 seconds (User 2.36 secs, Sys 49.78 secs)
1.2.3 Random I/O performance
Random access pattern generated by "find /mnt -type f -printf "%s %p\n" | sort
-g | awk '{ printf $2 }' > /tmp/sort
Time taken to perform "cpio -o --quiet -H newc < /tmp/sort > /dev/null"
on filesystem mounted from Hard disk
Zisofs 11 minutes 13.64 seconds (User 5.08 secs, Sys 52.62 secs)
Cloop 5 minutes 37.93 seconds (user 6 secs, Sys 2 mins 22.38 secs)
Squashfs2.0 5 minutes 7.11 seconds (user 5.63 secs, Sys 2 mins 35.23 secs)
Squashfs2.1 5 minutes 1.87 seconds (User 5.71 secs, Sys 2 mins 29.98 secs)
2. Damn Small Linux liveCD performance tests
ext3 uncompressed size 126 MB
CRAMFS compressed size 52.19 MB
Squashfs2.0 compressed size 46.52 MB
Squashfs2.1 compressed size 46.52 MB
2.1 Performance tests from CDROM
2.1.1 Directory Lookup performance
Time taken to perform "ls -lR --color=alawys | cat > /dev/null" on filesystem
mounted from CDROM
CRAMFS 10.85 seconds (User 0.39 secs, Sys 0.98 secs)
Squashfs2.0 2.97 seconds (User 0.36 secs, Sys 2.15 secs)
Squashfs2.1 2.43 seconds (User 0.40 secs, Sys 1.42 secs)
2.1.2 Sequential I/O performance
Time taken to perform "tar cf - | cat > /dev/null" on filesystem mounted
from CDROM
CRAMFS 55.38 seconds (User 0.34 secs, Sys 6.98 secs)
Squashfs2.0 35.99 seconds (User 0.30 secs, Sys 6.35 secs)
Squashfs2.1 33.83 seconds (User 0.26 secs, Sys 5.56 secs)
2.1.3 Random I/O performance
Random access pattern generated by "find /mnt -type f -printf "%s %p\n" | sort
-g | awk '{ printf $2 }' > /tmp/sort
Time taken to perform "cpio -o --quiet -H newc < /tmp/sort > /dev/null"
on filesystem mounted from CDROM
CRAMFS 3 minutes 1.68 seconds (User 0.54 secs, Sys 9.51 secs)
Squashfs2.0 1 minute 39.45 seconds (User 0.57 secs, Sys 13.14 secs)
Squashfs2.1 1 minute 38.41 seconds (User 0.58 secs, Sys 13.08 secs)
2.2 Performance tests from Hard disk
2.2.1 Directory Lookup performance
Time taken to perform "ls -lR --color=alawys | cat > /dev/null" on filesystem
mounted from Hard disk
CRAMFS 1.77 seconds (User 0.53 secs, Sys 1.21 secs)
Squashfs2.0 2.67 seconds (User 0.41 secs, Sys 2.25 secs)
Squashfs2.1 1.87 seconds (User 0.41 secs, Sys 1.46 secs)
2.2.2 Sequential I/O performance
Time taken to perform "tar cf - | cat > /dev/null" on filesystem mounted
from Hard disk
CRAMFS 6.80 seconds (User 0.36 secs, Sys 6.02 secs)
Squashfs2.0 7.23 seconds (User 0.29 secs, Sys 6.62 secs)
Squashfs2.1 6.53 seconds (User 0.31 secs, Sys 5.82 secs)
2.2.3 Random I/O performance
Random access pattern generated by "find /mnt -type f -printf "%s %p\n" | sort
-g | awk '{ printf $2 }' > /tmp/sort
Time taken to perform "cpio -o --quiet -H newc < /tmp/sort > /dev/null"
on filesystem mounted from Hard disk
CRAMFS 28.55 seconds (User 0.49 secs, Sys 6.49 secs)
Squashfs2.0 25.44 seconds (User 0.58 secs, Sys 13.17 secs)
Squashfs2.1 24.72 seconds (User 0.56 secs, Sys 13.15 secs)

View File

@@ -0,0 +1,161 @@
NOTE: This the original README for version 2.0. It is retained as it
contains information about the fragment design. A description of the new 2.0
mksquashfs options has been added to the main README file, and that
file should now be consulted for these.
SQUASHFS 2.0 - A squashed read-only filesystem for Linux
Copyright 2004 Phillip Lougher (plougher@users.sourceforge.net)
Released under the GPL licence (version 2 or later).
Welcome to the final release of Squashfs version 2.0! A lot of changes to the
filesystem have been made under the bonnet (hood). Squashfs 2.0 uses fragment
blocks and larger blocks (64K) to improve compression ratio by about 5 - 20%
over Squashfs 1.0 depending on the files being compressed. Using fragment
blocks allows Squashfs 2.0 to achieve better compression than cloop and similar
compression to tgz files while retaining the I/O efficiency of a compressed
filesystem.
Detailed changes:
1. Squashfs 2.0 has added the concept of fragment blocks (see later discussion).
Files smaller than the file block size (64K in Squashfs 2.0) and optionally
the remainder of files that do not fit fully into a block (i.e. the last 32K
in a 96K file) are packed into shared fragments and compressed together.
This achieves on average 5 - 20% better compression than Squashfs 1.x.
2. The maximum block size has been increased to 64K.
3. The maximum number of UIDs has been increased to 256 (from 48 in 1.x).
4. The maximum number of GIDs has been increased to 256 (from 15 in 1.x).
5. New mksquashfs -all-root, -root-owned, -force-uid, and -force-gid
options. These allow the uids/gids of files in the generated
filesystem to be specified, overriding the uids/gids in the
source filesystem.
6. Initrds are now supported for kernels 2.6.x.
7. Removal of sleep_on() function call in 2.6.x patch, to allow Squashfs
to work on the Fedora rc2 kernel.
8. AMD64, check-data and gid bug fixes.
9. Numerous small bug fixes have been made.
10. New patch for Linux 2.6.7.
New Squashfs 2.0 options
------------------------
-noF or -noFragmentCompression
Do not compress the fragments. Added for compatibility with noI and
noD, probably not that useful.
-no-fragments
Do not use fragment blocks, and rather generate a filesystem
similar to a Squashfs 1.x filesystem. It will of course still
be a Squashfs 2.0 filesystem but without fragments, and so
it won't be mountable on a Squashfs 1.x system.
-always-use-fragments
By default only small files less than the block size are packed into
fragment blocks. The ends of files which do not fit fully into a block,
are NOT by default packed into fragments. To illustrate this, a
100K file has an initial 64K block and a 36K remainder. This
36K remainder is not packed into a fragment by default. This is
because to do so leads to a 10 - 20% drop in sequential I/O
performance, as a disk head seek is needed to seek to the initial
file data and another disk seek is need to seek to the fragment
block.
Specify this option if you want file remainders to be packed into
fragment blocks. Doing so may increase the compression obtained
BUT at the expense of I/O speed.
-no-duplicates
Do not detect duplicate files.
-all-root
-root-owned
These options (both do exactly the same thing), force all file
uids/gids in the generated Squashfs filesystem to be root.
This allows root owned filesystems to be built without root access
on the host machine.
-force-uid uid
This option forces all files in the generated Squashfs filesystem to
be owned by the specified uid. The uid can be specified either by
name (i.e. "root") or by number.
-force-gid gid
This option forces all files in the generated Squashfs filesystem to
be group owned by the specified gid. The gid can be specified either by
name (i.e. "root") or by number.
Compression improvements example
--------------------------------
The following is the compression results obtained compressing the 2.6.6
linux kernel source using CRAMFS, Cloop (with iso filesystem), Squashfs 1.3 and
Squashfs 2.0 (results generated using big-endian filesystems).
In decreasing order of size:
CRAMFS 62791680 bytes (59.9M)
Squashfs 1.x 51351552 bytes (48.9M)
Cloop 46118681 bytes (44.0M)
Squashfs 2.0 45604854 bytes (43.5M)
The Squashfs 1.x filesystem is 12.6% larger than the new 2.0 filesystem.
The cloop filesystem is 1.1% larger than the Squashfs 2.0 filesystem.
Fragment blocks in Squashfs 2.0
-------------------------------
Squashfs like all other compressed filesystems compresses files individually
on a block by block basis. This is performed to allow mounting and
de-compression of files on a block by block basis without requiring the entire
filesystem to be decompressed. This is in contrast to data-based compression
schemes which compress without understanding the underlying filesystem (i.e.
cloop and tgz files) and which, therefore, do not compress files individually.
Each approach has advantages and disadvantages, data-based systems have better
compression because compression is always performed at the maximum block size
(64K in cloop) irrespective of the size of each file (which could be less than
the block size). Compressed filesystems tend to be faster at I/O because
they understand the filesystem and therefore employ better caching stategies
and read less un-needed data from the filesystem.
Fragment blocks in Squashfs 2.0 solves this problem by packing files (and
optionally the ends of files) which are smaller than the block size into
shared blocks, which are compressed together. For example five files each of
10K will be packed into one shared fragment of 50K and compressed together,
rather than being compressed in five 10K blocks.
This scheme produces a hybrid filesystem, retaining the I/O efficiency
of a compressed filesystem, while obtaining the compression efficiency
of data-based schemes by compressing small files together.
Squashfs 1.x and Squashfs 2.0 compatibility
-------------------------------------------
Appending to Squashfs 1.x filesystems is not supported. If you wish to append
to 1.x filesystems, then either use the original mksquashfs, or convert them
to Squashfs 2.0 by mounting the filesystem and running the 2.0 mksquashfs
on the mounted filesystem.
Mounting Squashfs 1.x filesystems IS supported by the 2.0 kernel patch.

View File

@@ -0,0 +1,18 @@
Information for amd64 users
---------------------------
All previous releases of Squashfs (2.0-alpha and older) generate incorrect
filesystems on amd64 machines. These filesystems work correctly on amd64
machines, but cannot be mounted on non-amd64 machines. Likewise, filesystems
generated on non amd64 machines could not be mounted on amd64 machines.
This bug was caused by the different size of the "time_t" definition used in
SquashFS filesystem structures.
This bug is now fixed in this release. However, all amd64 filesystems
generated by previous releases will not be mountable on amd64 machines
with this release. If you have pre-existing amd64 generated filesystems,
it is important that you recreate the filesystem. This can be performed
by mounting the filesystem using a kernel with the original patch
(i.e. a 2.0-alpha or older patch) and running the SquashFS 2.0
(i.e. this release) mksquashfs tool to create a new SquashFS filesystem.

View File

@@ -0,0 +1,87 @@
SQUASHFS 2.1 - A squashed read-only filesystem for Linux
Copyright 2004 Phillip Lougher (plougher@users.sourceforge.net)
Released under the GPL licence (version 2 or later).
Welcome to Squashfs version 2.1-r2. Squashfs 2.1 introduces indexed
directories which considerably speed up directory lookup (ls, find etc.) for
directories which are greater than 8K in size. All directories are now also
sorted alphabetically which further speeds up directory lookup. Many smaller
improvements have also been made to this release, please see the CHANGES file
entry for detailed changes.
1. DIRECTORY SPEED IMPROVEMENT EXAMPLES
---------------------------------------
To give an indication of the directory speed improvements a number of test
results are shown here. There is in addition a new PERFORMANCE.README file
which gives details of I/O and lookup performance for Squashfs 2.1 against
the Zisofs, Cloop and CRAMFS filesystems.
example 1:
Filesystems generated from a single directory of 72,784 files (2.6 MB
directory size). Each file is 10 bytes in size (the test is directory
lookup and so the file size isn't an issue). The ext3 uncompressed
directory size is 288 MB (presumably because of one file per block).
Zisofs compressed size 153.50 MB
Cloop (isofs) compressed size 1.74 MB
Squashfs2.1 compressed size 612 KB (0.60 MB)
Time taken to perform "ls -lR --color=always | cat > /dev/null" on
filesystems mounted on hard disk.
Zisofs 35 minutes 7.895 seconds (User 7.868 secs, Sys 34 mins 5.621 secs)
Cloop 35 minutes 12.765 seconds (User 7.771 secs, Sys 34 mins 3.869 secs)
Squashfs2.1 19 seconds (User 5.119 secs, Sys 14.547 secs)
example 2:
Filesystems were generated from the Ubuntu Warty livecd (original uncompressed
size on ext3 is 1.4 GB).
Zisofs compressed size 589.81 MB
Cloop (isofs) compressed size 471.19 MB
Squashfs2.0 compressed size 448.58 MB
Squashfs2.1 compressed size 448.58 MB
Time taken to perform "ls -lR --color=always | cat > /dev/null" on
filesystems mounted on hard disk.
Zisofs 49.875 seconds (User time 2.589 secs, Sys 11.194 secs)
Cloop 20.797 seconds (User time 2.706 secs, Sys 13.496 secs)
Squashfs2.0 16.556 seconds (User time 2.424 secs, Sys 10.371 secs)
Squashfs2.1 10.143 seconds (User time 2.475 secs, Sys 4.440 secs)
NOTE: the usual warnings apply to these results, they are provided for
illustrative purposes only, and due to different hardware and/or file data, you
may obtain different results. As such the results are provided "as is" without
any warranty (either express or implied) and you assume all risks as to their
quality and accuracy.
2. NEW MKSQUASHFS OPTIONS
-------------------------
There is only one extra option "-2.0". This tells mksquashfs to generate
a filesystem which is mountable with Squashfs version 2.0.
3. APPENDING AND MOUNTING SQUASHFS 2.0 FILESYSTEMS
--------------------------------------------------
Mounting 2.0 filesystems is supported by Squashfs 2.1. In addition
mksquashfs v2.1 can append to 2.0 filesystems, although the generated
filesystem will still be a 2.0 filesystem.
4. DONATIONS
------------
If you find Squashfs useful then please consider making a donation,
particularly if you use Squashfs in a commercial product. Please consider
giving something back especially if you're making money from it.
Off the Squashfs subject somewhat I'm currently looking for another
job doing Linux kernel or filesystems work. If you know of any such
work that can be performed from the UK then please get in touch. Thanks.

View File

@@ -0,0 +1,60 @@
SQUASHFS 3.0 - A squashed read-only filesystem for Linux
Copyright 2002-2006 Phillip Lougher <phillip@lougher.org.uk>
Released under the GPL licence (version 2 or later).
Welcome to the first release of Squashfs version 3.0. Squashfs 3.0 has the
the following improvements to 2.x.
1. Filesystems are no longer limited to 4 GB. In
theory 2^64 or 4 exabytes is now supported.
2. Files are no longer limited to 4 GB. In theory the maximum
file size is 4 exabytes.
3. Metadata (inode table and directory tables) are no longer
restricted to 16 Mbytes.
4. Hardlinks are now suppported.
5. Nlink counts are now supported.
6. Readdir now returns '.' and '..' entries.
7. Special support for files larger than 256 MB has been added to
the Squashfs kernel code for faster read access.
8. Inode numbers are now stored within the inode rather than being
computed from inode location on disk (this is not so much an
improvement, but a change forced by the previously listed
improvements).
There is a new Unsquashfs utility (in squashfs-tools) than can be used to
decompress a filesystem without mounting it.
Squashfs 3.0 supports 2.x filesystems. Support for 1.x filesystems
will be added in the future.
1. UNSQUASHFS
-------------
Unsquashfs has the following options:
SYNTAX: unsquashfs [-ls | -dest] filesystem
-version print version, licence and copyright information
-info print files as they are unsquashed
-ls list filesystem only
-dest <pathname> unsquash to <pathname>, default "squashfs-root"
The "-ls" option can be used to list the contents of a filesystem without
decompressing the filesystem data itself.
The "-info" option forces Unsquashfs to print each file as it is decompressed.
The "-dest" option specifies the directory that is used to decompress
the filesystem data. If this option is not given then the filesystem is
decompressed to the directory "squashfs-root" in the current working directory.
Unsquashfs can decompress 3.0 filesystems. Support for 2.x and 1.x
filesystems will be added in the future.

View File

@@ -0,0 +1,158 @@
SQUASHFS 3.1 - A squashed read-only filesystem for Linux
Copyright 2002-2006 Phillip Lougher <phillip@lougher.org.uk>
Released under the GPL licence (version 2 or later).
Welcome to Squashfs version 3.1-r2. Squashfs 3.1 has major improvements to
the Squashfs tools (Mksquashfs and Unsquashfs), some major bug fixes, new
kernel patches, and various other smaller improvements and bug fixes.
Please see the CHANGES file for a detailed list.
1. MKSQUASHFS
-------------
Mksquashfs has been rewritten and it is now multi-threaded. It offers
the following improvements:
1. Parallel compression. By default as many compression and fragment
compression threads are created as there are available processors.
This significantly speeds up performance on SMP systems.
2. File input and filesystem output is peformed in parallel on separate
threads to maximise I/O performance. Even on single processor systems
this speeds up performance by at least 10%.
3. Appending has been significantly improved, and files within the
filesystem being appended to are no longer scanned and checksummed. This
significantly improves append time for large filesystems.
4. File duplicate checking has been optimised, and split into two separate
phases. Only files which are considered possible duplicates after the
first phase are checksummed and cached in memory.
5. The use of swap memory was found to significantly impact performance. The
amount of memory used to cache the file is now a command line option, by default
this is 512 Mbytes.
1.1 NEW COMMAND LINE OPTIONS
----------------------------
The new Mksquashfs program has a couple of extra command line options
which can be used to control the new features:
-processors <processors>
This specifies the number of processors used by Mksquashfs.
By default this is the number of available processors.
-read_queue <size in Mbytes>
This specifies the size of the file input queue used by the reader thread.
This defaults to 64 Mbytes.
-write_queue <size in Mbytes>
This specifies the size of the filesystem output queue used by the
writer thread. It also specifies the maximum cache used in file
duplicate detection (the output queue is shared between these tasks).
This defaults to 512 Mbytes.
1.2 PERFORMANCE RESULTS
-----------------------
The following results give an indication of the speed improvements. Two
example filesystems were tested, a liveCD filesystem (about 1.8 Gbytes
uncompressed), and my home directory consisting largely of text files
(about 1.3 Gbytes uncompressed). Tests were run on a single core
and a dual core system.
Dual Core (AMDx2 3800+) system:
Source directories on ext3.
LiveCD, old mksquashfs:
real 11m48.401s
user 9m27.056s
sys 0m15.281s
LiveCD, new par_mksquashfs:
real 4m8.736s
user 7m11.771s
sys 0m27.749s
"Home", old mksquashfs:
real 4m34.360s
user 3m54.007s
sys 0m32.155s
"Home", new par_mksquashfs:
real 1m27.381s
user 2m7.304s
sys 0m17.234s
Single Core PowerBook (PowerPC G4 1.5 GHz Ubuntu Linux)
Source directories on ext3.
LiveCD, old mksquashs:
real 11m38.472s
user 9m6.137s
sys 0m23.799s
LiveCD, par_mksquashfs:
real 10m5.572s
user 8m59.921s
sys 0m16.145s
"Home", old mksquashfs:
real 3m42.298s
user 2m49.478s
sys 0m13.675s
"Home", new par_mksquashfs:
real 3m9.178s
user 2m50.699s
sys 0m9.069s
I'll be interested in any performance results obtained, especially from SMP
machines larger than my dual-core AMD box, as this will give an indication of
the scalability of the code. Obviously, I'm also interested in any problems,
deadlocks, low performance etc.
2. UNSQUASHFS
-------------
Unsquashfs now allows you to specify the filename or directory that is to be
extracted from the Squashfs filesystem, rather than always extracting the
entire filesystem. It also has a new "-force" option, and all options can be
specified in a short form (-i rather than -info).
The Unsquashfs usage info is now:
SYNTAX: ./unsquashfs [options] filesystem [directory or file to extract]
-v[ersion] print version, licence and copyright information
-i[nfo] print files as they are unsquashed
-l[s] list filesystem only
-d[est] <pathname> unsquash to <pathname>, default "squashfs-root"
-f[orce] if file already exists then overwrite
To extract a subset of the filesystem, the filename or directory
tree that is to be extracted can now be specified on the command line. The
file/directory should be specified using the full path to the file/directory
as it appears within the Squashfs filesystem. The file/directory will also be
extracted to that position within the specified destination directory.
The new "-force" option forces Unsquashfs to output to the destination
directory even if files or directories already exist. This allows you
to update an existing directory tree, or to Unsquashfs to a partially
filled directory. Without the "-force" option, Unsquashfs will
refuse to overwrite any existing files, or to create any directories if they
already exist. This is done to protect data in case of mistakes, and
so the "-force" option should be used with caution.

View File

@@ -0,0 +1,33 @@
SQUASHFS 3.2 - A squashed read-only filesystem for Linux
Copyright 2002-2007 Phillip Lougher <phillip@lougher.org.uk>
Released under the GPL licence (version 2 or later).
Welcome to Squashfs version 3.2. Squashfs 3.2 has support for NFS exporting,
some improvements to the Squashfs tools (Mksquashfs and Unsquashfs), some
major bug fixes, new kernel patches, and various other smaller improvements
and bug fixes. Please see the CHANGES file for a detailed list.
1. MKSQUASHFS
-------------
New command line options:
-no-exports
Squashfs now supports NFS exports. By default the additional
information necessary is added to the filesystem by Mksquashfs. If you
do not wish this extra information, then this option can be specified.
This will save a couple of bytes per file, and the filesystem
will be identical to Squashfs 3.1.
-no-progress
Mksquashfs by default now displays a progress bar. This option disables
it.
2. UNSQUASHFS
-------------
Unsquashfs now supports Squashfs 2.x filesystems.

View File

@@ -0,0 +1,169 @@
SQUASHFS 3.3 - A squashed read-only filesystem for Linux
Copyright 2002-2007 Phillip Lougher <phillip@lougher.demon.co.uk>
Released under the GPL licence (version 2 or later).
Welcome to another release of Squashfs. This is the 22nd release in just
over five years of work. Squashfs 3.3 has lots of nice improvements,
both to the filesystem itself (bigger blocks, and sparse files), but
also to the Squashfs-tools Mksquashfs and Unsquashfs. As usual the
CHANGES file has a detailed list of all the improvements.
Following is a description of the changes to the Squashfs tools, usage
guides to the new options, and a summary of the new options.
1. MKSQUASHFS - EXTENDED EXCLUDE FILE HANDLING
----------------------------------------------
1. Extended wildcard pattern matching now supported in exclude files
Enabled by specifying -wildcards option
Supports both anchored and non-anchored exclude files.
1.1 Anchored excludes
Similar to existing exclude files except with wildcards. Exclude
file matches from root of source directories.
Examples:
1. mksquashfs example image.sqsh -wildcards -e 'test/*.gz'
Exclude all files matching "*.gz" in the top level directory "test".
2. mksquashfs example image.sqsh -wildcards -e '*/[Tt]est/example*'
Exclude all files beginning with "example" inside directories called
"Test" or "test", that occur inside any top level directory.
Using extended wildcards, negative matching is also possible.
3. mksquashfs example image.sqsh -wildcards -e 'test/!(*data*).gz'
Exclude all files matching "*.gz" in top level directory "test",
except those with "data" in the name.
1.2 Non-anchored excludes
By default excludes match from the top level directory, but it is
often useful to exclude a file matching anywhere in the source directories.
For this non-anchored excludes can be used, specified by pre-fixing the
exclude with "...".
Examples:
1. mksquashfs example image.sqsh -wildcards -e '... *.gz'
Exclude files matching "*.gz" anywhere in the source directories.
For example this will match "example.gz", "test/example.gz", and
"test/test/example.gz".
2. mksquashfs example image.sqsh -wildcards -e '... [Tt]est/*.gz'
Exclude files matching "*.gz" inside directories called "Test" or
"test" that occur anywhere in the source directories.
Again, using extended wildcards, negative matching is also possible.
3. mksquashfs example image.sqsh -wildcards -e '... !(*data*).gz'
Exclude all files matching "*.gz" anywhere in the source directories,
except those with "data" in the name.
2. Regular expression pattern matching now supported in exclude files
Enabled by specifying -regex option. Identical behaviour to wild
card pattern matching, except patterns are considered to be regular
expressions.
Supports both anchored and non-anchored exclude files.
2. MKSQUASHFS - NEW RECOVERY FILE FEATURE
-----------------------------------------
Recovery files are now created when appending to existing Squashfs
filesystems. This allows the original filesystem to be recovered
if Mksquashfs aborts unexpectedly (i.e. power failure).
The recovery files are called squashfs_recovery_xxx_yyy, where
"xxx" is the name of the filesystem being appended to, and "yyy" is a
number to guarantee filename uniqueness (the PID of the parent Mksquashfs
process).
Normally if Mksquashfs exits correctly the recovery file is deleted to
avoid cluttering the filesystem. If Mksquashfs aborts, the "-recover"
option can be used to recover the filesystem, giving the previously
created recovery file as a parameter, i.e.
mksquashfs dummy image.sqsh -recover squashfs_recovery_image.sqsh_1234
The writing of the recovery file can be disabled by specifying the
"-no-recovery" option.
3. UNSQUASHFS - EXTENDED EXTRACT FILE HANDLING
----------------------------------------------
1. Multiple extract files can now be specified on the command line, and the
files/directories to be extracted can now also be given in a file.
To specify a file containing the extract files use the "-e[f]" option.
2. Extended wildcard pattern matching now supported in extract files
Enabled by default. Similar to existing extract files except with
wildcards.
Examples:
1. unsquashfs image.sqsh 'test/*.gz'
Extract all files matching "*.gz" in the top level directory "test".
2. unsquashfs image.sqsh '[Tt]est/example*'
Extract all files beginning with "example" inside top level directories
called "Test" or "test".
Using extended wildcards, negative matching is also possible.
3. unsquashfs image.sqsh 'test/!(*data*).gz'
Extract all files matching "*.gz" in top level directory "test",
except those with "data" in the name.
3. Regular expression pattern matching now supported in extract files
Enabled by specifying -r[egex] option. Identical behaviour to wild
card pattern matching, except patterns are considered to be regular
expressions.
4. UNSQUASHFS - EXTENDED FILENAME PRINTING
------------------------------------------
Filename printing has been enhanced and Unquashfs can now display filenames
with file attributes ('ls -l' style output).
New options:
-ll[s]
list filesystem with file attributes, but don't unsquash
-li[nfo]
print files as they are unsquashed with file attributes
5. UNSQUASHFS - MISCELLANEOUS OPTIONS
-------------------------------------
-s[tat]
Display the filesystem superblock information. This is useful to
discover the filesystem version, byte ordering, whether it has an
NFS export table, and what options were used to compress
the filesystem.

View File

@@ -0,0 +1,48 @@
SQUASHFS 4.0 - A squashed read-only filesystem for Linux
Copyright 2002-2009 Phillip Lougher <phillip@lougher.demon.co.uk>
Released under the GPL licence (version 2 or later).
Welcome to Squashfs 4.0. This is an initial tools only release to
support users of the 2.6.29 kernel, following the mainlining of Squashfs
earlier this year.
Later releases will probably contain kernel patches supporting 4.0
layouts for earlier kernels.
New Mksquashfs options
----------------------
Mksquashfs now supports pseudo files, these allow fake directories, character
and block devices to be specified and added to the Squashfs filesystem being
built, rather than requiring them to be present in the source directories.
This, for example, allows device nodes to be added to the filesystem without
requiring root access.
Two options are supported, -p allows one pseudo file to be specified on the
command line, and -pf allows a pseudo file to be specified containing a
list of pseduo definitions, one per line.
Pseudo device nodes are specified using 7 arguments
Filename type mode uid gid major minor
Where type is either
b - for block devices, and
c - for character devices
mode is the octal mode specifier, similar to that expected by chmod.
Uid and gid can be either specified as a decimal number, or by name.
For example:
/dev/chr_dev c 666 root root 100 1
/dev/blk_dev b 444 0 0 200 200
Directories are specified using 5 arguments
Filename type mode uid gid
Where type is d.

View File

@@ -0,0 +1,265 @@
SQUASHFS 4.1 - A squashed read-only filesystem for Linux
Copyright 2002-2010 Phillip Lougher <phillip@lougher.demon.co.uk>
Released under the GPL licence (version 2 or later).
Welcome to Squashfs 4.1. This is a tools only release, support for Squashfs
file systems is in mainline (2.6.29 and later).
New features in Squashfs-tools 4.1
----------------------------------
1. Support for extended attributes
2. Support for LZMA and LZO compression
3. New pseudo file features
Compatiblity
------------
Mksquashfs 4.1 generates 4.0 filesystems. These filesystems are fully
compatible/interchangable with filesystems generated by Mksquashfs 4.0 and are
mountable on 2.6.29 and later kernels.
Extended attributes (xattrs)
----------------------------
Squashfs file systems now have extended attribute support. The
extended attribute implementation has the following features:
1. Layout can store up to 2^48 bytes of compressed xattr data.
2. Number of xattrs per inode unlimited.
3. Total size of xattr data per inode 2^48 bytes of compressed data.
4. Up to 4 Gbytes of data per xattr value.
5. Inline and out-of-line xattr values supported for higher performance
in xattr scanning (listxattr & getxattr), and to allow xattr value
de-duplication.
6. Both whole inode xattr duplicate detection and individual xattr value
duplicate detection supported. These can obviously nest, file C's
xattrs can be a complete duplicate of file B, and file B's xattrs
can be a partial duplicate of file A.
7. Xattr name prefix types stored, allowing the redundant "user.", "trusted."
etc. characters to be eliminated and more concisely stored.
8. Support for files, directories, symbolic links, device nodes, fifos
and sockets.
Extended attribute support is in 2.6.35 and later kernels. File systems
with extended attributes can be mounted on 2.6.29 and later kernels, the
extended attributes will be ignored with a warning.
LZMA and LZO compression
------------------------
Squashfs now supports LZMA and LZO compression.
LZO support is in 2.6.36 and newer kernels. LZMA is not yet in mainline.
New Mksquashfs options
----------------------
-comp <comp>
Select <comp> compression.
The compression algorithms supported by the build of Mksquashfs can be
found by typing mksquashfs without any arguments. The compressors available
are displayed at the end of the help message, e.g.
Compressors available:
gzip (default)
lzma
lzo
The default compression used when -comp isn't specified on the command line
is indicated by "(default)".
-no-xattrs
Don't store extended attributes
-xattrs
Store extended attributes
The default behaviour of Mksquashfs with respect to extended attribute
storage is build time selectable. The Mksquashfs help message indicates
whether extended attributes are stored or not, e.g.
-no-xattrs don't store extended attributes
-xattrs store extended attributes (default)
shows that extended attributes are stored by default, and can be disabled
by the -no-xattrs option.
-no-xattrs don't store extended attributes (default)
-xattrs store extended attributes
shows that extended attributes are not stored by default, storage can be
enabled by the -xattrs option.
-noX
-noXattrCompression
Don't compress extended attributes
New Unsquashfs options
----------------------
-n[o-xattrs]
Don't extract xattrs in filesystem
-x[attrs]
Extract xattrs in filesystem
The default behaviour of Unsquashfs with respect to extended attributes
is build time selectable. The Unsquashfs help message indicates whether
extended attributes are stored or not, e.g.
-no[-xattrs] don't extract xattrs in file system
-x[attrs] extract xattrs in file system (default)
shows that xattrs are extracted by default.
-no[-xattrs] don't extract xattrs in file system (default)
-x[attrs] extract xattrs in file system
shows that xattrs are not extracted by default.
New pseudo file support
-----------------------
Mksquashfs supports pseudo files, these allow fake files, directories, character
and block devices to be specified and added to the Squashfs filesystem being
built, rather than requiring them to be present in the source directories.
This, for example, allows device nodes to be added to the filesystem without
requiring root access.
Mksquashfs 4.1 adds support for "dynamic pseudo files" and a modify operation.
Dynamic pseudo files allow files to be dynamically created when Mksquashfs
is run, their contents being the result of running a command or piece of
shell script. The modifiy operation allows the mode/uid/gid of an existing
file in the source filesystem to be modified.
Two Mksquashfs options are supported, -p allows one pseudo file to be specified
on the command line, and -pf allows a pseudo file to be specified containing a
list of pseduo definitions, one per line.
Pseudo operations
-----------------
1. Creating a dynamic file
--------------------------
Pseudo definition
Filename f mode uid gid command
mode is the octal mode specifier, similar to that expected by chmod.
uid and gid can be either specified as a decimal number, or by name.
command can be an executable or a piece of shell script, and it is executed
by running "/bin/sh -c command". The stdout becomes the contents of
"Filename".
Examples:
Running a basic command
-----------------------
/somedir/dmesg f 444 root root dmesg
creates a file "/somedir/dmesg" containing the output from dmesg.
Executing shell script
----------------------
RELEASE f 444 root root \
if [ ! -e /tmp/ver ]; then \
echo 0 > /tmp/ver; \
fi; \
ver=`cat /tmp/ver`; \
ver=$((ver +1)); \
echo $ver > /tmp/ver; \
echo -n `cat /tmp/release`; \
echo "-dev #"$ver `date` "Build host" `hostname`
Creates a file RELEASE containing the release name, date, build host, and
an incrementing version number. The incrementing version is a side-effect
of executing the shell script, and ensures every time Mksquashfs is run a
new version number is used without requiring any other shell scripting.
The above example also shows that commands can be split across multiple lines
using "\". Obviously as the script will be presented to the shell as a single
line, a semicolon is need to separate individual shell commands within the
shell script.
Reading from a device (or fifo/named socket)
--------------------------------------------
input f 444 root root dd if=/dev/sda1 bs=1024 count=10
Copies 10K from the device /dev/sda1 into the file input. Ordinarily Mksquashfs
given a device, fifo, or named socket will place that special file within the
Squashfs filesystem, the above allows input from these special files to be
captured and placed in the Squashfs filesystem.
2. Creating a block or character device
---------------------------------------
Pseudo definition
Filename type mode uid gid major minor
Where type is either
b - for block devices, and
c - for character devices
mode is the octal mode specifier, similar to that expected by chmod.
uid and gid can be either specified as a decimal number, or by name.
For example:
/dev/chr_dev c 666 root root 100 1
/dev/blk_dev b 666 0 0 200 200
creates a character device "/dev/chr_dev" with major:minor 100:1 and
a block device "/dev/blk_dev" with major:minor 200:200, both with root
uid/gid and a mode of rw-rw-rw.
3. Creating a directory
-----------------------
Pseudo definition
Filename d mode uid gid
mode is the octal mode specifier, similar to that expected by chmod.
uid and gid can be either specified as a decimal number, or by name.
For example:
/pseudo_dir d 666 root root
creates a directory "/pseudo_dir" with root uid/gid and mode of rw-rw-rw.
4. Modifying attributes of an existing file
-------------------------------------------
Pseudo definition
Filename m mode uid gid
mode is the octal mode specifier, similar to that expected by chmod.
uid and gid can be either specified as a decimal number, or by name.
For example:
dmesg m 666 root root
Changes the attributes of the file "dmesg" in the filesystem to have
root uid/gid and a mode of rw-rw-rw, overriding the attributes obtained
from the source filesystem.

View File

@@ -0,0 +1,57 @@
SQUASHFS 4.2 - A squashed read-only filesystem for Linux
Copyright 2002-2011 Phillip Lougher <phillip@lougher.demon.co.uk>
Released under the GPL licence (version 2 or later).
Welcome to Squashfs 4.2. This is a tools only release, support for Squashfs
filesystems is in mainline (2.6.29 and later).
New features in Squashfs-tools 4.2
----------------------------------
1. Support for XZ compression
2. Support for compressor specific options
Compatiblity
------------
Mksquashfs 4.2 generates 4.0 filesystems. These filesystems are fully
compatible/interchangable with filesystems generated by Mksquashfs 4.0 and are
mountable on 2.6.29 and later kernels.
XZ compression
--------------
Squashfs now supports XZ compression.
XZ support is in 2.6.38 and newer kernels.
New Mksquashfs options
----------------------
-X<compressor-option>
Compression algorithms can now support compression specific options. These
options are prefixed by -X, and are passed to the compressor for handling.
The compression specific options supported by each compressor can be
found by typing mksquashfs without any arguments. They are displayed at the
end of the help message, e.g.
Compressors available and compressor specific options:
gzip (no options) (default)
lzo (no options)
xz
-Xbcj filter1,filter2,...,filterN
Compress using filter1,filter2,...,filterN in turn
(in addition to no filter), and choose the best compression.
Available filters: x86, arm, armthumb, powerpc, sparc, ia64
-Xdict-size <dict-size>
Use <dict-size> as the XZ dictionary size. The dictionary size
can be specified as a percentage of the block size, or as an
absolute value. The dictionary size must be less than or equal
to the block size and 8192 bytes or larger. It must also be
storable in the xz header as either 2^n or as 2^n+2^(n+1).
Example dict-sizes are 75%, 50%, 37.5%, 25%, or 32K, 16K, 8K
etc.

View File

@@ -0,0 +1,182 @@
SQUASHFS 4.3 - A squashed read-only filesystem for Linux
Copyright 2002-2014 Phillip Lougher <phillip@lougher.demon.co.uk>
Released under the GPL licence (version 2 or later).
Welcome to Squashfs 4.3. This is the first release in over 3 years, and
there are substantial improvements to stability, new compression options
and compressors, speed optimisations, and new options for Mksquashfs/Unsquashfs.
This is a tools only release, support for Squashfs filesystems is
in mainline (2.6.29 and later).
Changes in Squashfs-tools 4.3
-----------------------------
1. Stability improvements. Better checking of user input for out of
range/invalid values. Better handling of corrupted Squashfs filesystems
(Mksquashfs append mode, and Unsquashfs). Better handling of buffer
overflow/underflow.
2. GZIP compressor now supports compression options, allowing different
compression levels to be used.
3. Rewritten LZO compressor with compression options, allowing different
LZO algorithms and different compression levels to be used.
4. New LZ4 compressor (note not yet in mainline kernel)
5. Better default memory usage for Mksquashfs. Mksquashfs by default now
uses 25% of physical memory.
6. Duplicate checking in Mksquashfs further optimised. With certain
"problem filesystems" greater than 2x performance improvement.
Filesystems with a lot of duplicates should see at least 10-20% speed
improvement.
7. The -stat option in Unsquashfs now displays the compression options
used to generate the original filesystem. Previously -stat only displayed
the compression algorithm used.
8. The file being compressed/uncompressed in Mksquashfs/Unsquashfs is now
displayed if CTRL-\ (SIGQUIT from keyboard) typed.
9. The status of the internal queues/caches in Mksquashfs/Unsquashfs is
now displayed if CTRL-\ (SIGQUIT from keyboard) is typed twice within
one second. Normally only useful for "power users", but it can be
used to discover if there's any bottlenecks affecting performance
(the bottleneck will normally be the compressors/fragment compressors).
10. Miscellaneous new options for Mksquashfs/Unsquashfs to fine tune behaviour.
11. Fixes for CVE-2012-4024 and CVE-2012-4025.
Compatiblity
------------
Mksquashfs 4.3 generates 4.0 filesystems. These filesystems are fully
compatible/interchangable with filesystems generated by Mksquashfs 4.0 and are
mountable on 2.6.29 and later kernels.
Compressors
-----------
New compression options and compressors are now supported.
The new options and compressors are:
1. gzip
-Xcompression-level <compression-level>
<compression-level> should be 1 .. 9 (default 9)
-Xwindow-size <window-size>
<window-size> should be 8 .. 15 (default 15)
-Xstrategy strategy1,strategy2,...,strategyN
Compress using strategy1,strategy2,...,strategyN in turn
and choose the best compression.
Available strategies: default, filtered, huffman_only,
run_length_encoded and fixed
2. lzo
-Xalgorithm <algorithm>
Where <algorithm> is one of:
lzo1x_1
lzo1x_1_11
lzo1x_1_12
lzo1x_1_15
lzo1x_999 (default)
-Xcompression-level <compression-level>
<compression-level> should be 1 .. 9 (default 8)
Only applies to lzo1x_999 algorithm
3. lz4
-Xhc
Compress using LZ4 High Compression
The compression specific options are, obviously, specific to the compressor
in question, and you should read the compressor documentation and check
their web sites to understand their behaviour.
In general the defaults used by Mksquashfs for each compressor are optimised
to give the best performance for each compressor, where what constitutes
best depends on the compressor. For gzip/xz best means highest compression
(trying multiple filters/strategies can improve compression, but this is
extremely expensive computationally, and hence, not suitable for the defaults),
for LZO/LZ4 best means a tradeoff between compression and (de)-compression
overhead (LZO/LZ4 by definition are intended for weaker processors).
New Mksquashfs options
----------------------
1. -mem <size>
Set the amount of memory used by Mksquashfs to <size> bytes. G/M and K
post-fixes are supported.
By default Mksquashfs uses 25% of the physical memory. Increasing
this with the -mem option can increase performance (note it does not have
any effect on compression). Reducing it can prevent thrashing if the
system is busy and there is not 25% of physical memory free (again, note
it does not have any effect on compression).
2. -exit-on-error
By default Mksquashfs treats certain errors as benign, if these
errors occur Mksquashfs prints the error on the console but continues.
These errors are typically failure to read a file from the source filesystem.
This is deliberate, in many cases users prefer Mksquashfs to flag
the error but continue rather than abort what may be hours of compression.
But there are times where failure to read any file is considered critical,
and users (especially in the case of automated scripts where the
errors output to the console may be missed) prefer Mksquashfs to exit.
The new -exit-on-error option can be used in this scenario. This option
makes Mksquashfs treat all benign errors as fatal.
3. -progress
By default if -info is specified, the progress bar is disabled as it gets
in the way. Occasionally you might want the progress bar enabled whilst
-info is enabled. This option forces Mksquashfs to output the progress
bar when -info is specified.
4. -Xhelp
Display the usage text for the currently selected compressor.
New Unsquashfs options
----------------------
1. -u[ser-xattrs]
Only write user xattrs. This forces Unsquashfs to ignore system xattrs.
This is useful when Unsquashing a filesystem as a non-root user, and the
filesystem contains system xattrs which are only writable by root.
Major bugs fixed
----------------
1. If Mksquashfs ran out of space in the destination filesystem, this
would not cause Mksquashfs to immediately abort, and Mksquashfs would
continue to process the source filesystem. Mksquashfs now immediately
aborts on out of space in the destination filesystem.
2. Unsquashfs ignored the maximum number of open files limit, and if that
was lower than the default limit for Linux, it would run out of file
descriptors. Unsquashfs now limits the number of open files to the
limit currently in force (e.g. specified by setrlimit).
3. If huge numbers of dynamic pseudo files were specified, Mksquashfs
could exceed the maximum number of open files limit. This was because
Mksquashfs created all the dynamic file processes up front before
commencing source filesystem reading and compression. Mksquashfs
now creates the dynamic file processes on demand whilst reading
and compressing the source filesystem, thus limiting the number of
dynamic pseudo file processes in existence at any one time.
4. When outputting Unsquashfs used to set the permissions of directories
as it recursively descended. This in hindsight had an obvious oversight,
if a directory had only read permission (or was otherwise restricted), then
Unsquashfs would fail to write its contents when descending into it. Fixed
by setting directory permissions as Unsquashfs recursively unwinds.

View File

@@ -0,0 +1,74 @@
# Pseudo file example
# Mksquashfs supports pseudo files, these allow fake files, directories,
# character and block devices to be specified and added to the Squashfs
# filesystem being built, rather than requiring them to be present in the
# source directories.
#
# This, for example, allows device nodes to be added to the filesystem without
# requiring root access.
# Mksquashfs 4.1 adds support for "dynamic pseudo files" and a modify operation.
# Dynamic pseudo files allow files to be dynamically created when Mksquashfs
# is run, their contents being the result of running a command or piece of
# shell script. The modifiy operation allows the mode/uid/gid of an existing
# file in the source filesystem to be modified.
# Two Mksquashfs options are supported, -p allows one pseudo file to be
# specified #on the command line, and -pf allows a pseudo file to be specified
# containing a list of pseduo definitions, one per line.
# Pseudo file examples
# Run mkquashfs . /tmp/img -pf pseudo-file.examples
# to see their effect
# Creating dynamic file examples
# Create a file "dmesg" containing the output from dmesg.
dmesg f 444 root root dmesg
# Create a file RELEASE containing the release name, date, build host, and
# an incrementing version number. The incrementing version is a side-effect
# of executing the shell script, and ensures every time Mksquashfs is run a
# new version number is used without requiring any other shell scripting.
RELEASE f 444 root root \
if [ ! -e /tmp/ver ]; then \
echo 0 > /tmp/ver; \
fi; \
ver=`cat /tmp/ver`; \
ver=$((ver +1)); \
echo $ver > /tmp/ver; \
echo -n "release x.x"; \
echo "-dev #"$ver `date` "Build host" `hostname`
# Copy 10K from the device /dev/sda1 into the file input. Ordinarily
# Mksquashfs given a device, fifo, or named socket will place that special file
# within the Squashfs filesystem, this allows input from these special
# files to be captured and placed in the Squashfs filesystem.
input f 444 root root dd if=/dev/sda1 bs=1024 count=10
# Creating a block or character device examples
# Create a character device "chr_dev" with major:minor 100:1 and
# a block device "blk_dev" with major:minor 200:200, both with root
# uid/gid and a mode of rw-rw-rw.
chr_dev c 666 root root 100 1
blk_dev b 666 0 0 200 200
# Creating a directory example
# create a directory "pseudo_dir" with root uid/gid and mode of r--r--r--.
pseudo_dir d 444 root root
# Modifying attributes of an existing file exmaple
# Change the attributes of the file "INSTALL" in the filesystem to have
# root uid/gid and a mode of rw-rw-rw, overriding the attributes obtained
# from the source filesystem.
INSTALL m 666 root root

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
Squashfs is now in mainline at www.kernel.org.
These files are obsolete and not updated.

View File

@@ -0,0 +1,11 @@
#
# Makefile for the linux squashfs routines.
#
O_TARGET := squashfs.o
obj-y := inode.o squashfs2_0.o
obj-m := $(O_TARGET)
include $(TOPDIR)/Rules.make

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* squashfs.h
*/
#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
#endif
#ifdef SQUASHFS_TRACE
#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args)
#else
#define TRACE(s, args...) {}
#endif
#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args)
#define SERROR(s, args...) do { \
if (!silent) \
printk(KERN_ERR "SQUASHFS error: "s, ## args);\
} while(0)
#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args)
#define SQUASHFS_I(INO) (&INO->u.squashfs_i)
#define i_size_read(INO) (INO->i_size)
#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY)
#define SQSH_EXTERN
extern unsigned int squashfs_read_data(struct super_block *s, char *buffer,
long long index, unsigned int length,
long long *next_index);
extern int squashfs_get_cached_block(struct super_block *s, char *buffer,
long long block, unsigned int offset,
int length, long long *next_block,
unsigned int *next_offset);
extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct
squashfs_fragment_cache *fragment);
extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block
*s, long long start_block,
int length);
extern struct address_space_operations squashfs_symlink_aops;
extern struct address_space_operations squashfs_aops;
extern struct address_space_operations squashfs_aops_4K;
extern struct file_operations squashfs_dir_ops;
extern struct inode_operations squashfs_dir_inode_ops;
#else
#define SQSH_EXTERN static
#endif
#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk);
#else
static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk)
{
return 0;
}
#endif
#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk);
#else
static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
{
return 0;
}
#endif

View File

@@ -0,0 +1,751 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* squashfs2_0.c
*/
#include <linux/types.h>
#include <linux/squashfs_fs.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include <linux/fs.h>
#include <linux/smp_lock.h>
#include <linux/locks.h>
#include <linux/init.h>
#include <linux/dcache.h>
#include <linux/wait.h>
#include <linux/zlib.h>
#include <linux/blkdev.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#include "squashfs.h"
static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry);
static struct file_operations squashfs_dir_ops_2 = {
.read = generic_read_dir,
.readdir = squashfs_readdir_2
};
static struct inode_operations squashfs_dir_inode_ops_2 = {
.lookup = squashfs_lookup_2
};
static unsigned char squashfs_filetype_table[] = {
DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
};
static int read_fragment_index_table_2(struct super_block *s)
{
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
(sblk->fragments), GFP_KERNEL))) {
ERROR("Failed to allocate uid/gid table\n");
return 0;
}
if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
!squashfs_read_data(s, (char *)
msblk->fragment_index_2,
sblk->fragment_table_start,
SQUASHFS_FRAGMENT_INDEX_BYTES_2
(sblk->fragments) |
SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
ERROR("unable to read fragment index table\n");
return 0;
}
if (msblk->swap) {
int i;
unsigned int fragment;
for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
i++) {
SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
&msblk->fragment_index_2[i], 1);
msblk->fragment_index_2[i] = fragment;
}
}
return 1;
}
static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
long long *fragment_start_block,
unsigned int *fragment_size)
{
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
long long start_block =
msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
struct squashfs_fragment_entry_2 fragment_entry;
if (msblk->swap) {
struct squashfs_fragment_entry_2 sfragment_entry;
if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
start_block, offset,
sizeof(sfragment_entry), &start_block,
&offset))
goto out;
SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
} else
if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
start_block, offset,
sizeof(fragment_entry), &start_block,
&offset))
goto out;
*fragment_start_block = fragment_entry.start_block;
*fragment_size = fragment_entry.size;
return 1;
out:
return 0;
}
static struct inode *squashfs_new_inode(struct super_block *s,
struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
{
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
struct inode *i = new_inode(s);
if (i) {
i->i_ino = ino;
i->i_mtime = sblk->mkfs_time;
i->i_atime = sblk->mkfs_time;
i->i_ctime = sblk->mkfs_time;
i->i_uid = msblk->uid[inodeb->uid];
i->i_mode = inodeb->mode;
i->i_nlink = 1;
i->i_size = 0;
if (inodeb->guid == SQUASHFS_GUIDS)
i->i_gid = i->i_uid;
else
i->i_gid = msblk->guid[inodeb->guid];
}
return i;
}
static struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode)
{
struct inode *i;
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
unsigned int block = SQUASHFS_INODE_BLK(inode) +
sblk->inode_table_start;
unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
unsigned int ino = SQUASHFS_MK_VFS_INODE(block
- sblk->inode_table_start, offset);
long long next_block;
unsigned int next_offset;
union squashfs_inode_header_2 id, sid;
struct squashfs_base_inode_header_2 *inodeb = &id.base,
*sinodeb = &sid.base;
TRACE("Entered squashfs_iget\n");
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
offset, sizeof(*sinodeb), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
sizeof(*sinodeb));
} else
if (!squashfs_get_cached_block(s, (char *) inodeb, block,
offset, sizeof(*inodeb), &next_block,
&next_offset))
goto failed_read;
switch(inodeb->inode_type) {
case SQUASHFS_FILE_TYPE: {
struct squashfs_reg_inode_header_2 *inodep = &id.reg;
struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
long long frag_blk;
unsigned int frag_size;
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *)
sinodep, block, offset,
sizeof(*sinodep), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
} else
if (!squashfs_get_cached_block(s, (char *)
inodep, block, offset,
sizeof(*inodep), &next_block,
&next_offset))
goto failed_read;
frag_blk = SQUASHFS_INVALID_BLK;
if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
!get_fragment_location_2(s,
inodep->fragment, &frag_blk, &frag_size))
goto failed_read;
if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
goto failed_read1;
i->i_size = inodep->file_size;
i->i_fop = &generic_ro_fops;
i->i_mode |= S_IFREG;
i->i_mtime = inodep->mtime;
i->i_atime = inodep->mtime;
i->i_ctime = inodep->mtime;
i->i_blocks = ((i->i_size - 1) >> 9) + 1;
i->i_blksize = PAGE_CACHE_SIZE;
SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
SQUASHFS_I(i)->start_block = inodep->start_block;
SQUASHFS_I(i)->u.s1.block_list_start = next_block;
SQUASHFS_I(i)->offset = next_offset;
if (sblk->block_size > 4096)
i->i_data.a_ops = &squashfs_aops;
else
i->i_data.a_ops = &squashfs_aops_4K;
TRACE("File inode %x:%x, start_block %x, "
"block_list_start %llx, offset %x\n",
SQUASHFS_INODE_BLK(inode), offset,
inodep->start_block, next_block,
next_offset);
break;
}
case SQUASHFS_DIR_TYPE: {
struct squashfs_dir_inode_header_2 *inodep = &id.dir;
struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *)
sinodep, block, offset,
sizeof(*sinodep), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
} else
if (!squashfs_get_cached_block(s, (char *)
inodep, block, offset,
sizeof(*inodep), &next_block,
&next_offset))
goto failed_read;
if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
goto failed_read1;
i->i_size = inodep->file_size;
i->i_op = &squashfs_dir_inode_ops_2;
i->i_fop = &squashfs_dir_ops_2;
i->i_mode |= S_IFDIR;
i->i_mtime = inodep->mtime;
i->i_atime = inodep->mtime;
i->i_ctime = inodep->mtime;
SQUASHFS_I(i)->start_block = inodep->start_block;
SQUASHFS_I(i)->offset = inodep->offset;
SQUASHFS_I(i)->u.s2.directory_index_count = 0;
SQUASHFS_I(i)->u.s2.parent_inode = 0;
TRACE("Directory inode %x:%x, start_block %x, offset "
"%x\n", SQUASHFS_INODE_BLK(inode),
offset, inodep->start_block,
inodep->offset);
break;
}
case SQUASHFS_LDIR_TYPE: {
struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *)
sinodep, block, offset,
sizeof(*sinodep), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
sinodep);
} else
if (!squashfs_get_cached_block(s, (char *)
inodep, block, offset,
sizeof(*inodep), &next_block,
&next_offset))
goto failed_read;
if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
goto failed_read1;
i->i_size = inodep->file_size;
i->i_op = &squashfs_dir_inode_ops_2;
i->i_fop = &squashfs_dir_ops_2;
i->i_mode |= S_IFDIR;
i->i_mtime = inodep->mtime;
i->i_atime = inodep->mtime;
i->i_ctime = inodep->mtime;
SQUASHFS_I(i)->start_block = inodep->start_block;
SQUASHFS_I(i)->offset = inodep->offset;
SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
SQUASHFS_I(i)->u.s2.directory_index_offset =
next_offset;
SQUASHFS_I(i)->u.s2.directory_index_count =
inodep->i_count;
SQUASHFS_I(i)->u.s2.parent_inode = 0;
TRACE("Long directory inode %x:%x, start_block %x, "
"offset %x\n",
SQUASHFS_INODE_BLK(inode), offset,
inodep->start_block, inodep->offset);
break;
}
case SQUASHFS_SYMLINK_TYPE: {
struct squashfs_symlink_inode_header_2 *inodep =
&id.symlink;
struct squashfs_symlink_inode_header_2 *sinodep =
&sid.symlink;
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *)
sinodep, block, offset,
sizeof(*sinodep), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
sinodep);
} else
if (!squashfs_get_cached_block(s, (char *)
inodep, block, offset,
sizeof(*inodep), &next_block,
&next_offset))
goto failed_read;
if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
goto failed_read1;
i->i_size = inodep->symlink_size;
i->i_op = &page_symlink_inode_operations;
i->i_data.a_ops = &squashfs_symlink_aops;
i->i_mode |= S_IFLNK;
SQUASHFS_I(i)->start_block = next_block;
SQUASHFS_I(i)->offset = next_offset;
TRACE("Symbolic link inode %x:%x, start_block %llx, "
"offset %x\n",
SQUASHFS_INODE_BLK(inode), offset,
next_block, next_offset);
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE: {
struct squashfs_dev_inode_header_2 *inodep = &id.dev;
struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *)
sinodep, block, offset,
sizeof(*sinodep), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
} else
if (!squashfs_get_cached_block(s, (char *)
inodep, block, offset,
sizeof(*inodep), &next_block,
&next_offset))
goto failed_read;
if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
goto failed_read1;
i->i_mode |= (inodeb->inode_type ==
SQUASHFS_CHRDEV_TYPE) ? S_IFCHR :
S_IFBLK;
init_special_inode(i, i->i_mode, inodep->rdev);
TRACE("Device inode %x:%x, rdev %x\n",
SQUASHFS_INODE_BLK(inode), offset,
inodep->rdev);
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE: {
if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
goto failed_read1;
i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
? S_IFIFO : S_IFSOCK;
init_special_inode(i, i->i_mode, 0);
break;
}
default:
ERROR("Unknown inode type %d in squashfs_iget!\n",
inodeb->inode_type);
goto failed_read1;
}
insert_inode_hash(i);
return i;
failed_read:
ERROR("Unable to read inode [%x:%x]\n", block, offset);
failed_read1:
return NULL;
}
static int get_dir_index_using_offset(struct super_block *s, long long
*next_block, unsigned int *next_offset,
long long index_start,
unsigned int index_offset, int i_count,
long long f_pos)
{
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
int i, length = 0;
struct squashfs_dir_index_2 index;
TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
i_count, (unsigned int) f_pos);
if (f_pos == 0)
goto finish;
for (i = 0; i < i_count; i++) {
if (msblk->swap) {
struct squashfs_dir_index_2 sindex;
squashfs_get_cached_block(s, (char *) &sindex,
index_start, index_offset,
sizeof(sindex), &index_start,
&index_offset);
SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex);
} else
squashfs_get_cached_block(s, (char *) &index,
index_start, index_offset,
sizeof(index), &index_start,
&index_offset);
if (index.index > f_pos)
break;
squashfs_get_cached_block(s, NULL, index_start, index_offset,
index.size + 1, &index_start,
&index_offset);
length = index.index;
*next_block = index.start_block + sblk->directory_table_start;
}
*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
finish:
return length;
}
static int get_dir_index_using_name(struct super_block *s, long long
*next_block, unsigned int *next_offset,
long long index_start,
unsigned int index_offset, int i_count,
const char *name, int size)
{
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
int i, length = 0;
char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1];
struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer;
char str[SQUASHFS_NAME_LEN + 1];
TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
strncpy(str, name, size);
str[size] = '\0';
for (i = 0; i < i_count; i++) {
if (msblk->swap) {
struct squashfs_dir_index_2 sindex;
squashfs_get_cached_block(s, (char *) &sindex,
index_start, index_offset,
sizeof(sindex), &index_start,
&index_offset);
SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
} else
squashfs_get_cached_block(s, (char *) index,
index_start, index_offset,
sizeof(struct squashfs_dir_index_2),
&index_start, &index_offset);
squashfs_get_cached_block(s, index->name, index_start,
index_offset, index->size + 1,
&index_start, &index_offset);
index->name[index->size + 1] = '\0';
if (strcmp(index->name, str) > 0)
break;
length = index->index;
*next_block = index->start_block + sblk->directory_table_start;
}
*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
return length;
}
static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir)
{
struct inode *i = file->f_dentry->d_inode;
struct squashfs_sb_info *msblk = &i->i_sb->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
long long next_block = SQUASHFS_I(i)->start_block +
sblk->directory_table_start;
int next_offset = SQUASHFS_I(i)->offset, length = 0,
dir_count;
struct squashfs_dir_header_2 dirh;
char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1];
struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
SQUASHFS_I(i)->u.s2.directory_index_start,
SQUASHFS_I(i)->u.s2.directory_index_offset,
SQUASHFS_I(i)->u.s2.directory_index_count,
file->f_pos);
while (length < i_size_read(i)) {
/* read directory header */
if (msblk->swap) {
struct squashfs_dir_header_2 sdirh;
if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
next_block, next_offset, sizeof(sdirh),
&next_block, &next_offset))
goto failed_read;
length += sizeof(sdirh);
SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
} else {
if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
next_block, next_offset, sizeof(dirh),
&next_block, &next_offset))
goto failed_read;
length += sizeof(dirh);
}
dir_count = dirh.count + 1;
while (dir_count--) {
if (msblk->swap) {
struct squashfs_dir_entry_2 sdire;
if (!squashfs_get_cached_block(i->i_sb, (char *)
&sdire, next_block, next_offset,
sizeof(sdire), &next_block,
&next_offset))
goto failed_read;
length += sizeof(sdire);
SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
} else {
if (!squashfs_get_cached_block(i->i_sb, (char *)
dire, next_block, next_offset,
sizeof(*dire), &next_block,
&next_offset))
goto failed_read;
length += sizeof(*dire);
}
if (!squashfs_get_cached_block(i->i_sb, dire->name,
next_block, next_offset,
dire->size + 1, &next_block,
&next_offset))
goto failed_read;
length += dire->size + 1;
if (file->f_pos >= length)
continue;
dire->name[dire->size + 1] = '\0';
TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
(unsigned int) dirent, dire->name,
dire->size + 1, (int) file->f_pos,
dirh.start_block, dire->offset,
squashfs_filetype_table[dire->type]);
if (filldir(dirent, dire->name, dire->size + 1,
file->f_pos, SQUASHFS_MK_VFS_INODE(
dirh.start_block, dire->offset),
squashfs_filetype_table[dire->type])
< 0) {
TRACE("Filldir returned less than 0\n");
goto finish;
}
file->f_pos = length;
}
}
finish:
return 0;
failed_read:
ERROR("Unable to read directory block [%llx:%x]\n", next_block,
next_offset);
return 0;
}
static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry)
{
const unsigned char *name = dentry->d_name.name;
int len = dentry->d_name.len;
struct inode *inode = NULL;
struct squashfs_sb_info *msblk = &i->i_sb->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
long long next_block = SQUASHFS_I(i)->start_block +
sblk->directory_table_start;
int next_offset = SQUASHFS_I(i)->offset, length = 0,
dir_count;
struct squashfs_dir_header_2 dirh;
char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN];
struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
if (len > SQUASHFS_NAME_LEN)
goto exit_loop;
length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
SQUASHFS_I(i)->u.s2.directory_index_start,
SQUASHFS_I(i)->u.s2.directory_index_offset,
SQUASHFS_I(i)->u.s2.directory_index_count, name,
len);
while (length < i_size_read(i)) {
/* read directory header */
if (msblk->swap) {
struct squashfs_dir_header_2 sdirh;
if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
next_block, next_offset, sizeof(sdirh),
&next_block, &next_offset))
goto failed_read;
length += sizeof(sdirh);
SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
} else {
if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
next_block, next_offset, sizeof(dirh),
&next_block, &next_offset))
goto failed_read;
length += sizeof(dirh);
}
dir_count = dirh.count + 1;
while (dir_count--) {
if (msblk->swap) {
struct squashfs_dir_entry_2 sdire;
if (!squashfs_get_cached_block(i->i_sb, (char *)
&sdire, next_block,next_offset,
sizeof(sdire), &next_block,
&next_offset))
goto failed_read;
length += sizeof(sdire);
SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
} else {
if (!squashfs_get_cached_block(i->i_sb, (char *)
dire, next_block,next_offset,
sizeof(*dire), &next_block,
&next_offset))
goto failed_read;
length += sizeof(*dire);
}
if (!squashfs_get_cached_block(i->i_sb, dire->name,
next_block, next_offset, dire->size + 1,
&next_block, &next_offset))
goto failed_read;
length += dire->size + 1;
if (sorted && name[0] < dire->name[0])
goto exit_loop;
if ((len == dire->size + 1) && !strncmp(name,
dire->name, len)) {
squashfs_inode_t ino =
SQUASHFS_MKINODE(dirh.start_block,
dire->offset);
TRACE("calling squashfs_iget for directory "
"entry %s, inode %x:%x, %d\n", name,
dirh.start_block, dire->offset, ino);
inode = (msblk->iget)(i->i_sb, ino);
goto exit_loop;
}
}
}
exit_loop:
d_add(dentry, inode);
return ERR_PTR(0);
failed_read:
ERROR("Unable to read directory block [%llx:%x]\n", next_block,
next_offset);
goto exit_loop;
}
int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
{
struct squashfs_super_block *sblk = &msblk->sblk;
msblk->iget = squashfs_iget_2;
msblk->read_fragment_index_table = read_fragment_index_table_2;
sblk->bytes_used = sblk->bytes_used_2;
sblk->uid_start = sblk->uid_start_2;
sblk->guid_start = sblk->guid_start_2;
sblk->inode_table_start = sblk->inode_table_start_2;
sblk->directory_table_start = sblk->directory_table_start_2;
sblk->fragment_table_start = sblk->fragment_table_start_2;
return 1;
}

View File

@@ -0,0 +1,915 @@
#ifndef SQUASHFS_FS
#define SQUASHFS_FS
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* squashfs_fs.h
*/
#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY
#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
#endif
#ifdef CONFIG_SQUASHFS_VMALLOC
#define SQUASHFS_ALLOC(a) vmalloc(a)
#define SQUASHFS_FREE(a) vfree(a)
#else
#define SQUASHFS_ALLOC(a) kmalloc(a, GFP_KERNEL)
#define SQUASHFS_FREE(a) kfree(a)
#endif
#ifdef CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#else
#define SQUASHFS_CACHED_FRAGMENTS 3
#endif
#define SQUASHFS_MAJOR 3
#define SQUASHFS_MINOR 0
#define SQUASHFS_MAGIC 0x73717368
#define SQUASHFS_MAGIC_SWAP 0x68737173
#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 65536
#define SQUASHFS_FILE_LOG 16
#define SQUASHFS_FILE_MAX_SIZE 65536
/* Max number of uids and gids */
#define SQUASHFS_UIDS 256
#define SQUASHFS_GUIDS 255
/* Max length of filename (not 255) */
#define SQUASHFS_NAME_LEN 256
#define SQUASHFS_INVALID ((long long) 0xffffffffffff)
#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff)
#define SQUASHFS_INVALID_BLK ((long long) -1)
#define SQUASHFS_USED_BLK ((long long) -2)
/* Filesystem flags */
#define SQUASHFS_NOI 0
#define SQUASHFS_NOD 1
#define SQUASHFS_CHECK 2
#define SQUASHFS_NOF 3
#define SQUASHFS_NO_FRAG 4
#define SQUASHFS_ALWAYS_FRAG 5
#define SQUASHFS_DUPLICATE 6
#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOI)
#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOD)
#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOF)
#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_FRAG)
#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_ALWAYS_FRAG)
#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_DUPLICATE)
#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_CHECK)
#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
duplicate_checking) (noi | (nod << 1) | (check_data << 2) \
| (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
(duplicate_checking << 6))
/* Max number of types and file types */
#define SQUASHFS_DIR_TYPE 1
#define SQUASHFS_FILE_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
/* 1.0 filesystem type definitions */
#define SQUASHFS_TYPES 5
#define SQUASHFS_IPC_TYPE 0
/* Flag whether block is compressed or uncompressed, bit is set if block is
* uncompressed */
#define SQUASHFS_COMPRESSED_BIT (1 << 15)
#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
(B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK)
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
/*
* Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block
*/
#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16))
#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff))
#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\
<< 16) + (B)))
/* Compute 32 bit VFS inode number from squashfs inode number */
#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \
((b) >> 2) + 1))
/* XXX */
/* Translate between VFS mode and squashfs mode */
#define SQUASHFS_MODE(a) ((a) & 0xfff)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES(A) (A * sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
sizeof(long long))
/* cached data constants for filesystem */
#define SQUASHFS_CACHED_BLKS 8
#define SQUASHFS_MAX_FILE_SIZE_LOG 64
#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \
(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
#define SQUASHFS_MARKER_BYTE 0xff
/* meta index cache */
#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
#define SQUASHFS_META_ENTRIES 31
#define SQUASHFS_META_NUMBER 8
#define SQUASHFS_SLOTS 4
struct meta_entry {
long long data_block;
unsigned int index_block;
unsigned short offset;
unsigned short pad;
};
struct meta_index {
unsigned int inode_number;
unsigned int offset;
unsigned short entries;
unsigned short skip;
unsigned short locked;
unsigned short pad;
struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
};
/*
* definitions for structures on disk
*/
typedef long long squashfs_block_t;
typedef long long squashfs_inode_t;
struct squashfs_super_block {
unsigned int s_magic;
unsigned int inodes;
unsigned int bytes_used_2;
unsigned int uid_start_2;
unsigned int guid_start_2;
unsigned int inode_table_start_2;
unsigned int directory_table_start_2;
unsigned int s_major:16;
unsigned int s_minor:16;
unsigned int block_size_1:16;
unsigned int block_log:16;
unsigned int flags:8;
unsigned int no_uids:8;
unsigned int no_guids:8;
unsigned int mkfs_time /* time of filesystem creation */;
squashfs_inode_t root_inode;
unsigned int block_size;
unsigned int fragments;
unsigned int fragment_table_start_2;
long long bytes_used;
long long uid_start;
long long guid_start;
long long inode_table_start;
long long directory_table_start;
long long fragment_table_start;
long long unused;
} __attribute__ ((packed));
struct squashfs_dir_index {
unsigned int index;
unsigned int start_block;
unsigned char size;
unsigned char name[0];
} __attribute__ ((packed));
#define SQUASHFS_BASE_INODE_HEADER \
unsigned int inode_type:4; \
unsigned int mode:12; \
unsigned int uid:8; \
unsigned int guid:8; \
unsigned int mtime; \
unsigned int inode_number;
struct squashfs_base_inode_header {
SQUASHFS_BASE_INODE_HEADER;
} __attribute__ ((packed));
struct squashfs_ipc_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
} __attribute__ ((packed));
struct squashfs_dev_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header {
SQUASHFS_BASE_INODE_HEADER;
squashfs_block_t start_block;
unsigned int fragment;
unsigned int offset;
unsigned int file_size;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_lreg_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
squashfs_block_t start_block;
unsigned int fragment;
unsigned int offset;
long long file_size;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
unsigned int file_size:19;
unsigned int offset:13;
unsigned int start_block;
unsigned int parent_inode;
} __attribute__ ((packed));
struct squashfs_ldir_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
unsigned int file_size:27;
unsigned int offset:13;
unsigned int start_block;
unsigned int i_count:16;
unsigned int parent_inode;
struct squashfs_dir_index index[0];
} __attribute__ ((packed));
union squashfs_inode_header {
struct squashfs_base_inode_header base;
struct squashfs_dev_inode_header dev;
struct squashfs_symlink_inode_header symlink;
struct squashfs_reg_inode_header reg;
struct squashfs_lreg_inode_header lreg;
struct squashfs_dir_inode_header dir;
struct squashfs_ldir_inode_header ldir;
struct squashfs_ipc_inode_header ipc;
};
struct squashfs_dir_entry {
unsigned int offset:13;
unsigned int type:3;
unsigned int size:8;
int inode_number:16;
char name[0];
} __attribute__ ((packed));
struct squashfs_dir_header {
unsigned int count:8;
unsigned int start_block;
unsigned int inode_number;
} __attribute__ ((packed));
struct squashfs_fragment_entry {
long long start_block;
unsigned int size;
unsigned int unused;
} __attribute__ ((packed));
extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
extern int squashfs_uncompress_init(void);
extern int squashfs_uncompress_exit(void);
/*
* macros to convert each packed bitfield structure from little endian to big
* endian and vice versa. These are needed when creating or using a filesystem
* on a machine with different byte ordering to the target architecture.
*
*/
#define SQUASHFS_SWAP_START \
int bits;\
int b_pos;\
unsigned long long val;\
unsigned char *s;\
unsigned char *d;
#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
SQUASHFS_SWAP((s)->flags, d, 288, 8);\
SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
SQUASHFS_SWAP((s)->unused, d, 888, 64);\
}
#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 8);\
SQUASHFS_SWAP((s)->guid, d, 24, 8);\
SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_ipc_inode_header))\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
}
#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_dev_inode_header)); \
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_symlink_inode_header));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_reg_inode_header));\
SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
SQUASHFS_SWAP((s)->offset, d, 192, 32);\
SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
}
#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_lreg_inode_header));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
SQUASHFS_SWAP((s)->offset, d, 224, 32);\
SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_dir_inode_header));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
SQUASHFS_SWAP((s)->offset, d, 147, 13);\
SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
}
#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_ldir_inode_header));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
SQUASHFS_SWAP((s)->offset, d, 155, 13);\
SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
}
#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
SQUASHFS_SWAP((s)->index, d, 0, 32);\
SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
SQUASHFS_SWAP((s)->size, d, 64, 8);\
}
#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
SQUASHFS_SWAP((s)->count, d, 0, 8);\
SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
}
#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
SQUASHFS_SWAP((s)->offset, d, 0, 13);\
SQUASHFS_SWAP((s)->type, d, 13, 3);\
SQUASHFS_SWAP((s)->size, d, 16, 8);\
SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
}
#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
SQUASHFS_SWAP((s)->size, d, 64, 32);\
}
#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 2);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
16)\
SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
}
#define SQUASHFS_SWAP_INTS(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 4);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
32)\
SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
}
#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 8);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
64)\
SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
}
#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * bits / 8);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
bits)\
SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
}
#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
struct squashfs_base_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_ipc_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned int type:4;
unsigned int offset:4;
} __attribute__ ((packed));
struct squashfs_dev_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned int mtime;
unsigned int start_block;
unsigned int file_size:32;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned int file_size:19;
unsigned int offset:13;
unsigned int mtime;
unsigned int start_block:24;
} __attribute__ ((packed));
#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 4);\
SQUASHFS_SWAP((s)->guid, d, 20, 4);
#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_ipc_inode_header_1));\
SQUASHFS_SWAP((s)->type, d, 24, 4);\
SQUASHFS_SWAP((s)->offset, d, 28, 4);\
}
#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_dev_inode_header_1));\
SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_symlink_inode_header_1));\
SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_reg_inode_header_1));\
SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_dir_inode_header_1));\
SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
SQUASHFS_SWAP((s)->offset, d, 43, 13);\
SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
}
#endif
#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
struct squashfs_dir_index_2 {
unsigned int index:27;
unsigned int start_block:29;
unsigned char size;
unsigned char name[0];
} __attribute__ ((packed));
struct squashfs_base_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_ipc_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_dev_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned int mtime;
unsigned int start_block;
unsigned int fragment;
unsigned int offset;
unsigned int file_size:32;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned int file_size:19;
unsigned int offset:13;
unsigned int mtime;
unsigned int start_block:24;
} __attribute__ ((packed));
struct squashfs_ldir_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned int file_size:27;
unsigned int offset:13;
unsigned int mtime;
unsigned int start_block:24;
unsigned int i_count:16;
struct squashfs_dir_index_2 index[0];
} __attribute__ ((packed));
union squashfs_inode_header_2 {
struct squashfs_base_inode_header_2 base;
struct squashfs_dev_inode_header_2 dev;
struct squashfs_symlink_inode_header_2 symlink;
struct squashfs_reg_inode_header_2 reg;
struct squashfs_dir_inode_header_2 dir;
struct squashfs_ldir_inode_header_2 ldir;
struct squashfs_ipc_inode_header_2 ipc;
};
struct squashfs_dir_header_2 {
unsigned int count:8;
unsigned int start_block:24;
} __attribute__ ((packed));
struct squashfs_dir_entry_2 {
unsigned int offset:13;
unsigned int type:3;
unsigned int size:8;
char name[0];
} __attribute__ ((packed));
struct squashfs_fragment_entry_2 {
unsigned int start_block;
unsigned int size;
} __attribute__ ((packed));
#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 8);\
SQUASHFS_SWAP((s)->guid, d, 24, 8);\
#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_dev_inode_header_2)); \
SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_symlink_inode_header_2));\
SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_reg_inode_header_2));\
SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
SQUASHFS_SWAP((s)->offset, d, 128, 32);\
SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_dir_inode_header_2));\
SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
SQUASHFS_SWAP((s)->offset, d, 51, 13);\
SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
}
#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_ldir_inode_header_2));\
SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
SQUASHFS_SWAP((s)->offset, d, 59, 13);\
SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
}
#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
SQUASHFS_SWAP((s)->index, d, 0, 27);\
SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
SQUASHFS_SWAP((s)->size, d, 56, 8);\
}
#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
SQUASHFS_SWAP((s)->count, d, 0, 8);\
SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
}
#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
SQUASHFS_SWAP((s)->offset, d, 0, 13);\
SQUASHFS_SWAP((s)->type, d, 13, 3);\
SQUASHFS_SWAP((s)->size, d, 16, 8);\
}
#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
SQUASHFS_SWAP((s)->size, d, 32, 32);\
}
#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2))
#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
sizeof(int))
#endif
#ifdef __KERNEL__
/*
* macros used to swap each structure entry, taking into account
* bitfields and different bitfield placing conventions on differing
* architectures
*/
#include <asm/byteorder.h>
#ifdef __BIG_ENDIAN
/* convert from little endian to big endian */
#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
tbits, b_pos)
#else
/* convert from big endian to little endian */
#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
tbits, 64 - tbits - b_pos)
#endif
#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
b_pos = pos % 8;\
val = 0;\
s = (unsigned char *)p + (pos / 8);\
d = ((unsigned char *) &val) + 7;\
for(bits = 0; bits < (tbits + b_pos); bits += 8) \
*d-- = *s++;\
value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
}
#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n);
#endif
#endif

View File

@@ -0,0 +1,44 @@
#ifndef SQUASHFS_FS_I
#define SQUASHFS_FS_I
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* squashfs_fs_i.h
*/
struct squashfs_inode_info {
long long start_block;
unsigned int offset;
union {
struct {
long long fragment_start_block;
unsigned int fragment_size;
unsigned int fragment_offset;
long long block_list_start;
} s1;
struct {
long long directory_index_start;
unsigned int directory_index_offset;
unsigned int directory_index_count;
unsigned int parent_inode;
} s2;
} u;
};
#endif

View File

@@ -0,0 +1,76 @@
#ifndef SQUASHFS_FS_SB
#define SQUASHFS_FS_SB
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* squashfs_fs_sb.h
*/
#include <linux/squashfs_fs.h>
#include <linux/zlib.h>
struct squashfs_cache {
long long block;
int length;
long long next_index;
char *data;
};
struct squashfs_fragment_cache {
long long block;
int length;
unsigned int locked;
char *data;
};
struct squashfs_sb_info {
struct squashfs_super_block sblk;
int devblksize;
int devblksize_log2;
int swap;
struct squashfs_cache *block_cache;
struct squashfs_fragment_cache *fragment;
int next_cache;
int next_fragment;
int next_meta_index;
unsigned int *uid;
unsigned int *guid;
long long *fragment_index;
unsigned int *fragment_index_2;
unsigned int read_size;
char *read_data;
char *read_page;
struct semaphore read_data_mutex;
struct semaphore read_page_mutex;
struct semaphore block_cache_mutex;
struct semaphore fragment_mutex;
struct semaphore meta_index_mutex;
wait_queue_head_t waitq;
wait_queue_head_t fragment_wait_queue;
struct meta_index *meta_index;
z_stream stream;
struct inode *(*iget)(struct super_block *s, squashfs_inode_t
inode);
long long (*read_blocklist)(struct inode *inode, int
index, int readahead_blks, char *block_list,
unsigned short **block_p, unsigned int *bsize);
int (*read_fragment_index_table)(struct super_block *s);
};
#endif

View File

@@ -0,0 +1,225 @@
SQUASHFS 4.0 FILESYSTEM
=======================
Squashfs is a compressed read-only filesystem for Linux.
It uses zlib compression to compress files, inodes and directories.
Inodes in the system are very small and all blocks are packed to minimise
data overhead. Block sizes greater than 4K are supported up to a maximum
of 1Mbytes (default block size 128K).
Squashfs is intended for general read-only filesystem use, for archival
use (i.e. in cases where a .tar.gz file may be used), and in constrained
block device/memory systems (e.g. embedded systems) where low overhead is
needed.
Mailing list: squashfs-devel@lists.sourceforge.net
Web site: www.squashfs.org
1. FILESYSTEM FEATURES
----------------------
Squashfs filesystem features versus Cramfs:
Squashfs Cramfs
Max filesystem size: 2^64 16 MiB
Max file size: ~ 2 TiB 16 MiB
Max files: unlimited unlimited
Max directories: unlimited unlimited
Max entries per directory: unlimited unlimited
Max block size: 1 MiB 4 KiB
Metadata compression: yes no
Directory indexes: yes no
Sparse file support: yes no
Tail-end packing (fragments): yes no
Exportable (NFS etc.): yes no
Hard link support: yes no
"." and ".." in readdir: yes no
Real inode numbers: yes no
32-bit uids/gids: yes no
File creation time: yes no
Xattr and ACL support: no no
Squashfs compresses data, inodes and directories. In addition, inode and
directory data are highly compacted, and packed on byte boundaries. Each
compressed inode is on average 8 bytes in length (the exact length varies on
file type, i.e. regular file, directory, symbolic link, and block/char device
inodes have different sizes).
2. USING SQUASHFS
-----------------
As squashfs is a read-only filesystem, the mksquashfs program must be used to
create populated squashfs filesystems. This and other squashfs utilities
can be obtained from http://www.squashfs.org. Usage instructions can be
obtained from this site also.
3. SQUASHFS FILESYSTEM DESIGN
-----------------------------
A squashfs filesystem consists of seven parts, packed together on a byte
alignment:
---------------
| superblock |
|---------------|
| datablocks |
| & fragments |
|---------------|
| inode table |
|---------------|
| directory |
| table |
|---------------|
| fragment |
| table |
|---------------|
| export |
| table |
|---------------|
| uid/gid |
| lookup table |
---------------
Compressed data blocks are written to the filesystem as files are read from
the source directory, and checked for duplicates. Once all file data has been
written the completed inode, directory, fragment, export and uid/gid lookup
tables are written.
3.1 Inodes
----------
Metadata (inodes and directories) are compressed in 8Kbyte blocks. Each
compressed block is prefixed by a two byte length, the top bit is set if the
block is uncompressed. A block will be uncompressed if the -noI option is set,
or if the compressed block was larger than the uncompressed block.
Inodes are packed into the metadata blocks, and are not aligned to block
boundaries, therefore inodes overlap compressed blocks. Inodes are identified
by a 48-bit number which encodes the location of the compressed metadata block
containing the inode, and the byte offset into that block where the inode is
placed (<block, offset>).
To maximise compression there are different inodes for each file type
(regular file, directory, device, etc.), the inode contents and length
varying with the type.
To further maximise compression, two types of regular file inode and
directory inode are defined: inodes optimised for frequently occurring
regular files and directories, and extended types where extra
information has to be stored.
3.2 Directories
---------------
Like inodes, directories are packed into compressed metadata blocks, stored
in a directory table. Directories are accessed using the start address of
the metablock containing the directory and the offset into the
decompressed block (<block, offset>).
Directories are organised in a slightly complex way, and are not simply
a list of file names. The organisation takes advantage of the
fact that (in most cases) the inodes of the files will be in the same
compressed metadata block, and therefore, can share the start block.
Directories are therefore organised in a two level list, a directory
header containing the shared start block value, and a sequence of directory
entries, each of which share the shared start block. A new directory header
is written once/if the inode start block changes. The directory
header/directory entry list is repeated as many times as necessary.
Directories are sorted, and can contain a directory index to speed up
file lookup. Directory indexes store one entry per metablock, each entry
storing the index/filename mapping to the first directory header
in each metadata block. Directories are sorted in alphabetical order,
and at lookup the index is scanned linearly looking for the first filename
alphabetically larger than the filename being looked up. At this point the
location of the metadata block the filename is in has been found.
The general idea of the index is ensure only one metadata block needs to be
decompressed to do a lookup irrespective of the length of the directory.
This scheme has the advantage that it doesn't require extra memory overhead
and doesn't require much extra storage on disk.
3.3 File data
-------------
Regular files consist of a sequence of contiguous compressed blocks, and/or a
compressed fragment block (tail-end packed block). The compressed size
of each datablock is stored in a block list contained within the
file inode.
To speed up access to datablocks when reading 'large' files (256 Mbytes or
larger), the code implements an index cache that caches the mapping from
block index to datablock location on disk.
The index cache allows Squashfs to handle large files (up to 1.75 TiB) while
retaining a simple and space-efficient block list on disk. The cache
is split into slots, caching up to eight 224 GiB files (128 KiB blocks).
Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
The index cache is designed to be memory efficient, and by default uses
16 KiB.
3.4 Fragment lookup table
-------------------------
Regular files can contain a fragment index which is mapped to a fragment
location on disk and compressed size using a fragment lookup table. This
fragment lookup table is itself stored compressed into metadata blocks.
A second index table is used to locate these. This second index table for
speed of access (and because it is small) is read at mount time and cached
in memory.
3.5 Uid/gid lookup table
------------------------
For space efficiency regular files store uid and gid indexes, which are
converted to 32-bit uids/gids using an id look up table. This table is
stored compressed into metadata blocks. A second index table is used to
locate these. This second index table for speed of access (and because it
is small) is read at mount time and cached in memory.
3.6 Export table
----------------
To enable Squashfs filesystems to be exportable (via NFS etc.) filesystems
can optionally (disabled with the -no-exports Mksquashfs option) contain
an inode number to inode disk location lookup table. This is required to
enable Squashfs to map inode numbers passed in filehandles to the inode
location on disk, which is necessary when the export code reinstantiates
expired/flushed inodes.
This table is stored compressed into metadata blocks. A second index table is
used to locate these. This second index table for speed of access (and because
it is small) is read at mount time and cached in memory.
4. TODOS AND OUTSTANDING ISSUES
-------------------------------
4.1 Todo list
-------------
Implement Xattr and ACL support. The Squashfs 4.0 filesystem layout has hooks
for these but the code has not been written. Once the code has been written
the existing layout should not require modification.
4.2 Squashfs internal cache
---------------------------
Blocks in Squashfs are compressed. To avoid repeatedly decompressing
recently accessed data Squashfs uses two small metadata and fragment caches.
The cache is not used for file datablocks, these are decompressed and cached in
the page-cache in the normal way. The cache is used to temporarily cache
fragment and metadata blocks which have been read as a result of a metadata
(i.e. inode or directory) or fragment access. Because metadata and fragments
are packed together into blocks (to gain greater compression) the read of a
particular piece of metadata or fragment will retrieve other metadata/fragments
which have been packed with it, these because of locality-of-reference may be
read in the near future. Temporarily caching them ensures they are available
for near future access without requiring an additional read and decompress.
In the future this internal cache may be replaced with an implementation which
uses the kernel page cache. Because the page cache operates on page sized
units this may introduce additional complexity in terms of locking and
associated race conditions.

View File

@@ -0,0 +1,8 @@
#
# Makefile for the linux squashfs routines.
#
obj-$(CONFIG_SQUASHFS) += squashfs.o
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
squashfs-y += namei.o super.o symlink.o
#squashfs-y += squashfs2_0.o

View File

@@ -0,0 +1,274 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* block.c
*/
/*
* This file implements the low-level routines to read and decompress
* datablocks and metadata blocks.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/buffer_head.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Read the metadata block length, this is stored in the first two
* bytes of the metadata block.
*/
static struct buffer_head *get_block_length(struct super_block *sb,
u64 *cur_index, int *offset, int *length)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
struct buffer_head *bh;
bh = sb_bread(sb, *cur_index);
if (bh == NULL)
return NULL;
if (msblk->devblksize - *offset == 1) {
*length = (unsigned char) bh->b_data[*offset];
put_bh(bh);
bh = sb_bread(sb, ++(*cur_index));
if (bh == NULL)
return NULL;
*length |= (unsigned char) bh->b_data[0] << 8;
*offset = 1;
} else {
*length = (unsigned char) bh->b_data[*offset] |
(unsigned char) bh->b_data[*offset + 1] << 8;
*offset += 2;
}
return bh;
}
/*
* Read and decompress a metadata block or datablock. Length is non-zero
* if a datablock is being read (the size is stored elsewhere in the
* filesystem), otherwise the length is obtained from the first two bytes of
* the metadata block. A bit in the length field indicates if the block
* is stored uncompressed in the filesystem (usually because compression
* generated a larger block - this does occasionally happen with zlib).
*/
int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
int length, u64 *next_index, int srclength)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
struct buffer_head **bh;
int offset = index & ((1 << msblk->devblksize_log2) - 1);
u64 cur_index = index >> msblk->devblksize_log2;
int bytes, compressed, b = 0, k = 0, page = 0, avail;
bh = kcalloc((msblk->block_size >> msblk->devblksize_log2) + 1,
sizeof(*bh), GFP_KERNEL);
if (bh == NULL)
return -ENOMEM;
if (length) {
/*
* Datablock.
*/
bytes = -offset;
compressed = SQUASHFS_COMPRESSED_BLOCK(length);
length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
if (next_index)
*next_index = index + length;
TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
index, compressed ? "" : "un", length, srclength);
if (length < 0 || length > srclength ||
(index + length) > msblk->bytes_used)
goto read_failure;
for (b = 0; bytes < length; b++, cur_index++) {
bh[b] = sb_getblk(sb, cur_index);
if (bh[b] == NULL)
goto block_release;
bytes += msblk->devblksize;
}
ll_rw_block(READ, b, bh);
} else {
/*
* Metadata block.
*/
if ((index + 2) > msblk->bytes_used)
goto read_failure;
bh[0] = get_block_length(sb, &cur_index, &offset, &length);
if (bh[0] == NULL)
goto read_failure;
b = 1;
bytes = msblk->devblksize - offset;
compressed = SQUASHFS_COMPRESSED(length);
length = SQUASHFS_COMPRESSED_SIZE(length);
if (next_index)
*next_index = index + length + 2;
TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
compressed ? "" : "un", length);
if (length < 0 || length > srclength ||
(index + length) > msblk->bytes_used)
goto block_release;
for (; bytes < length; b++) {
bh[b] = sb_getblk(sb, ++cur_index);
if (bh[b] == NULL)
goto block_release;
bytes += msblk->devblksize;
}
ll_rw_block(READ, b - 1, bh + 1);
}
if (compressed) {
int zlib_err = 0, zlib_init = 0;
/*
* Uncompress block.
*/
mutex_lock(&msblk->read_data_mutex);
msblk->stream.avail_out = 0;
msblk->stream.avail_in = 0;
bytes = length;
do {
if (msblk->stream.avail_in == 0 && k < b) {
avail = min(bytes, msblk->devblksize - offset);
bytes -= avail;
wait_on_buffer(bh[k]);
if (!buffer_uptodate(bh[k]))
goto release_mutex;
if (avail == 0) {
offset = 0;
put_bh(bh[k++]);
continue;
}
msblk->stream.next_in = bh[k]->b_data + offset;
msblk->stream.avail_in = avail;
offset = 0;
}
if (msblk->stream.avail_out == 0) {
msblk->stream.next_out = buffer[page++];
msblk->stream.avail_out = PAGE_CACHE_SIZE;
}
if (!zlib_init) {
zlib_err = zlib_inflateInit(&msblk->stream);
if (zlib_err != Z_OK) {
ERROR("zlib_inflateInit returned"
" unexpected result 0x%x,"
" srclength %d\n", zlib_err,
srclength);
goto release_mutex;
}
zlib_init = 1;
}
zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH);
if (msblk->stream.avail_in == 0 && k < b)
put_bh(bh[k++]);
} while (zlib_err == Z_OK);
if (zlib_err != Z_STREAM_END) {
ERROR("zlib_inflate returned unexpected result"
" 0x%x, srclength %d, avail_in %d,"
" avail_out %d\n", zlib_err, srclength,
msblk->stream.avail_in,
msblk->stream.avail_out);
goto release_mutex;
}
zlib_err = zlib_inflateEnd(&msblk->stream);
if (zlib_err != Z_OK) {
ERROR("zlib_inflateEnd returned unexpected result 0x%x,"
" srclength %d\n", zlib_err, srclength);
goto release_mutex;
}
length = msblk->stream.total_out;
mutex_unlock(&msblk->read_data_mutex);
} else {
/*
* Block is uncompressed.
*/
int i, in, pg_offset = 0;
for (i = 0; i < b; i++) {
wait_on_buffer(bh[i]);
if (!buffer_uptodate(bh[i]))
goto block_release;
}
for (bytes = length; k < b; k++) {
in = min(bytes, msblk->devblksize - offset);
bytes -= in;
while (in) {
if (pg_offset == PAGE_CACHE_SIZE) {
page++;
pg_offset = 0;
}
avail = min_t(int, in, PAGE_CACHE_SIZE -
pg_offset);
memcpy(buffer[page] + pg_offset,
bh[k]->b_data + offset, avail);
in -= avail;
pg_offset += avail;
offset += avail;
}
offset = 0;
put_bh(bh[k]);
}
}
kfree(bh);
return length;
release_mutex:
mutex_unlock(&msblk->read_data_mutex);
block_release:
for (; k < b; k++)
put_bh(bh[k]);
read_failure:
ERROR("sb_bread failed reading block 0x%llx\n", cur_index);
kfree(bh);
return -EIO;
}

View File

@@ -0,0 +1,412 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* cache.c
*/
/*
* Blocks in Squashfs are compressed. To avoid repeatedly decompressing
* recently accessed data Squashfs uses two small metadata and fragment caches.
*
* This file implements a generic cache implementation used for both caches,
* plus functions layered ontop of the generic cache implementation to
* access the metadata and fragment caches.
*
* To avoid out of memory and fragmentation isssues with vmalloc the cache
* uses sequences of kmalloced PAGE_CACHE_SIZE buffers.
*
* It should be noted that the cache is not used for file datablocks, these
* are decompressed and cached in the page-cache in the normal way. The
* cache is only used to temporarily cache fragment and metadata blocks
* which have been read as as a result of a metadata (i.e. inode or
* directory) or fragment access. Because metadata and fragments are packed
* together into blocks (to gain greater compression) the read of a particular
* piece of metadata or fragment will retrieve other metadata/fragments which
* have been packed with it, these because of locality-of-reference may be read
* in the near future. Temporarily caching them ensures they are available for
* near future access without requiring an additional read and decompress.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/zlib.h>
#include <linux/pagemap.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Look-up block in cache, and increment usage count. If not in cache, read
* and decompress it from disk.
*/
struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,
struct squashfs_cache *cache, u64 block, int length)
{
int i, n;
struct squashfs_cache_entry *entry;
spin_lock(&cache->lock);
while (1) {
for (i = 0; i < cache->entries; i++)
if (cache->entry[i].block == block)
break;
if (i == cache->entries) {
/*
* Block not in cache, if all cache entries are used
* go to sleep waiting for one to become available.
*/
if (cache->unused == 0) {
cache->num_waiters++;
spin_unlock(&cache->lock);
wait_event(cache->wait_queue, cache->unused);
spin_lock(&cache->lock);
cache->num_waiters--;
continue;
}
/*
* At least one unused cache entry. A simple
* round-robin strategy is used to choose the entry to
* be evicted from the cache.
*/
i = cache->next_blk;
for (n = 0; n < cache->entries; n++) {
if (cache->entry[i].refcount == 0)
break;
i = (i + 1) % cache->entries;
}
cache->next_blk = (i + 1) % cache->entries;
entry = &cache->entry[i];
/*
* Initialise choosen cache entry, and fill it in from
* disk.
*/
cache->unused--;
entry->block = block;
entry->refcount = 1;
entry->pending = 1;
entry->num_waiters = 0;
entry->error = 0;
spin_unlock(&cache->lock);
entry->length = squashfs_read_data(sb, entry->data,
block, length, &entry->next_index,
cache->block_size);
spin_lock(&cache->lock);
if (entry->length < 0)
entry->error = entry->length;
entry->pending = 0;
/*
* While filling this entry one or more other processes
* have looked it up in the cache, and have slept
* waiting for it to become available.
*/
if (entry->num_waiters) {
spin_unlock(&cache->lock);
wake_up_all(&entry->wait_queue);
} else
spin_unlock(&cache->lock);
goto out;
}
/*
* Block already in cache. Increment refcount so it doesn't
* get reused until we're finished with it, if it was
* previously unused there's one less cache entry available
* for reuse.
*/
entry = &cache->entry[i];
if (entry->refcount == 0)
cache->unused--;
entry->refcount++;
/*
* If the entry is currently being filled in by another process
* go to sleep waiting for it to become available.
*/
if (entry->pending) {
entry->num_waiters++;
spin_unlock(&cache->lock);
wait_event(entry->wait_queue, !entry->pending);
} else
spin_unlock(&cache->lock);
goto out;
}
out:
TRACE("Got %s %d, start block %lld, refcount %d, error %d\n",
cache->name, i, entry->block, entry->refcount, entry->error);
if (entry->error)
ERROR("Unable to read %s cache entry [%llx]\n", cache->name,
block);
return entry;
}
/*
* Release cache entry, once usage count is zero it can be reused.
*/
void squashfs_cache_put(struct squashfs_cache_entry *entry)
{
struct squashfs_cache *cache = entry->cache;
spin_lock(&cache->lock);
entry->refcount--;
if (entry->refcount == 0) {
cache->unused++;
/*
* If there's any processes waiting for a block to become
* available, wake one up.
*/
if (cache->num_waiters) {
spin_unlock(&cache->lock);
wake_up(&cache->wait_queue);
return;
}
}
spin_unlock(&cache->lock);
}
/*
* Delete cache reclaiming all kmalloced buffers.
*/
void squashfs_cache_delete(struct squashfs_cache *cache)
{
int i, j;
if (cache == NULL)
return;
for (i = 0; i < cache->entries; i++) {
if (cache->entry[i].data) {
for (j = 0; j < cache->pages; j++)
kfree(cache->entry[i].data[j]);
kfree(cache->entry[i].data);
}
}
kfree(cache->entry);
kfree(cache);
}
/*
* Initialise cache allocating the specified number of entries, each of
* size block_size. To avoid vmalloc fragmentation issues each entry
* is allocated as a sequence of kmalloced PAGE_CACHE_SIZE buffers.
*/
struct squashfs_cache *squashfs_cache_init(char *name, int entries,
int block_size)
{
int i, j;
struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL);
if (cache == NULL) {
ERROR("Failed to allocate %s cache\n", name);
return NULL;
}
cache->entry = kcalloc(entries, sizeof(*(cache->entry)), GFP_KERNEL);
if (cache->entry == NULL) {
ERROR("Failed to allocate %s cache\n", name);
goto cleanup;
}
cache->next_blk = 0;
cache->unused = entries;
cache->entries = entries;
cache->block_size = block_size;
cache->pages = block_size >> PAGE_CACHE_SHIFT;
cache->name = name;
cache->num_waiters = 0;
spin_lock_init(&cache->lock);
init_waitqueue_head(&cache->wait_queue);
for (i = 0; i < entries; i++) {
struct squashfs_cache_entry *entry = &cache->entry[i];
init_waitqueue_head(&cache->entry[i].wait_queue);
entry->cache = cache;
entry->block = SQUASHFS_INVALID_BLK;
entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL);
if (entry->data == NULL) {
ERROR("Failed to allocate %s cache entry\n", name);
goto cleanup;
}
for (j = 0; j < cache->pages; j++) {
entry->data[j] = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
if (entry->data[j] == NULL) {
ERROR("Failed to allocate %s buffer\n", name);
goto cleanup;
}
}
}
return cache;
cleanup:
squashfs_cache_delete(cache);
return NULL;
}
/*
* Copy upto length bytes from cache entry to buffer starting at offset bytes
* into the cache entry. If there's not length bytes then copy the number of
* bytes available. In all cases return the number of bytes copied.
*/
int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry,
int offset, int length)
{
int remaining = length;
if (length == 0)
return 0;
else if (buffer == NULL)
return min(length, entry->length - offset);
while (offset < entry->length) {
void *buff = entry->data[offset / PAGE_CACHE_SIZE]
+ (offset % PAGE_CACHE_SIZE);
int bytes = min_t(int, entry->length - offset,
PAGE_CACHE_SIZE - (offset % PAGE_CACHE_SIZE));
if (bytes >= remaining) {
memcpy(buffer, buff, remaining);
remaining = 0;
break;
}
memcpy(buffer, buff, bytes);
buffer += bytes;
remaining -= bytes;
offset += bytes;
}
return length - remaining;
}
/*
* Read length bytes from metadata position <block, offset> (block is the
* start of the compressed block on disk, and offset is the offset into
* the block once decompressed). Data is packed into consecutive blocks,
* and length bytes may require reading more than one block.
*/
int squashfs_read_metadata(struct super_block *sb, void *buffer,
u64 *block, int *offset, int length)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int bytes, copied = length;
struct squashfs_cache_entry *entry;
TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset);
while (length) {
entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0);
if (entry->error)
return entry->error;
else if (*offset >= entry->length)
return -EIO;
bytes = squashfs_copy_data(buffer, entry, *offset, length);
if (buffer)
buffer += bytes;
length -= bytes;
*offset += bytes;
if (*offset == entry->length) {
*block = entry->next_index;
*offset = 0;
}
squashfs_cache_put(entry);
}
return copied;
}
/*
* Look-up in the fragmment cache the fragment located at <start_block> in the
* filesystem. If necessary read and decompress it from disk.
*/
struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *sb,
u64 start_block, int length)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
return squashfs_cache_get(sb, msblk->fragment_cache, start_block,
length);
}
/*
* Read and decompress the datablock located at <start_block> in the
* filesystem. The cache is used here to avoid duplicating locking and
* read/decompress code.
*/
struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb,
u64 start_block, int length)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
return squashfs_cache_get(sb, msblk->read_page, start_block, length);
}
/*
* Read a filesystem table (uncompressed sequence of bytes) from disk
*/
int squashfs_read_table(struct super_block *sb, void *buffer, u64 block,
int length)
{
int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
int i, res;
void **data = kcalloc(pages, sizeof(void *), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
data[i] = buffer;
res = squashfs_read_data(sb, data, block, length |
SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length);
kfree(data);
return res;
}

View File

@@ -0,0 +1,235 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* dir.c
*/
/*
* This file implements code to read directories from disk.
*
* See namei.c for a description of directory organisation on disk.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
static const unsigned char squashfs_filetype_table[] = {
DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
};
/*
* Lookup offset (f_pos) in the directory index, returning the
* metadata block containing it.
*
* If we get an error reading the index then return the part of the index
* (if any) we have managed to read - the index isn't essential, just
* quicker.
*/
static int get_dir_index_using_offset(struct super_block *sb,
u64 *next_block, int *next_offset, u64 index_start, int index_offset,
int i_count, u64 f_pos)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int err, i, index, length = 0;
struct squashfs_dir_index dir_index;
TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %lld\n",
i_count, f_pos);
/*
* Translate from external f_pos to the internal f_pos. This
* is offset by 3 because we invent "." and ".." entries which are
* not actually stored in the directory.
*/
if (f_pos < 3)
return f_pos;
f_pos -= 3;
for (i = 0; i < i_count; i++) {
err = squashfs_read_metadata(sb, &dir_index, &index_start,
&index_offset, sizeof(dir_index));
if (err < 0)
break;
index = le32_to_cpu(dir_index.index);
if (index > f_pos)
/*
* Found the index we're looking for.
*/
break;
err = squashfs_read_metadata(sb, NULL, &index_start,
&index_offset, le32_to_cpu(dir_index.size) + 1);
if (err < 0)
break;
length = index;
*next_block = le32_to_cpu(dir_index.start_block) +
msblk->directory_table;
}
*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
/*
* Translate back from internal f_pos to external f_pos.
*/
return length + 3;
}
static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
{
struct inode *inode = file->f_dentry->d_inode;
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
u64 block = squashfs_i(inode)->start + msblk->directory_table;
int offset = squashfs_i(inode)->offset, length = 0, dir_count, size,
type, err;
unsigned int inode_number;
struct squashfs_dir_header dirh;
struct squashfs_dir_entry *dire;
TRACE("Entered squashfs_readdir [%llx:%x]\n", block, offset);
dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
if (dire == NULL) {
ERROR("Failed to allocate squashfs_dir_entry\n");
goto finish;
}
/*
* Return "." and ".." entries as the first two filenames in the
* directory. To maximise compression these two entries are not
* stored in the directory, and so we invent them here.
*
* It also means that the external f_pos is offset by 3 from the
* on-disk directory f_pos.
*/
while (file->f_pos < 3) {
char *name;
int i_ino;
if (file->f_pos == 0) {
name = ".";
size = 1;
i_ino = inode->i_ino;
} else {
name = "..";
size = 2;
i_ino = squashfs_i(inode)->parent;
}
TRACE("Calling filldir(%p, %s, %d, %lld, %d, %d)\n",
dirent, name, size, file->f_pos, i_ino,
squashfs_filetype_table[1]);
if (filldir(dirent, name, size, file->f_pos, i_ino,
squashfs_filetype_table[1]) < 0) {
TRACE("Filldir returned less than 0\n");
goto finish;
}
file->f_pos += size;
}
length = get_dir_index_using_offset(inode->i_sb, &block, &offset,
squashfs_i(inode)->dir_idx_start,
squashfs_i(inode)->dir_idx_offset,
squashfs_i(inode)->dir_idx_cnt,
file->f_pos);
while (length < i_size_read(inode)) {
/*
* Read directory header
*/
err = squashfs_read_metadata(inode->i_sb, &dirh, &block,
&offset, sizeof(dirh));
if (err < 0)
goto failed_read;
length += sizeof(dirh);
dir_count = le32_to_cpu(dirh.count) + 1;
while (dir_count--) {
/*
* Read directory entry.
*/
err = squashfs_read_metadata(inode->i_sb, dire, &block,
&offset, sizeof(*dire));
if (err < 0)
goto failed_read;
size = le16_to_cpu(dire->size) + 1;
err = squashfs_read_metadata(inode->i_sb, dire->name,
&block, &offset, size);
if (err < 0)
goto failed_read;
length += sizeof(*dire) + size;
if (file->f_pos >= length)
continue;
dire->name[size] = '\0';
inode_number = le32_to_cpu(dirh.inode_number) +
((short) le16_to_cpu(dire->inode_number));
type = le16_to_cpu(dire->type);
TRACE("Calling filldir(%p, %s, %d, %lld, %x:%x, %d, %d)"
"\n", dirent, dire->name, size,
file->f_pos,
le32_to_cpu(dirh.start_block),
le16_to_cpu(dire->offset),
inode_number,
squashfs_filetype_table[type]);
if (filldir(dirent, dire->name, size, file->f_pos,
inode_number,
squashfs_filetype_table[type]) < 0) {
TRACE("Filldir returned less than 0\n");
goto finish;
}
file->f_pos = length;
}
}
finish:
kfree(dire);
return 0;
failed_read:
ERROR("Unable to read directory block [%llx:%x]\n", block, offset);
kfree(dire);
return 0;
}
const struct file_operations squashfs_dir_ops = {
.read = generic_read_dir,
.readdir = squashfs_readdir
};

View File

@@ -0,0 +1,155 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* export.c
*/
/*
* This file implements code to make Squashfs filesystems exportable (NFS etc.)
*
* The export code uses an inode lookup table to map inode numbers passed in
* filehandles to an inode location on disk. This table is stored compressed
* into metadata blocks. A second index table is used to locate these. This
* second index table for speed of access (and because it is small) is read at
* mount time and cached in memory.
*
* The inode lookup table is used only by the export code, inode disk
* locations are directly encoded in directories, enabling direct access
* without an intermediate lookup for all operations except the export ops.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/dcache.h>
#include <linux/exportfs.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Look-up inode number (ino) in table, returning the inode location.
*/
static long long squashfs_inode_lookup(struct super_block *sb, int ino_num)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int blk = SQUASHFS_LOOKUP_BLOCK(ino_num - 1);
int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino_num - 1);
u64 start = le64_to_cpu(msblk->inode_lookup_table[blk]);
__le64 ino;
int err;
TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino_num);
err = squashfs_read_metadata(sb, &ino, &start, &offset, sizeof(ino));
if (err < 0)
return err;
TRACE("squashfs_inode_lookup, inode = 0x%llx\n",
(u64) le64_to_cpu(ino));
return le64_to_cpu(ino);
}
static struct dentry *squashfs_export_iget(struct super_block *sb,
unsigned int ino_num)
{
long long ino;
struct dentry *dentry = ERR_PTR(-ENOENT);
TRACE("Entered squashfs_export_iget\n");
ino = squashfs_inode_lookup(sb, ino_num);
if (ino >= 0)
dentry = d_obtain_alias(squashfs_iget(sb, ino, ino_num));
return dentry;
}
static struct dentry *squashfs_fh_to_dentry(struct super_block *sb,
struct fid *fid, int fh_len, int fh_type)
{
if ((fh_type != FILEID_INO32_GEN && fh_type != FILEID_INO32_GEN_PARENT)
|| fh_len < 2)
return NULL;
return squashfs_export_iget(sb, fid->i32.ino);
}
static struct dentry *squashfs_fh_to_parent(struct super_block *sb,
struct fid *fid, int fh_len, int fh_type)
{
if (fh_type != FILEID_INO32_GEN_PARENT || fh_len < 4)
return NULL;
return squashfs_export_iget(sb, fid->i32.parent_ino);
}
static struct dentry *squashfs_get_parent(struct dentry *child)
{
struct inode *inode = child->d_inode;
unsigned int parent_ino = squashfs_i(inode)->parent;
return squashfs_export_iget(inode->i_sb, parent_ino);
}
/*
* Read uncompressed inode lookup table indexes off disk into memory
*/
__le64 *squashfs_read_inode_lookup_table(struct super_block *sb,
u64 lookup_table_start, unsigned int inodes)
{
unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes);
__le64 *inode_lookup_table;
int err;
TRACE("In read_inode_lookup_table, length %d\n", length);
/* Allocate inode lookup table indexes */
inode_lookup_table = kmalloc(length, GFP_KERNEL);
if (inode_lookup_table == NULL) {
ERROR("Failed to allocate inode lookup table\n");
return ERR_PTR(-ENOMEM);
}
err = squashfs_read_table(sb, inode_lookup_table, lookup_table_start,
length);
if (err < 0) {
ERROR("unable to read inode lookup table\n");
kfree(inode_lookup_table);
return ERR_PTR(err);
}
return inode_lookup_table;
}
const struct export_operations squashfs_export_ops = {
.fh_to_dentry = squashfs_fh_to_dentry,
.fh_to_parent = squashfs_fh_to_parent,
.get_parent = squashfs_get_parent
};

View File

@@ -0,0 +1,502 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* file.c
*/
/*
* This file contains code for handling regular files. A regular file
* consists of a sequence of contiguous compressed blocks, and/or a
* compressed fragment block (tail-end packed block). The compressed size
* of each datablock is stored in a block list contained within the
* file inode (itself stored in one or more compressed metadata blocks).
*
* To speed up access to datablocks when reading 'large' files (256 Mbytes or
* larger), the code implements an index cache that caches the mapping from
* block index to datablock location on disk.
*
* The index cache allows Squashfs to handle large files (up to 1.75 TiB) while
* retaining a simple and space-efficient block list on disk. The cache
* is split into slots, caching up to eight 224 GiB files (128 KiB blocks).
* Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
* The index cache is designed to be memory efficient, and by default uses
* 16 KiB.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/mutex.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Locate cache slot in range [offset, index] for specified inode. If
* there's more than one return the slot closest to index.
*/
static struct meta_index *locate_meta_index(struct inode *inode, int offset,
int index)
{
struct meta_index *meta = NULL;
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
int i;
mutex_lock(&msblk->meta_index_mutex);
TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
if (msblk->meta_index == NULL)
goto not_allocated;
for (i = 0; i < SQUASHFS_META_SLOTS; i++) {
if (msblk->meta_index[i].inode_number == inode->i_ino &&
msblk->meta_index[i].offset >= offset &&
msblk->meta_index[i].offset <= index &&
msblk->meta_index[i].locked == 0) {
TRACE("locate_meta_index: entry %d, offset %d\n", i,
msblk->meta_index[i].offset);
meta = &msblk->meta_index[i];
offset = meta->offset;
}
}
if (meta)
meta->locked = 1;
not_allocated:
mutex_unlock(&msblk->meta_index_mutex);
return meta;
}
/*
* Find and initialise an empty cache slot for index offset.
*/
static struct meta_index *empty_meta_index(struct inode *inode, int offset,
int skip)
{
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
struct meta_index *meta = NULL;
int i;
mutex_lock(&msblk->meta_index_mutex);
TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
if (msblk->meta_index == NULL) {
/*
* First time cache index has been used, allocate and
* initialise. The cache index could be allocated at
* mount time but doing it here means it is allocated only
* if a 'large' file is read.
*/
msblk->meta_index = kcalloc(SQUASHFS_META_SLOTS,
sizeof(*(msblk->meta_index)), GFP_KERNEL);
if (msblk->meta_index == NULL) {
ERROR("Failed to allocate meta_index\n");
goto failed;
}
for (i = 0; i < SQUASHFS_META_SLOTS; i++) {
msblk->meta_index[i].inode_number = 0;
msblk->meta_index[i].locked = 0;
}
msblk->next_meta_index = 0;
}
for (i = SQUASHFS_META_SLOTS; i &&
msblk->meta_index[msblk->next_meta_index].locked; i--)
msblk->next_meta_index = (msblk->next_meta_index + 1) %
SQUASHFS_META_SLOTS;
if (i == 0) {
TRACE("empty_meta_index: failed!\n");
goto failed;
}
TRACE("empty_meta_index: returned meta entry %d, %p\n",
msblk->next_meta_index,
&msblk->meta_index[msblk->next_meta_index]);
meta = &msblk->meta_index[msblk->next_meta_index];
msblk->next_meta_index = (msblk->next_meta_index + 1) %
SQUASHFS_META_SLOTS;
meta->inode_number = inode->i_ino;
meta->offset = offset;
meta->skip = skip;
meta->entries = 0;
meta->locked = 1;
failed:
mutex_unlock(&msblk->meta_index_mutex);
return meta;
}
static void release_meta_index(struct inode *inode, struct meta_index *meta)
{
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
mutex_lock(&msblk->meta_index_mutex);
meta->locked = 0;
mutex_unlock(&msblk->meta_index_mutex);
}
/*
* Read the next n blocks from the block list, starting from
* metadata block <start_block, offset>.
*/
static long long read_indexes(struct super_block *sb, int n,
u64 *start_block, int *offset)
{
int err, i;
long long block = 0;
__le32 *blist = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
if (blist == NULL) {
ERROR("read_indexes: Failed to allocate block_list\n");
return -ENOMEM;
}
while (n) {
int blocks = min_t(int, n, PAGE_CACHE_SIZE >> 2);
err = squashfs_read_metadata(sb, blist, start_block,
offset, blocks << 2);
if (err < 0) {
ERROR("read_indexes: reading block [%llx:%x]\n",
*start_block, *offset);
goto failure;
}
for (i = 0; i < blocks; i++) {
int size = le32_to_cpu(blist[i]);
block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
}
n -= blocks;
}
kfree(blist);
return block;
failure:
kfree(blist);
return err;
}
/*
* Each cache index slot has SQUASHFS_META_ENTRIES, each of which
* can cache one index -> datablock/blocklist-block mapping. We wish
* to distribute these over the length of the file, entry[0] maps index x,
* entry[1] maps index x + skip, entry[2] maps index x + 2 * skip, and so on.
* The larger the file, the greater the skip factor. The skip factor is
* limited to the size of the metadata cache (SQUASHFS_CACHED_BLKS) to ensure
* the number of metadata blocks that need to be read fits into the cache.
* If the skip factor is limited in this way then the file will use multiple
* slots.
*/
static inline int calculate_skip(int blocks)
{
int skip = blocks / ((SQUASHFS_META_ENTRIES + 1)
* SQUASHFS_META_INDEXES);
return min(SQUASHFS_CACHED_BLKS - 1, skip + 1);
}
/*
* Search and grow the index cache for the specified inode, returning the
* on-disk locations of the datablock and block list metadata block
* <index_block, index_offset> for index (scaled to nearest cache index).
*/
static int fill_meta_index(struct inode *inode, int index,
u64 *index_block, int *index_offset, u64 *data_block)
{
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
int skip = calculate_skip(i_size_read(inode) >> msblk->block_log);
int offset = 0;
struct meta_index *meta;
struct meta_entry *meta_entry;
u64 cur_index_block = squashfs_i(inode)->block_list_start;
int cur_offset = squashfs_i(inode)->offset;
u64 cur_data_block = squashfs_i(inode)->start;
int err, i;
/*
* Scale index to cache index (cache slot entry)
*/
index /= SQUASHFS_META_INDEXES * skip;
while (offset < index) {
meta = locate_meta_index(inode, offset + 1, index);
if (meta == NULL) {
meta = empty_meta_index(inode, offset + 1, skip);
if (meta == NULL)
goto all_done;
} else {
offset = index < meta->offset + meta->entries ? index :
meta->offset + meta->entries - 1;
meta_entry = &meta->meta_entry[offset - meta->offset];
cur_index_block = meta_entry->index_block +
msblk->inode_table;
cur_offset = meta_entry->offset;
cur_data_block = meta_entry->data_block;
TRACE("get_meta_index: offset %d, meta->offset %d, "
"meta->entries %d\n", offset, meta->offset,
meta->entries);
TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
" data_block 0x%llx\n", cur_index_block,
cur_offset, cur_data_block);
}
/*
* If necessary grow cache slot by reading block list. Cache
* slot is extended up to index or to the end of the slot, in
* which case further slots will be used.
*/
for (i = meta->offset + meta->entries; i <= index &&
i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
int blocks = skip * SQUASHFS_META_INDEXES;
long long res = read_indexes(inode->i_sb, blocks,
&cur_index_block, &cur_offset);
if (res < 0) {
if (meta->entries == 0)
/*
* Don't leave an empty slot on read
* error allocated to this inode...
*/
meta->inode_number = 0;
err = res;
goto failed;
}
cur_data_block += res;
meta_entry = &meta->meta_entry[i - meta->offset];
meta_entry->index_block = cur_index_block -
msblk->inode_table;
meta_entry->offset = cur_offset;
meta_entry->data_block = cur_data_block;
meta->entries++;
offset++;
}
TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
meta->offset, meta->entries);
release_meta_index(inode, meta);
}
all_done:
*index_block = cur_index_block;
*index_offset = cur_offset;
*data_block = cur_data_block;
/*
* Scale cache index (cache slot entry) to index
*/
return offset * SQUASHFS_META_INDEXES * skip;
failed:
release_meta_index(inode, meta);
return err;
}
/*
* Get the on-disk location and compressed size of the datablock
* specified by index. Fill_meta_index() does most of the work.
*/
static int read_blocklist(struct inode *inode, int index, u64 *block)
{
u64 start;
long long blks;
int offset;
__le32 size;
int res = fill_meta_index(inode, index, &start, &offset, block);
TRACE("read_blocklist: res %d, index %d, start 0x%llx, offset"
" 0x%x, block 0x%llx\n", res, index, start, offset,
*block);
if (res < 0)
return res;
/*
* res contains the index of the mapping returned by fill_meta_index(),
* this will likely be less than the desired index (because the
* meta_index cache works at a higher granularity). Read any
* extra block indexes needed.
*/
if (res < index) {
blks = read_indexes(inode->i_sb, index - res, &start, &offset);
if (blks < 0)
return (int) blks;
*block += blks;
}
/*
* Read length of block specified by index.
*/
res = squashfs_read_metadata(inode->i_sb, &size, &start, &offset,
sizeof(size));
if (res < 0)
return res;
return le32_to_cpu(size);
}
static int squashfs_readpage(struct file *file, struct page *page)
{
struct inode *inode = page->mapping->host;
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
int bytes, i, offset = 0, sparse = 0;
struct squashfs_cache_entry *buffer = NULL;
void *pageaddr;
int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
int start_index = page->index & ~mask;
int end_index = start_index | mask;
int file_end = i_size_read(inode) >> msblk->block_log;
TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
page->index, squashfs_i(inode)->start);
if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
PAGE_CACHE_SHIFT))
goto out;
if (index < file_end || squashfs_i(inode)->fragment_block ==
SQUASHFS_INVALID_BLK) {
/*
* Reading a datablock from disk. Need to read block list
* to get location and block size.
*/
u64 block = 0;
int bsize = read_blocklist(inode, index, &block);
if (bsize < 0)
goto error_out;
if (bsize == 0) { /* hole */
bytes = index == file_end ?
(i_size_read(inode) & (msblk->block_size - 1)) :
msblk->block_size;
sparse = 1;
} else {
/*
* Read and decompress datablock.
*/
buffer = squashfs_get_datablock(inode->i_sb,
block, bsize);
if (buffer->error) {
ERROR("Unable to read page, block %llx, size %x"
"\n", block, bsize);
squashfs_cache_put(buffer);
goto error_out;
}
bytes = buffer->length;
}
} else {
/*
* Datablock is stored inside a fragment (tail-end packed
* block).
*/
buffer = squashfs_get_fragment(inode->i_sb,
squashfs_i(inode)->fragment_block,
squashfs_i(inode)->fragment_size);
if (buffer->error) {
ERROR("Unable to read page, block %llx, size %x\n",
squashfs_i(inode)->fragment_block,
squashfs_i(inode)->fragment_size);
squashfs_cache_put(buffer);
goto error_out;
}
bytes = i_size_read(inode) & (msblk->block_size - 1);
offset = squashfs_i(inode)->fragment_offset;
}
/*
* Loop copying datablock into pages. As the datablock likely covers
* many PAGE_CACHE_SIZE pages (default block size is 128 KiB) explicitly
* grab the pages from the page cache, except for the page that we've
* been called to fill.
*/
for (i = start_index; i <= end_index && bytes > 0; i++,
bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
struct page *push_page;
int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE);
TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
push_page = (i == page->index) ? page :
grab_cache_page_nowait(page->mapping, i);
if (!push_page)
continue;
if (PageUptodate(push_page))
goto skip_page;
pageaddr = kmap_atomic(push_page, KM_USER0);
squashfs_copy_data(pageaddr, buffer, offset, avail);
memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
kunmap_atomic(pageaddr, KM_USER0);
flush_dcache_page(push_page);
SetPageUptodate(push_page);
skip_page:
unlock_page(push_page);
if (i != page->index)
page_cache_release(push_page);
}
if (!sparse)
squashfs_cache_put(buffer);
return 0;
error_out:
SetPageError(page);
out:
pageaddr = kmap_atomic(page, KM_USER0);
memset(pageaddr, 0, PAGE_CACHE_SIZE);
kunmap_atomic(pageaddr, KM_USER0);
flush_dcache_page(page);
if (!PageError(page))
SetPageUptodate(page);
unlock_page(page);
return 0;
}
const struct address_space_operations squashfs_aops = {
.readpage = squashfs_readpage
};

View File

@@ -0,0 +1,98 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* fragment.c
*/
/*
* This file implements code to handle compressed fragments (tail-end packed
* datablocks).
*
* Regular files contain a fragment index which is mapped to a fragment
* location on disk and compressed size using a fragment lookup table.
* Like everything in Squashfs this fragment lookup table is itself stored
* compressed into metadata blocks. A second index table is used to locate
* these. This second index table for speed of access (and because it
* is small) is read at mount time and cached in memory.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Look-up fragment using the fragment index table. Return the on disk
* location of the fragment and its compressed size
*/
int squashfs_frag_lookup(struct super_block *sb, unsigned int fragment,
u64 *fragment_block)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int block = SQUASHFS_FRAGMENT_INDEX(fragment);
int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
u64 start_block = le64_to_cpu(msblk->fragment_index[block]);
struct squashfs_fragment_entry fragment_entry;
int size;
size = squashfs_read_metadata(sb, &fragment_entry, &start_block,
&offset, sizeof(fragment_entry));
if (size < 0)
return size;
*fragment_block = le64_to_cpu(fragment_entry.start_block);
size = le32_to_cpu(fragment_entry.size);
return size;
}
/*
* Read the uncompressed fragment lookup table indexes off disk into memory
*/
__le64 *squashfs_read_fragment_index_table(struct super_block *sb,
u64 fragment_table_start, unsigned int fragments)
{
unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(fragments);
__le64 *fragment_index;
int err;
/* Allocate fragment lookup table indexes */
fragment_index = kmalloc(length, GFP_KERNEL);
if (fragment_index == NULL) {
ERROR("Failed to allocate fragment index table\n");
return ERR_PTR(-ENOMEM);
}
err = squashfs_read_table(sb, fragment_index, fragment_table_start,
length);
if (err < 0) {
ERROR("unable to read fragment index table\n");
kfree(fragment_index);
return ERR_PTR(err);
}
return fragment_index;
}

View File

@@ -0,0 +1,94 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* id.c
*/
/*
* This file implements code to handle uids and gids.
*
* For space efficiency regular files store uid and gid indexes, which are
* converted to 32-bit uids/gids using an id look up table. This table is
* stored compressed into metadata blocks. A second index table is used to
* locate these. This second index table for speed of access (and because it
* is small) is read at mount time and cached in memory.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Map uid/gid index into real 32-bit uid/gid using the id look up table
*/
int squashfs_get_id(struct super_block *sb, unsigned int index,
unsigned int *id)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int block = SQUASHFS_ID_BLOCK(index);
int offset = SQUASHFS_ID_BLOCK_OFFSET(index);
u64 start_block = le64_to_cpu(msblk->id_table[block]);
__le32 disk_id;
int err;
err = squashfs_read_metadata(sb, &disk_id, &start_block, &offset,
sizeof(disk_id));
if (err < 0)
return err;
*id = le32_to_cpu(disk_id);
return 0;
}
/*
* Read uncompressed id lookup table indexes from disk into memory
*/
__le64 *squashfs_read_id_index_table(struct super_block *sb,
u64 id_table_start, unsigned short no_ids)
{
unsigned int length = SQUASHFS_ID_BLOCK_BYTES(no_ids);
__le64 *id_table;
int err;
TRACE("In read_id_index_table, length %d\n", length);
/* Allocate id lookup table indexes */
id_table = kmalloc(length, GFP_KERNEL);
if (id_table == NULL) {
ERROR("Failed to allocate id index table\n");
return ERR_PTR(-ENOMEM);
}
err = squashfs_read_table(sb, id_table, id_table_start, length);
if (err < 0) {
ERROR("unable to read id index table\n");
kfree(id_table);
return ERR_PTR(err);
}
return id_table;
}

View File

@@ -0,0 +1,346 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* inode.c
*/
/*
* This file implements code to create and read inodes from disk.
*
* Inodes in Squashfs are identified by a 48-bit inode which encodes the
* location of the compressed metadata block containing the inode, and the byte
* offset into that block where the inode is placed (<block, offset>).
*
* To maximise compression there are different inodes for each file type
* (regular file, directory, device, etc.), the inode contents and length
* varying with the type.
*
* To further maximise compression, two types of regular file inode and
* directory inode are defined: inodes optimised for frequently occurring
* regular files and directories, and extended types where extra
* information has to be stored.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Initialise VFS inode with the base inode information common to all
* Squashfs inode types. Sqsh_ino contains the unswapped base inode
* off disk.
*/
static int squashfs_new_inode(struct super_block *sb, struct inode *inode,
struct squashfs_base_inode *sqsh_ino)
{
int err;
err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->uid), &inode->i_uid);
if (err)
return err;
err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->guid), &inode->i_gid);
if (err)
return err;
inode->i_ino = le32_to_cpu(sqsh_ino->inode_number);
inode->i_mtime.tv_sec = le32_to_cpu(sqsh_ino->mtime);
inode->i_atime.tv_sec = inode->i_mtime.tv_sec;
inode->i_ctime.tv_sec = inode->i_mtime.tv_sec;
inode->i_mode = le16_to_cpu(sqsh_ino->mode);
inode->i_size = 0;
return err;
}
struct inode *squashfs_iget(struct super_block *sb, long long ino,
unsigned int ino_number)
{
struct inode *inode = iget_locked(sb, ino_number);
int err;
TRACE("Entered squashfs_iget\n");
if (!inode)
return ERR_PTR(-ENOMEM);
if (!(inode->i_state & I_NEW))
return inode;
err = squashfs_read_inode(inode, ino);
if (err) {
iget_failed(inode);
return ERR_PTR(err);
}
unlock_new_inode(inode);
return inode;
}
/*
* Initialise VFS inode by reading inode from inode table (compressed
* metadata). The format and amount of data read depends on type.
*/
int squashfs_read_inode(struct inode *inode, long long ino)
{
struct super_block *sb = inode->i_sb;
struct squashfs_sb_info *msblk = sb->s_fs_info;
u64 block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
int err, type, offset = SQUASHFS_INODE_OFFSET(ino);
union squashfs_inode squashfs_ino;
struct squashfs_base_inode *sqshb_ino = &squashfs_ino.base;
TRACE("Entered squashfs_read_inode\n");
/*
* Read inode base common to all inode types.
*/
err = squashfs_read_metadata(sb, sqshb_ino, &block,
&offset, sizeof(*sqshb_ino));
if (err < 0)
goto failed_read;
err = squashfs_new_inode(sb, inode, sqshb_ino);
if (err)
goto failed_read;
block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
offset = SQUASHFS_INODE_OFFSET(ino);
type = le16_to_cpu(sqshb_ino->inode_type);
switch (type) {
case SQUASHFS_REG_TYPE: {
unsigned int frag_offset, frag_size, frag;
u64 frag_blk;
struct squashfs_reg_inode *sqsh_ino = &squashfs_ino.reg;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
frag = le32_to_cpu(sqsh_ino->fragment);
if (frag != SQUASHFS_INVALID_FRAG) {
frag_offset = le32_to_cpu(sqsh_ino->offset);
frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
if (frag_size < 0) {
err = frag_size;
goto failed_read;
}
} else {
frag_blk = SQUASHFS_INVALID_BLK;
frag_size = 0;
frag_offset = 0;
}
inode->i_nlink = 1;
inode->i_size = le32_to_cpu(sqsh_ino->file_size);
inode->i_fop = &generic_ro_fops;
inode->i_mode |= S_IFREG;
inode->i_blocks = ((inode->i_size - 1) >> 9) + 1;
squashfs_i(inode)->fragment_block = frag_blk;
squashfs_i(inode)->fragment_size = frag_size;
squashfs_i(inode)->fragment_offset = frag_offset;
squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
squashfs_i(inode)->block_list_start = block;
squashfs_i(inode)->offset = offset;
inode->i_data.a_ops = &squashfs_aops;
TRACE("File inode %x:%x, start_block %llx, block_list_start "
"%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
offset, squashfs_i(inode)->start, block, offset);
break;
}
case SQUASHFS_LREG_TYPE: {
unsigned int frag_offset, frag_size, frag;
u64 frag_blk;
struct squashfs_lreg_inode *sqsh_ino = &squashfs_ino.lreg;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
frag = le32_to_cpu(sqsh_ino->fragment);
if (frag != SQUASHFS_INVALID_FRAG) {
frag_offset = le32_to_cpu(sqsh_ino->offset);
frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
if (frag_size < 0) {
err = frag_size;
goto failed_read;
}
} else {
frag_blk = SQUASHFS_INVALID_BLK;
frag_size = 0;
frag_offset = 0;
}
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
inode->i_size = le64_to_cpu(sqsh_ino->file_size);
inode->i_fop = &generic_ro_fops;
inode->i_mode |= S_IFREG;
inode->i_blocks = ((inode->i_size -
le64_to_cpu(sqsh_ino->sparse) - 1) >> 9) + 1;
squashfs_i(inode)->fragment_block = frag_blk;
squashfs_i(inode)->fragment_size = frag_size;
squashfs_i(inode)->fragment_offset = frag_offset;
squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block);
squashfs_i(inode)->block_list_start = block;
squashfs_i(inode)->offset = offset;
inode->i_data.a_ops = &squashfs_aops;
TRACE("File inode %x:%x, start_block %llx, block_list_start "
"%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
offset, squashfs_i(inode)->start, block, offset);
break;
}
case SQUASHFS_DIR_TYPE: {
struct squashfs_dir_inode *sqsh_ino = &squashfs_ino.dir;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
inode->i_size = le16_to_cpu(sqsh_ino->file_size);
inode->i_op = &squashfs_dir_inode_ops;
inode->i_fop = &squashfs_dir_ops;
inode->i_mode |= S_IFDIR;
squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
squashfs_i(inode)->dir_idx_cnt = 0;
squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);
TRACE("Directory inode %x:%x, start_block %llx, offset %x\n",
SQUASHFS_INODE_BLK(ino), offset,
squashfs_i(inode)->start,
le16_to_cpu(sqsh_ino->offset));
break;
}
case SQUASHFS_LDIR_TYPE: {
struct squashfs_ldir_inode *sqsh_ino = &squashfs_ino.ldir;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
inode->i_size = le32_to_cpu(sqsh_ino->file_size);
inode->i_op = &squashfs_dir_inode_ops;
inode->i_fop = &squashfs_dir_ops;
inode->i_mode |= S_IFDIR;
squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
squashfs_i(inode)->dir_idx_start = block;
squashfs_i(inode)->dir_idx_offset = offset;
squashfs_i(inode)->dir_idx_cnt = le16_to_cpu(sqsh_ino->i_count);
squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);
TRACE("Long directory inode %x:%x, start_block %llx, offset "
"%x\n", SQUASHFS_INODE_BLK(ino), offset,
squashfs_i(inode)->start,
le16_to_cpu(sqsh_ino->offset));
break;
}
case SQUASHFS_SYMLINK_TYPE:
case SQUASHFS_LSYMLINK_TYPE: {
struct squashfs_symlink_inode *sqsh_ino = &squashfs_ino.symlink;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
inode->i_op = &page_symlink_inode_operations;
inode->i_data.a_ops = &squashfs_symlink_aops;
inode->i_mode |= S_IFLNK;
squashfs_i(inode)->start = block;
squashfs_i(inode)->offset = offset;
TRACE("Symbolic link inode %x:%x, start_block %llx, offset "
"%x\n", SQUASHFS_INODE_BLK(ino), offset,
block, offset);
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE:
case SQUASHFS_LBLKDEV_TYPE:
case SQUASHFS_LCHRDEV_TYPE: {
struct squashfs_dev_inode *sqsh_ino = &squashfs_ino.dev;
unsigned int rdev;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
if (type == SQUASHFS_CHRDEV_TYPE)
inode->i_mode |= S_IFCHR;
else
inode->i_mode |= S_IFBLK;
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
rdev = le32_to_cpu(sqsh_ino->rdev);
init_special_inode(inode, inode->i_mode, new_decode_dev(rdev));
TRACE("Device inode %x:%x, rdev %x\n",
SQUASHFS_INODE_BLK(ino), offset, rdev);
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE:
case SQUASHFS_LFIFO_TYPE:
case SQUASHFS_LSOCKET_TYPE: {
struct squashfs_ipc_inode *sqsh_ino = &squashfs_ino.ipc;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
if (type == SQUASHFS_FIFO_TYPE)
inode->i_mode |= S_IFIFO;
else
inode->i_mode |= S_IFSOCK;
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
init_special_inode(inode, inode->i_mode, 0);
break;
}
default:
ERROR("Unknown inode type %d in squashfs_iget!\n", type);
return -EINVAL;
}
return 0;
failed_read:
ERROR("Unable to read inode 0x%llx\n", ino);
return err;
}

View File

@@ -0,0 +1,242 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* namei.c
*/
/*
* This file implements code to do filename lookup in directories.
*
* Like inodes, directories are packed into compressed metadata blocks, stored
* in a directory table. Directories are accessed using the start address of
* the metablock containing the directory and the offset into the
* decompressed block (<block, offset>).
*
* Directories are organised in a slightly complex way, and are not simply
* a list of file names. The organisation takes advantage of the
* fact that (in most cases) the inodes of the files will be in the same
* compressed metadata block, and therefore, can share the start block.
* Directories are therefore organised in a two level list, a directory
* header containing the shared start block value, and a sequence of directory
* entries, each of which share the shared start block. A new directory header
* is written once/if the inode start block changes. The directory
* header/directory entry list is repeated as many times as necessary.
*
* Directories are sorted, and can contain a directory index to speed up
* file lookup. Directory indexes store one entry per metablock, each entry
* storing the index/filename mapping to the first directory header
* in each metadata block. Directories are sorted in alphabetical order,
* and at lookup the index is scanned linearly looking for the first filename
* alphabetically larger than the filename being looked up. At this point the
* location of the metadata block the filename is in has been found.
* The general idea of the index is ensure only one metadata block needs to be
* decompressed to do a lookup irrespective of the length of the directory.
* This scheme has the advantage that it doesn't require extra memory overhead
* and doesn't require much extra storage on disk.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/dcache.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Lookup name in the directory index, returning the location of the metadata
* block containing it, and the directory index this represents.
*
* If we get an error reading the index then return the part of the index
* (if any) we have managed to read - the index isn't essential, just
* quicker.
*/
static int get_dir_index_using_name(struct super_block *sb,
u64 *next_block, int *next_offset, u64 index_start,
int index_offset, int i_count, const char *name,
int len)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int i, size, length = 0, err;
struct squashfs_dir_index *index;
char *str;
TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
index = kmalloc(sizeof(*index) + SQUASHFS_NAME_LEN * 2 + 2, GFP_KERNEL);
if (index == NULL) {
ERROR("Failed to allocate squashfs_dir_index\n");
goto out;
}
str = &index->name[SQUASHFS_NAME_LEN + 1];
strncpy(str, name, len);
str[len] = '\0';
for (i = 0; i < i_count; i++) {
err = squashfs_read_metadata(sb, index, &index_start,
&index_offset, sizeof(*index));
if (err < 0)
break;
size = le32_to_cpu(index->size) + 1;
err = squashfs_read_metadata(sb, index->name, &index_start,
&index_offset, size);
if (err < 0)
break;
index->name[size] = '\0';
if (strcmp(index->name, str) > 0)
break;
length = le32_to_cpu(index->index);
*next_block = le32_to_cpu(index->start_block) +
msblk->directory_table;
}
*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
kfree(index);
out:
/*
* Return index (f_pos) of the looked up metadata block. Translate
* from internal f_pos to external f_pos which is offset by 3 because
* we invent "." and ".." entries which are not actually stored in the
* directory.
*/
return length + 3;
}
static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
const unsigned char *name = dentry->d_name.name;
int len = dentry->d_name.len;
struct inode *inode = NULL;
struct squashfs_sb_info *msblk = dir->i_sb->s_fs_info;
struct squashfs_dir_header dirh;
struct squashfs_dir_entry *dire;
u64 block = squashfs_i(dir)->start + msblk->directory_table;
int offset = squashfs_i(dir)->offset;
int err, length = 0, dir_count, size;
TRACE("Entered squashfs_lookup [%llx:%x]\n", block, offset);
dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
if (dire == NULL) {
ERROR("Failed to allocate squashfs_dir_entry\n");
return ERR_PTR(-ENOMEM);
}
if (len > SQUASHFS_NAME_LEN) {
err = -ENAMETOOLONG;
goto failed;
}
length = get_dir_index_using_name(dir->i_sb, &block, &offset,
squashfs_i(dir)->dir_idx_start,
squashfs_i(dir)->dir_idx_offset,
squashfs_i(dir)->dir_idx_cnt, name, len);
while (length < i_size_read(dir)) {
/*
* Read directory header.
*/
err = squashfs_read_metadata(dir->i_sb, &dirh, &block,
&offset, sizeof(dirh));
if (err < 0)
goto read_failure;
length += sizeof(dirh);
dir_count = le32_to_cpu(dirh.count) + 1;
while (dir_count--) {
/*
* Read directory entry.
*/
err = squashfs_read_metadata(dir->i_sb, dire, &block,
&offset, sizeof(*dire));
if (err < 0)
goto read_failure;
size = le16_to_cpu(dire->size) + 1;
err = squashfs_read_metadata(dir->i_sb, dire->name,
&block, &offset, size);
if (err < 0)
goto read_failure;
length += sizeof(*dire) + size;
if (name[0] < dire->name[0])
goto exit_lookup;
if (len == size && !strncmp(name, dire->name, len)) {
unsigned int blk, off, ino_num;
long long ino;
blk = le32_to_cpu(dirh.start_block);
off = le16_to_cpu(dire->offset);
ino_num = le32_to_cpu(dirh.inode_number) +
(short) le16_to_cpu(dire->inode_number);
ino = SQUASHFS_MKINODE(blk, off);
TRACE("calling squashfs_iget for directory "
"entry %s, inode %x:%x, %d\n", name,
blk, off, ino_num);
inode = squashfs_iget(dir->i_sb, ino, ino_num);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
goto failed;
}
goto exit_lookup;
}
}
}
exit_lookup:
kfree(dire);
if (inode)
return d_splice_alias(inode, dentry);
d_add(dentry, inode);
return ERR_PTR(0);
read_failure:
ERROR("Unable to read directory block [%llx:%x]\n",
squashfs_i(dir)->start + msblk->directory_table,
squashfs_i(dir)->offset);
failed:
kfree(dire);
return ERR_PTR(err);
}
const struct inode_operations squashfs_dir_inode_ops = {
.lookup = squashfs_lookup
};

View File

@@ -0,0 +1,90 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs.h
*/
#define TRACE(s, args...) pr_debug("SQUASHFS: "s, ## args)
#define ERROR(s, args...) pr_err("SQUASHFS error: "s, ## args)
#define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args)
static inline struct squashfs_inode_info *squashfs_i(struct inode *inode)
{
return list_entry(inode, struct squashfs_inode_info, vfs_inode);
}
/* block.c */
extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *,
int);
/* cache.c */
extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
extern void squashfs_cache_delete(struct squashfs_cache *);
extern struct squashfs_cache_entry *squashfs_cache_get(struct super_block *,
struct squashfs_cache *, u64, int);
extern void squashfs_cache_put(struct squashfs_cache_entry *);
extern int squashfs_copy_data(void *, struct squashfs_cache_entry *, int, int);
extern int squashfs_read_metadata(struct super_block *, void *, u64 *,
int *, int);
extern struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *,
u64, int);
extern struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *,
u64, int);
extern int squashfs_read_table(struct super_block *, void *, u64, int);
/* export.c */
extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64,
unsigned int);
/* fragment.c */
extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *);
extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
u64, unsigned int);
/* id.c */
extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
extern __le64 *squashfs_read_id_index_table(struct super_block *, u64,
unsigned short);
/* inode.c */
extern struct inode *squashfs_iget(struct super_block *, long long,
unsigned int);
extern int squashfs_read_inode(struct inode *, long long);
/*
* Inodes and files operations
*/
/* dir.c */
extern const struct file_operations squashfs_dir_ops;
/* export.c */
extern const struct export_operations squashfs_export_ops;
/* file.c */
extern const struct address_space_operations squashfs_aops;
/* namei.c */
extern const struct inode_operations squashfs_dir_inode_ops;
/* symlink.c */
extern const struct address_space_operations squashfs_symlink_aops;

View File

@@ -0,0 +1,381 @@
#ifndef SQUASHFS_FS
#define SQUASHFS_FS
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs.h
*/
#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#define SQUASHFS_MAJOR 4
#define SQUASHFS_MINOR 0
#define SQUASHFS_MAGIC 0x73717368
#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_BLK (-1LL)
/* 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_BIT(flag, bit) ((flag >> bit) & 1)
#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOI)
#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOD)
#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOF)
#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_FRAG)
#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_ALWAYS_FRAG)
#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_DUPLICATE)
#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
SQUASHFS_EXPORT)
/* 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
/* Flag whether block is compressed or uncompressed, bit is set if block is
* uncompressed */
#define SQUASHFS_COMPRESSED_BIT (1 << 15)
#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
(B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK)
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
/*
* Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block
*/
#define SQUASHFS_INODE_BLK(A) ((unsigned int) ((A) >> 16))
#define SQUASHFS_INODE_OFFSET(A) ((unsigned int) ((A) & 0xffff))
#define SQUASHFS_MKINODE(A, B) ((long long)(((long long) (A)\
<< 16) + (B)))
/* Translate between VFS mode and squashfs mode */
#define SQUASHFS_MODE(A) ((A) & 0xfff)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES(A) \
((A) * sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
sizeof(u64))
/* inode lookup table defines */
#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(u64))
#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
sizeof(u64))
/* uid/gid lookup table defines */
#define SQUASHFS_ID_BYTES(A) ((A) * sizeof(unsigned int))
#define SQUASHFS_ID_BLOCK(A) (SQUASHFS_ID_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_OFFSET(A) (SQUASHFS_ID_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCKS(A) ((SQUASHFS_ID_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\
sizeof(u64))
/* cached data constants for filesystem */
#define SQUASHFS_CACHED_BLKS 8
#define SQUASHFS_MAX_FILE_SIZE_LOG 64
#define SQUASHFS_MAX_FILE_SIZE (1LL << \
(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
#define SQUASHFS_MARKER_BYTE 0xff
/* meta index cache */
#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
#define SQUASHFS_META_ENTRIES 127
#define SQUASHFS_META_SLOTS 8
struct meta_entry {
u64 data_block;
unsigned int index_block;
unsigned short offset;
unsigned short pad;
};
struct meta_index {
unsigned int inode_number;
unsigned int offset;
unsigned short entries;
unsigned short skip;
unsigned short locked;
unsigned short pad;
struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
};
/*
* definitions for structures on disk
*/
#define ZLIB_COMPRESSION 1
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_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;
unsigned char name[0];
};
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_dev_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
__le32 rdev;
};
struct squashfs_symlink_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
__le32 symlink_size;
char symlink[0];
};
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;
__le16 block_list[0];
};
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;
__le16 block_list[0];
};
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_index index[0];
};
union squashfs_inode {
struct squashfs_base_inode base;
struct squashfs_dev_inode dev;
struct squashfs_symlink_inode symlink;
struct squashfs_reg_inode reg;
struct squashfs_lreg_inode lreg;
struct squashfs_dir_inode dir;
struct squashfs_ldir_inode ldir;
struct squashfs_ipc_inode ipc;
};
struct squashfs_dir_entry {
__le16 offset;
__le16 inode_number;
__le16 type;
__le16 size;
char name[0];
};
struct squashfs_dir_header {
__le32 count;
__le32 start_block;
__le32 inode_number;
};
struct squashfs_fragment_entry {
__le64 start_block;
__le32 size;
unsigned int unused;
};
#endif

View File

@@ -0,0 +1,45 @@
#ifndef SQUASHFS_FS_I
#define SQUASHFS_FS_I
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_i.h
*/
struct squashfs_inode_info {
u64 start;
int offset;
union {
struct {
u64 fragment_block;
int fragment_size;
int fragment_offset;
u64 block_list_start;
};
struct {
u64 dir_idx_start;
int dir_idx_offset;
int dir_idx_cnt;
int parent;
};
};
struct inode vfs_inode;
};
#endif

View File

@@ -0,0 +1,76 @@
#ifndef SQUASHFS_FS_SB
#define SQUASHFS_FS_SB
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_sb.h
*/
#include "squashfs_fs.h"
struct squashfs_cache {
char *name;
int entries;
int next_blk;
int num_waiters;
int unused;
int block_size;
int pages;
spinlock_t lock;
wait_queue_head_t wait_queue;
struct squashfs_cache_entry *entry;
};
struct squashfs_cache_entry {
u64 block;
int length;
int refcount;
u64 next_index;
int pending;
int error;
int num_waiters;
wait_queue_head_t wait_queue;
struct squashfs_cache *cache;
void **data;
};
struct squashfs_sb_info {
int devblksize;
int devblksize_log2;
struct squashfs_cache *block_cache;
struct squashfs_cache *fragment_cache;
struct squashfs_cache *read_page;
int next_meta_index;
__le64 *id_table;
__le64 *fragment_index;
unsigned int *fragment_index_2;
struct mutex read_data_mutex;
struct mutex meta_index_mutex;
struct meta_index *meta_index;
z_stream stream;
__le64 *inode_lookup_table;
u64 inode_table;
u64 directory_table;
unsigned int block_size;
unsigned short block_log;
long long bytes_used;
unsigned int inodes;
};
#endif

View File

@@ -0,0 +1,440 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* super.c
*/
/*
* This file implements code to read the superblock, read and initialise
* in-memory structures at mount time, and all the VFS glue code to register
* the filesystem.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
static struct file_system_type squashfs_fs_type;
static struct super_operations squashfs_super_ops;
static int supported_squashfs_filesystem(short major, short minor, short comp)
{
if (major < SQUASHFS_MAJOR) {
ERROR("Major/Minor mismatch, older Squashfs %d.%d "
"filesystems are unsupported\n", major, minor);
return -EINVAL;
} else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) {
ERROR("Major/Minor mismatch, trying to mount newer "
"%d.%d filesystem\n", major, minor);
ERROR("Please update your kernel\n");
return -EINVAL;
}
if (comp != ZLIB_COMPRESSION)
return -EINVAL;
return 0;
}
static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct squashfs_sb_info *msblk;
struct squashfs_super_block *sblk = NULL;
char b[BDEVNAME_SIZE];
struct inode *root;
long long root_inode;
unsigned short flags;
unsigned int fragments;
u64 lookup_table_start;
int err;
TRACE("Entered squashfs_fill_superblock\n");
sb->s_fs_info = kzalloc(sizeof(*msblk), GFP_KERNEL);
if (sb->s_fs_info == NULL) {
ERROR("Failed to allocate squashfs_sb_info\n");
return -ENOMEM;
}
msblk = sb->s_fs_info;
msblk->stream.workspace = kmalloc(zlib_inflate_workspacesize(),
GFP_KERNEL);
if (msblk->stream.workspace == NULL) {
ERROR("Failed to allocate zlib workspace\n");
goto failure;
}
sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
if (sblk == NULL) {
ERROR("Failed to allocate squashfs_super_block\n");
goto failure;
}
msblk->devblksize = sb_min_blocksize(sb, BLOCK_SIZE);
msblk->devblksize_log2 = ffz(~msblk->devblksize);
mutex_init(&msblk->read_data_mutex);
mutex_init(&msblk->meta_index_mutex);
/*
* msblk->bytes_used is checked in squashfs_read_table to ensure reads
* are not beyond filesystem end. But as we're using
* squashfs_read_table here to read the superblock (including the value
* of bytes_used) we need to set it to an initial sensible dummy value
*/
msblk->bytes_used = sizeof(*sblk);
err = squashfs_read_table(sb, sblk, SQUASHFS_START, sizeof(*sblk));
if (err < 0) {
ERROR("unable to read squashfs_super_block\n");
goto failed_mount;
}
/* Check it is a SQUASHFS superblock */
sb->s_magic = le32_to_cpu(sblk->s_magic);
if (sb->s_magic != SQUASHFS_MAGIC) {
if (!silent)
ERROR("Can't find a SQUASHFS superblock on %s\n",
bdevname(sb->s_bdev, b));
err = -EINVAL;
goto failed_mount;
}
/* Check the MAJOR & MINOR versions and compression type */
err = supported_squashfs_filesystem(le16_to_cpu(sblk->s_major),
le16_to_cpu(sblk->s_minor),
le16_to_cpu(sblk->compression));
if (err < 0)
goto failed_mount;
err = -EINVAL;
/*
* Check if there's xattrs in the filesystem. These are not
* supported in this version, so warn that they will be ignored.
*/
if (le64_to_cpu(sblk->xattr_table_start) != SQUASHFS_INVALID_BLK)
ERROR("Xattrs in filesystem, these will be ignored\n");
/* Check the filesystem does not extend beyond the end of the
block device */
msblk->bytes_used = le64_to_cpu(sblk->bytes_used);
if (msblk->bytes_used < 0 || msblk->bytes_used >
i_size_read(sb->s_bdev->bd_inode))
goto failed_mount;
/* Check block size for sanity */
msblk->block_size = le32_to_cpu(sblk->block_size);
if (msblk->block_size > SQUASHFS_FILE_MAX_SIZE)
goto failed_mount;
msblk->block_log = le16_to_cpu(sblk->block_log);
if (msblk->block_log > SQUASHFS_FILE_MAX_LOG)
goto failed_mount;
/* Check the root inode for sanity */
root_inode = le64_to_cpu(sblk->root_inode);
if (SQUASHFS_INODE_OFFSET(root_inode) > SQUASHFS_METADATA_SIZE)
goto failed_mount;
msblk->inode_table = le64_to_cpu(sblk->inode_table_start);
msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
msblk->inodes = le32_to_cpu(sblk->inodes);
flags = le16_to_cpu(sblk->flags);
TRACE("Found valid superblock on %s\n", bdevname(sb->s_bdev, b));
TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags)
? "un" : "");
TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(flags)
? "un" : "");
TRACE("Filesystem size %lld bytes\n", msblk->bytes_used);
TRACE("Block size %d\n", msblk->block_size);
TRACE("Number of inodes %d\n", msblk->inodes);
TRACE("Number of fragments %d\n", le32_to_cpu(sblk->fragments));
TRACE("Number of ids %d\n", le16_to_cpu(sblk->no_ids));
TRACE("sblk->inode_table_start %llx\n", msblk->inode_table);
TRACE("sblk->directory_table_start %llx\n", msblk->directory_table);
TRACE("sblk->fragment_table_start %llx\n",
(u64) le64_to_cpu(sblk->fragment_table_start));
TRACE("sblk->id_table_start %llx\n",
(u64) le64_to_cpu(sblk->id_table_start));
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_flags |= MS_RDONLY;
sb->s_op = &squashfs_super_ops;
err = -ENOMEM;
msblk->block_cache = squashfs_cache_init("metadata",
SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE);
if (msblk->block_cache == NULL)
goto failed_mount;
/* Allocate read_page block */
msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size);
if (msblk->read_page == NULL) {
ERROR("Failed to allocate read_page block\n");
goto failed_mount;
}
/* Allocate and read id index table */
msblk->id_table = squashfs_read_id_index_table(sb,
le64_to_cpu(sblk->id_table_start), le16_to_cpu(sblk->no_ids));
if (IS_ERR(msblk->id_table)) {
err = PTR_ERR(msblk->id_table);
msblk->id_table = NULL;
goto failed_mount;
}
fragments = le32_to_cpu(sblk->fragments);
if (fragments == 0)
goto allocate_lookup_table;
msblk->fragment_cache = squashfs_cache_init("fragment",
SQUASHFS_CACHED_FRAGMENTS, msblk->block_size);
if (msblk->fragment_cache == NULL) {
err = -ENOMEM;
goto failed_mount;
}
/* Allocate and read fragment index table */
msblk->fragment_index = squashfs_read_fragment_index_table(sb,
le64_to_cpu(sblk->fragment_table_start), fragments);
if (IS_ERR(msblk->fragment_index)) {
err = PTR_ERR(msblk->fragment_index);
msblk->fragment_index = NULL;
goto failed_mount;
}
allocate_lookup_table:
lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
if (lookup_table_start == SQUASHFS_INVALID_BLK)
goto allocate_root;
/* Allocate and read inode lookup table */
msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
lookup_table_start, msblk->inodes);
if (IS_ERR(msblk->inode_lookup_table)) {
err = PTR_ERR(msblk->inode_lookup_table);
msblk->inode_lookup_table = NULL;
goto failed_mount;
}
sb->s_export_op = &squashfs_export_ops;
allocate_root:
root = new_inode(sb);
if (!root) {
err = -ENOMEM;
goto failed_mount;
}
err = squashfs_read_inode(root, root_inode);
if (err) {
iget_failed(root);
goto failed_mount;
}
insert_inode_hash(root);
sb->s_root = d_alloc_root(root);
if (sb->s_root == NULL) {
ERROR("Root inode create failed\n");
err = -ENOMEM;
iput(root);
goto failed_mount;
}
TRACE("Leaving squashfs_fill_super\n");
kfree(sblk);
return 0;
failed_mount:
squashfs_cache_delete(msblk->block_cache);
squashfs_cache_delete(msblk->fragment_cache);
squashfs_cache_delete(msblk->read_page);
kfree(msblk->inode_lookup_table);
kfree(msblk->fragment_index);
kfree(msblk->id_table);
kfree(msblk->stream.workspace);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
kfree(sblk);
return err;
failure:
kfree(msblk->stream.workspace);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
return -ENOMEM;
}
static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
TRACE("Entered squashfs_statfs\n");
buf->f_type = SQUASHFS_MAGIC;
buf->f_bsize = msblk->block_size;
buf->f_blocks = ((msblk->bytes_used - 1) >> msblk->block_log) + 1;
buf->f_bfree = buf->f_bavail = 0;
buf->f_files = msblk->inodes;
buf->f_ffree = 0;
buf->f_namelen = SQUASHFS_NAME_LEN;
return 0;
}
static int squashfs_remount(struct super_block *sb, int *flags, char *data)
{
*flags |= MS_RDONLY;
return 0;
}
static void squashfs_put_super(struct super_block *sb)
{
if (sb->s_fs_info) {
struct squashfs_sb_info *sbi = sb->s_fs_info;
squashfs_cache_delete(sbi->block_cache);
squashfs_cache_delete(sbi->fragment_cache);
squashfs_cache_delete(sbi->read_page);
kfree(sbi->id_table);
kfree(sbi->fragment_index);
kfree(sbi->meta_index);
kfree(sbi->stream.workspace);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
}
}
static int squashfs_get_sb(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data,
struct vfsmount *mnt)
{
return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
mnt);
}
static struct kmem_cache *squashfs_inode_cachep;
static void init_once(void *foo)
{
struct squashfs_inode_info *ei = foo;
inode_init_once(&ei->vfs_inode);
}
static int __init init_inodecache(void)
{
squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
sizeof(struct squashfs_inode_info), 0,
SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, init_once);
return squashfs_inode_cachep ? 0 : -ENOMEM;
}
static void destroy_inodecache(void)
{
kmem_cache_destroy(squashfs_inode_cachep);
}
static int __init init_squashfs_fs(void)
{
int err = init_inodecache();
if (err)
return err;
err = register_filesystem(&squashfs_fs_type);
if (err) {
destroy_inodecache();
return err;
}
printk(KERN_INFO "squashfs: version 4.0 (2009/01/03) "
"Phillip Lougher\n");
return 0;
}
static void __exit exit_squashfs_fs(void)
{
unregister_filesystem(&squashfs_fs_type);
destroy_inodecache();
}
static struct inode *squashfs_alloc_inode(struct super_block *sb)
{
struct squashfs_inode_info *ei =
kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
return ei ? &ei->vfs_inode : NULL;
}
static void squashfs_destroy_inode(struct inode *inode)
{
kmem_cache_free(squashfs_inode_cachep, squashfs_i(inode));
}
static struct file_system_type squashfs_fs_type = {
.owner = THIS_MODULE,
.name = "squashfs",
.get_sb = squashfs_get_sb,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV
};
static struct super_operations squashfs_super_ops = {
.alloc_inode = squashfs_alloc_inode,
.destroy_inode = squashfs_destroy_inode,
.statfs = squashfs_statfs,
.put_super = squashfs_put_super,
.remount_fs = squashfs_remount
};
module_init(init_squashfs_fs);
module_exit(exit_squashfs_fs);
MODULE_DESCRIPTION("squashfs 4.0, a compressed read-only filesystem");
MODULE_AUTHOR("Phillip Lougher <phillip@lougher.demon.co.uk>");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,118 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* symlink.c
*/
/*
* This file implements code to handle symbolic links.
*
* The data contents of symbolic links are stored inside the symbolic
* link inode within the inode table. This allows the normally small symbolic
* link to be compressed as part of the inode table, achieving much greater
* compression than if the symbolic link was compressed individually.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
static int squashfs_symlink_readpage(struct file *file, struct page *page)
{
struct inode *inode = page->mapping->host;
struct super_block *sb = inode->i_sb;
struct squashfs_sb_info *msblk = sb->s_fs_info;
int index = page->index << PAGE_CACHE_SHIFT;
u64 block = squashfs_i(inode)->start;
int offset = squashfs_i(inode)->offset;
int length = min_t(int, i_size_read(inode) - index, PAGE_CACHE_SIZE);
int bytes, copied;
void *pageaddr;
struct squashfs_cache_entry *entry;
TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
"%llx, offset %x\n", page->index, block, offset);
/*
* Skip index bytes into symlink metadata.
*/
if (index) {
bytes = squashfs_read_metadata(sb, NULL, &block, &offset,
index);
if (bytes < 0) {
ERROR("Unable to read symlink [%llx:%x]\n",
squashfs_i(inode)->start,
squashfs_i(inode)->offset);
goto error_out;
}
}
/*
* Read length bytes from symlink metadata. Squashfs_read_metadata
* is not used here because it can sleep and we want to use
* kmap_atomic to map the page. Instead call the underlying
* squashfs_cache_get routine. As length bytes may overlap metadata
* blocks, we may need to call squashfs_cache_get multiple times.
*/
for (bytes = 0; bytes < length; offset = 0, bytes += copied) {
entry = squashfs_cache_get(sb, msblk->block_cache, block, 0);
if (entry->error) {
ERROR("Unable to read symlink [%llx:%x]\n",
squashfs_i(inode)->start,
squashfs_i(inode)->offset);
squashfs_cache_put(entry);
goto error_out;
}
pageaddr = kmap_atomic(page, KM_USER0);
copied = squashfs_copy_data(pageaddr + bytes, entry, offset,
length - bytes);
if (copied == length - bytes)
memset(pageaddr + length, 0, PAGE_CACHE_SIZE - length);
else
block = entry->next_index;
kunmap_atomic(pageaddr, KM_USER0);
squashfs_cache_put(entry);
}
flush_dcache_page(page);
SetPageUptodate(page);
unlock_page(page);
return 0;
error_out:
SetPageError(page);
unlock_page(page);
return 0;
}
const struct address_space_operations squashfs_symlink_aops = {
.readpage = squashfs_symlink_readpage
};

View File

@@ -0,0 +1,380 @@
#ifndef SQUASHFS_FS
#define SQUASHFS_FS
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs.h
*/
#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#define SQUASHFS_MAJOR 4
#define SQUASHFS_MINOR 0
#define SQUASHFS_MAGIC 0x73717368
#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
/* 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_BLK (-1LL)
/* 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_BIT(flag, bit) ((flag >> bit) & 1)
#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOI)
#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOD)
#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOF)
#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_FRAG)
#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_ALWAYS_FRAG)
#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_DUPLICATE)
#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
SQUASHFS_EXPORT)
/* 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
/* Flag whether block is compressed or uncompressed, bit is set if block is
* uncompressed */
#define SQUASHFS_COMPRESSED_BIT (1 << 15)
#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
(B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK)
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
/*
* Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block
*/
#define SQUASHFS_INODE_BLK(A) ((unsigned int) ((A) >> 16))
#define SQUASHFS_INODE_OFFSET(A) ((unsigned int) ((A) & 0xffff))
#define SQUASHFS_MKINODE(A, B) ((long long)(((long long) (A)\
<< 16) + (B)))
/* Translate between VFS mode and squashfs mode */
#define SQUASHFS_MODE(A) ((A) & 0xfff)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES(A) \
((A) * sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
sizeof(long long))
/* inode lookup table defines */
#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(long long))
#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
sizeof(long long))
/* uid/gid lookup table defines */
#define SQUASHFS_ID_BYTES(A) ((A) * sizeof(unsigned int))
#define SQUASHFS_ID_BLOCK(A) (SQUASHFS_ID_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_OFFSET(A) (SQUASHFS_ID_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCKS(A) ((SQUASHFS_ID_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\
sizeof(long long))
/* cached data constants for filesystem */
#define SQUASHFS_CACHED_BLKS 8
#define SQUASHFS_MAX_FILE_SIZE_LOG 64
#define SQUASHFS_MAX_FILE_SIZE (1LL << \
(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
#define SQUASHFS_MARKER_BYTE 0xff
/* meta index cache */
#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
#define SQUASHFS_META_ENTRIES 127
#define SQUASHFS_META_SLOTS 8
struct meta_entry {
long long data_block;
unsigned int index_block;
unsigned short offset;
unsigned short pad;
};
struct meta_index {
unsigned int inode_number;
unsigned int offset;
unsigned short entries;
unsigned short skip;
unsigned short locked;
unsigned short pad;
struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
};
/*
* definitions for structures on disk
*/
#define ZLIB_COMPRESSION 1
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_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;
unsigned char name[0];
};
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_dev_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
__le32 rdev;
};
struct squashfs_symlink_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
__le32 symlink_size;
char symlink[0];
};
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;
__le16 block_list[0];
};
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;
__le16 block_list[0];
};
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_index index[0];
};
union squashfs_inode {
struct squashfs_base_inode base;
struct squashfs_dev_inode dev;
struct squashfs_symlink_inode symlink;
struct squashfs_reg_inode reg;
struct squashfs_lreg_inode lreg;
struct squashfs_dir_inode dir;
struct squashfs_ldir_inode ldir;
struct squashfs_ipc_inode ipc;
};
struct squashfs_dir_entry {
__le16 offset;
__le16 inode_number;
__le16 type;
__le16 size;
char name[0];
};
struct squashfs_dir_header {
__le32 count;
__le32 start_block;
__le32 inode_number;
};
struct squashfs_fragment_entry {
__le64 start_block;
__le32 size;
unsigned int unused;
};
#endif

View File

@@ -0,0 +1,45 @@
#ifndef SQUASHFS_FS_I
#define SQUASHFS_FS_I
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_i.h
*/
struct squashfs_inode_info {
long long start;
int offset;
union {
struct {
long long fragment_block;
int fragment_size;
int fragment_offset;
long long block_list_start;
};
struct {
long long dir_idx_start;
int dir_idx_offset;
int dir_idx_cnt;
int parent;
};
};
struct inode vfs_inode;
};
#endif

View File

@@ -0,0 +1,76 @@
#ifndef SQUASHFS_FS_SB
#define SQUASHFS_FS_SB
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_sb.h
*/
#include "squashfs_fs.h"
struct squashfs_cache_entry {
long long block;
int length;
int locked;
long long next_index;
char pending;
char error;
int waiting;
wait_queue_head_t wait_queue;
char *data;
};
struct squashfs_cache {
char *name;
int entries;
int block_size;
int next_blk;
int waiting;
int unused;
int use_vmalloc;
spinlock_t lock;
wait_queue_head_t wait_queue;
struct squashfs_cache_entry entry[0];
};
struct squashfs_sb_info {
int devblksize;
int devblksize_log2;
struct squashfs_cache *block_cache;
struct squashfs_cache *fragment_cache;
int next_meta_index;
__le64 *id_table;
__le64 *fragment_index;
unsigned int *fragment_index_2;
char *read_page;
struct mutex read_data_mutex;
struct mutex read_page_mutex;
struct mutex meta_index_mutex;
struct meta_index *meta_index;
z_stream stream;
__le64 *inode_lookup_table;
long long inode_table;
long long directory_table;
unsigned int block_size;
unsigned short block_log;
long long bytes_used;
unsigned int inodes;
};
#endif

View File

@@ -0,0 +1,365 @@
###############################################
# Compression build options #
###############################################
#
#
############# Building gzip support ###########
#
# Gzip support is by default enabled, and the compression type default
# (COMP_DEFAULT) is gzip.
#
# If you don't want/need gzip support then comment out the GZIP SUPPORT line
# below, and change COMP_DEFAULT to one of the compression types you have
# selected.
#
# Obviously, you must select at least one of the available gzip, lzma, lzo
# compression types.
#
GZIP_SUPPORT = 1
########### Building XZ support #############
#
# LZMA2 compression.
#
# XZ Utils liblzma (http://tukaani.org/xz/) is supported
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install the library and uncomment
# the XZ_SUPPORT line below.
#
XZ_SUPPORT = 1
############ Building LZO support ##############
#
# The LZO library (http://www.oberhumer.com/opensource/lzo/) is supported.
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install the library and uncomment
# the XZ_SUPPORT line below.
#
LZO_SUPPORT = 1
########### Building LZ4 support #############
#
# Yann Collet's LZ4 tools are supported
# LZ4 homepage: http://fastcompression.blogspot.com/p/lz4.html
# LZ4 source repository: http://code.google.com/p/lz4
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install and uncomment
# the LZ4_SUPPORT line below.
#
LZ4_SUPPORT = 1
########### Building ZSTD support ############
#
# The ZSTD library is supported
# ZSTD homepage: http://zstd.net
# ZSTD source repository: https://github.com/facebook/zstd
#
# Development packages (libraries and header files) should be
# supported by most modern distributions. Please refer to
# your distribution package manager.
#
# To build install the library and uncomment
# the XZ_SUPPORT line below.
#
ZSTD_SUPPORT = 1
######## Specifying default compression ########
#
# The next line specifies which compression algorithm is used by default
# in Mksquashfs. Obviously the compression algorithm must have been
# selected to be built
#
COMP_DEFAULT = gzip
###############################################
# Extended attribute (XATTRs) build options #
###############################################
#
# Building XATTR support for Mksquashfs and Unsquashfs
#
# If your C library or build/target environment doesn't support XATTRs then
# comment out the next line to build Mksquashfs and Unsquashfs without XATTR
# support
XATTR_SUPPORT = 1
# Select whether you wish xattrs to be stored by Mksquashfs and extracted
# by Unsquashfs by default. If selected users can disable xattr support by
# using the -no-xattrs option
#
# If unselected, Mksquashfs/Unsquashfs won't store and extract xattrs by
# default. Users can enable xattrs by using the -xattrs option.
XATTR_DEFAULT = 1
###############################################
# Reproducible Image options #
###############################################
#
# Select whether you wish reproducible builds by default. If selected users
# can disable reproducible builds using the not-reproducible option.
# If not selected, users can enable reproducible builds using the
# -reproducible option
REPRODUCIBLE_DEFAULT = 1
###############################################
# Obsolete options #
###############################################
########### Building LZMA support #############
#
# LZMA1 compression.
#
# LZMA1 compression is obsolete, and the newer and better XZ (LZMA2)
# compression should be used in preference.
#
# Both XZ Utils liblzma (http://tukaani.org/xz/) and LZMA SDK
# (http://www.7-zip.org/sdk.html) are supported
#
# To build using XZ Utils liblzma - install the library and uncomment
# the LZMA_XZ_SUPPORT line below.
#
# To build using the LZMA SDK (4.65 used in development, other versions may
# work) - download and unpack it, uncomment and set LZMA_DIR to unpacked source,
# and uncomment the LZMA_SUPPORT line below.
#
LZMA_XZ_SUPPORT = 1
#LZMA_SUPPORT = 1
#LZMA_DIR = ../../../../LZMA/lzma465
###############################################
# End of BUILD options section #
###############################################
INCLUDEDIR = -I.
INSTALL_DIR = /usr/local/bin
MKSQUASHFS_OBJS = mksquashfs.o read_fs.o action.o swap.o pseudo.o compressor.o \
sort.o progressbar.o read_file.o info.o restore.o process_fragments.o \
caches-queues-lists.o
UNSQUASHFS_OBJS = unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o \
unsquash-4.o unsquash-123.o unsquash-34.o swap.o compressor.o unsquashfs_info.o
CFLAGS ?= -Os
CFLAGS += $(EXTRA_CFLAGS) $(INCLUDEDIR) -D_FILE_OFFSET_BITS=64 \
-D_LARGEFILE_SOURCE -D_GNU_SOURCE -DCOMP_DEFAULT=\"$(COMP_DEFAULT)\" \
-Wall
LIBS = -lpthread
ifeq ($(GZIP_SUPPORT),1)
CFLAGS += -DGZIP_SUPPORT
MKSQUASHFS_OBJS += gzip_wrapper.o
UNSQUASHFS_OBJS += gzip_wrapper.o
LIBS += $(VTZLIB)
COMPRESSORS += gzip
endif
ifeq ($(LZMA_SUPPORT),1)
LZMA_OBJS = $(LZMA_DIR)/C/Alloc.o $(LZMA_DIR)/C/LzFind.o \
$(LZMA_DIR)/C/LzmaDec.o $(LZMA_DIR)/C/LzmaEnc.o $(LZMA_DIR)/C/LzmaLib.o
INCLUDEDIR += -I$(LZMA_DIR)/C
CFLAGS += -DLZMA_SUPPORT
MKSQUASHFS_OBJS += lzma_wrapper.o $(LZMA_OBJS)
UNSQUASHFS_OBJS += lzma_wrapper.o $(LZMA_OBJS)
COMPRESSORS += lzma
endif
ifeq ($(LZMA_XZ_SUPPORT),1)
CFLAGS += -DLZMA_SUPPORT
MKSQUASHFS_OBJS += lzma_xz_wrapper.o
UNSQUASHFS_OBJS += lzma_xz_wrapper.o
LIBS +=
COMPRESSORS += lzma
endif
ifeq ($(XZ_SUPPORT),1)
CFLAGS += -DXZ_SUPPORT -I$(LZMA_LIBDIR)/include
MKSQUASHFS_OBJS += xz_wrapper.o
UNSQUASHFS_OBJS += xz_wrapper.o
LIBS += $(LZMA_LIBDIR)/lib/liblzma.a
COMPRESSORS += xz
endif
ifeq ($(LZO_SUPPORT),1)
CFLAGS += -DLZO_SUPPORT -I $(LZO_LIBDIR)/include
MKSQUASHFS_OBJS += lzo_wrapper.o
UNSQUASHFS_OBJS += lzo_wrapper.o
LIBS += $(LZO_LIBDIR)/lib/liblzo2.a
COMPRESSORS += lzo
endif
ifeq ($(LZ4_SUPPORT),1)
CFLAGS += -DLZ4_SUPPORT -I $(LZ4_LIBDIR)/include
MKSQUASHFS_OBJS += lz4_wrapper.o
UNSQUASHFS_OBJS += lz4_wrapper.o
LIBS += $(LZ4_LIBDIR)/lib/liblz4.a
COMPRESSORS += lz4
endif
ifeq ($(ZSTD_SUPPORT),1)
CFLAGS += -DZSTD_SUPPORT -I $(ZSTD_LIBDIR)/include
MKSQUASHFS_OBJS += zstd_wrapper.o
UNSQUASHFS_OBJS += zstd_wrapper.o
LIBS += $(ZSTD_LIBDIR)/lib/libzstd.a
COMPRESSORS += zstd
endif
ifeq ($(XATTR_SUPPORT),1)
ifeq ($(XATTR_DEFAULT),1)
CFLAGS += -DXATTR_SUPPORT -DXATTR_DEFAULT
else
CFLAGS += -DXATTR_SUPPORT
endif
MKSQUASHFS_OBJS += xattr.o read_xattrs.o
UNSQUASHFS_OBJS += read_xattrs.o unsquashfs_xattr.o
endif
ifeq ($(REPRODUCIBLE_DEFAULT),1)
CFLAGS += -DREPRODUCIBLE_DEFAULT
endif
#
# If LZMA_SUPPORT is specified then LZMA_DIR must be specified too
#
ifeq ($(LZMA_SUPPORT),1)
ifndef LZMA_DIR
$(error "LZMA_SUPPORT requires LZMA_DIR to be also defined")
endif
endif
#
# Both LZMA_XZ_SUPPORT and LZMA_SUPPORT cannot be specified
#
ifeq ($(LZMA_XZ_SUPPORT),1)
ifeq ($(LZMA_SUPPORT),1)
$(error "Both LZMA_XZ_SUPPORT and LZMA_SUPPORT cannot be specified")
endif
endif
#
# At least one compressor must have been selected
#
ifndef COMPRESSORS
$(error "No compressor selected! Select one or more of GZIP, LZMA, XZ, LZO, \
LZ4 or ZSTD!")
endif
#
# COMP_DEFAULT should be defined
#
ifndef COMP_DEFAULT
$(error "COMP_DEFAULT must be set to a compressor!")
endif
#
# COMP_DEFAULT must be a selected compressor
#
ifeq (, $(findstring $(COMP_DEFAULT), $(COMPRESSORS)))
$(error "COMP_DEFAULT is set to ${COMP_DEFAULT}, which isn't selected to be \
built!")
endif
.PHONY: all
all: mksquashfs unsquashfs
mksquashfs: $(MKSQUASHFS_OBJS)
$(CC) $(LDFLAGS) $(EXTRA_LDFLAGS) $(MKSQUASHFS_OBJS) $(LIBS) -o $@
mksquashfs.o: Makefile mksquashfs.c squashfs_fs.h squashfs_swap.h mksquashfs.h \
sort.h pseudo.h compressor.h xattr.h action.h error.h progressbar.h \
info.h caches-queues-lists.h read_fs.h restore.h process_fragments.h
read_fs.o: read_fs.c squashfs_fs.h squashfs_swap.h compressor.h xattr.h \
error.h mksquashfs.h
sort.o: sort.c squashfs_fs.h mksquashfs.h sort.h error.h progressbar.h
swap.o: swap.c
pseudo.o: pseudo.c pseudo.h error.h progressbar.h
compressor.o: Makefile compressor.c compressor.h squashfs_fs.h
xattr.o: xattr.c squashfs_fs.h squashfs_swap.h mksquashfs.h xattr.h error.h \
progressbar.h
read_xattrs.o: read_xattrs.c squashfs_fs.h squashfs_swap.h xattr.h error.h
action.o: action.c squashfs_fs.h mksquashfs.h action.h error.h
progressbar.o: progressbar.c error.h
read_file.o: read_file.c error.h
info.o: info.c squashfs_fs.h mksquashfs.h error.h progressbar.h \
caches-queues-lists.h
restore.o: restore.c caches-queues-lists.h squashfs_fs.h mksquashfs.h error.h \
progressbar.h info.h
process_fragments.o: process_fragments.c process_fragments.h
caches-queues-lists.o: caches-queues-lists.c error.h caches-queues-lists.h
gzip_wrapper.o: gzip_wrapper.c squashfs_fs.h gzip_wrapper.h compressor.h
lzma_wrapper.o: lzma_wrapper.c compressor.h squashfs_fs.h
lzma_xz_wrapper.o: lzma_xz_wrapper.c compressor.h squashfs_fs.h
lzo_wrapper.o: lzo_wrapper.c squashfs_fs.h lzo_wrapper.h compressor.h
lz4_wrapper.o: lz4_wrapper.c squashfs_fs.h lz4_wrapper.h compressor.h
xz_wrapper.o: xz_wrapper.c squashfs_fs.h xz_wrapper.h compressor.h
unsquashfs: $(UNSQUASHFS_OBJS)
$(CC) $(LDFLAGS) $(EXTRA_LDFLAGS) $(UNSQUASHFS_OBJS) $(LIBS) -o $@
unsquashfs.o: unsquashfs.h unsquashfs.c squashfs_fs.h squashfs_swap.h \
squashfs_compat.h xattr.h read_fs.h compressor.h
unsquash-1.o: unsquashfs.h unsquash-1.c squashfs_fs.h squashfs_compat.h
unsquash-2.o: unsquashfs.h unsquash-2.c squashfs_fs.h squashfs_compat.h
unsquash-3.o: unsquashfs.h unsquash-3.c squashfs_fs.h squashfs_compat.h
unsquash-4.o: unsquashfs.h unsquash-4.c squashfs_fs.h squashfs_swap.h \
read_fs.h
unsquash-123.o: unsquashfs.h unsquash-123.c squashfs_fs.h squashfs_compat.h
unsquash-34.o: unsquashfs.h unsquash-34.c
unsquashfs_xattr.o: unsquashfs_xattr.c unsquashfs.h squashfs_fs.h xattr.h
unsquashfs_info.o: unsquashfs.h squashfs_fs.h
.PHONY: clean
clean:
-rm -f *.o mksquashfs unsquashfs
.PHONY: install
install: mksquashfs unsquashfs
mkdir -p $(INSTALL_DIR)
cp mksquashfs $(INSTALL_DIR)
cp unsquashfs $(INSTALL_DIR)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,328 @@
#ifndef ACTION_H
#define ACTION_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2011, 2012, 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* action.h
*/
/*
* Lexical analyser definitions
*/
#define TOK_OPEN_BRACKET 0
#define TOK_CLOSE_BRACKET 1
#define TOK_AND 2
#define TOK_OR 3
#define TOK_NOT 4
#define TOK_COMMA 5
#define TOK_AT 6
#define TOK_WHITE_SPACE 7
#define TOK_STRING 8
#define TOK_EOF 9
#define TOK_TO_STR(OP, S) ({ \
char *s; \
switch(OP) { \
case TOK_EOF: \
s = "EOF"; \
break; \
case TOK_STRING: \
s = S; \
break; \
default: \
s = token_table[OP].string; \
break; \
} \
s; \
})
struct token_entry {
char *string;
int token;
int size;
};
/*
* Expression parser definitions
*/
#define OP_TYPE 0
#define ATOM_TYPE 1
#define UNARY_TYPE 2
#define SYNTAX_ERROR(S, ARGS...) { \
char *src = strdup(source); \
src[cur_ptr - source] = '\0'; \
fprintf(stderr, "Failed to parse action \"%s\"\n", source); \
fprintf(stderr, "Syntax error: "S, ##ARGS); \
fprintf(stderr, "Got here \"%s\"\n", src); \
free(src); \
}
#define TEST_SYNTAX_ERROR(TEST, ARG, S, ARGS...) { \
char *src = strdup(source); \
src[cur_ptr - source] = '\0'; \
fprintf(stderr, "Failed to parse action \"%s\"\n", source); \
fprintf(stderr, "Syntax error in \"%s()\", arg %d: "S, TEST->name, \
ARG, ##ARGS); \
fprintf(stderr, "Got here \"%s\"\n", src); \
free(src); \
}
struct expr;
struct expr_op {
struct expr *lhs;
struct expr *rhs;
int op;
};
struct atom {
struct test_entry *test;
int args;
char **argv;
void *data;
};
struct unary_op {
struct expr *expr;
int op;
};
struct expr {
int type;
union {
struct atom atom;
struct expr_op expr_op;
struct unary_op unary_op;
};
};
/*
* Test operation definitions
*/
#define NUM_EQ 1
#define NUM_LESS 2
#define NUM_GREATER 3
struct test_number_arg {
long long size;
int range;
};
struct test_range_args {
long long start;
long long end;
};
struct action;
struct action_data;
struct test_entry {
char *name;
int args;
int (*fn)(struct atom *, struct action_data *);
int (*parse_args)(struct test_entry *, struct atom *);
int exclude_ok;
int handle_logging;
};
/*
* Type test specific definitions
*/
struct type_entry {
int value;
char type;
};
/*
* Action definitions
*/
#define FRAGMENT_ACTION 0
#define EXCLUDE_ACTION 1
#define FRAGMENTS_ACTION 2
#define NO_FRAGMENTS_ACTION 3
#define ALWAYS_FRAGS_ACTION 4
#define NO_ALWAYS_FRAGS_ACTION 5
#define COMPRESSED_ACTION 6
#define UNCOMPRESSED_ACTION 7
#define UID_ACTION 8
#define GID_ACTION 9
#define GUID_ACTION 10
#define MODE_ACTION 11
#define EMPTY_ACTION 12
#define MOVE_ACTION 13
#define PRUNE_ACTION 14
#define NOOP_ACTION 15
/*
* Define what file types each action operates over
*/
#define ACTION_DIR 1
#define ACTION_REG 2
#define ACTION_ALL_LNK 3
#define ACTION_ALL 4
#define ACTION_LNK 5
/*
* Action logging requested, specified by the various
* -action, -true-action, -false-action and -verbose-action
* options
*/
#define ACTION_LOG_NONE 0
#define ACTION_LOG_TRUE 1
#define ACTION_LOG_FALSE 2
#define ACTION_LOG_VERBOSE ACTION_LOG_TRUE | ACTION_LOG_FALSE
struct action_entry {
char *name;
int type;
int args;
int file_types;
int (*parse_args)(struct action_entry *, int, char **, void **);
void (*run_action)(struct action *, struct dir_ent *);
};
struct action_data {
int depth;
char *name;
char *pathname;
char *subpath;
struct stat *buf;
struct dir_ent *dir_ent;
struct dir_info *root;
};
struct action {
int type;
struct action_entry *action;
int args;
char **argv;
struct expr *expr;
void *data;
int verbose;
};
/*
* Uid/gid action specific definitions
*/
struct uid_info {
uid_t uid;
};
struct gid_info {
gid_t gid;
};
struct guid_info {
uid_t uid;
gid_t gid;
};
/*
* Mode action specific definitions
*/
#define ACTION_MODE_SET 0
#define ACTION_MODE_ADD 1
#define ACTION_MODE_REM 2
#define ACTION_MODE_OCT 3
struct mode_data {
struct mode_data *next;
int operation;
int mode;
unsigned int mask;
char X;
};
/*
* Empty action specific definitions
*/
#define EMPTY_ALL 0
#define EMPTY_SOURCE 1
#define EMPTY_EXCLUDED 2
struct empty_data {
int val;
};
/*
* Move action specific definitions
*/
#define ACTION_MOVE_RENAME 1
#define ACTION_MOVE_MOVE 2
struct move_ent {
int ops;
struct dir_ent *dir_ent;
char *name;
struct dir_info *dest;
struct move_ent *next;
};
/*
* Perm test function specific definitions
*/
#define PERM_ALL 1
#define PERM_ANY 2
#define PERM_EXACT 3
struct perm_data {
int op;
int mode;
};
/*
* External function definitions
*/
extern int parse_action(char *, int verbose);
extern void dump_actions();
extern void *eval_frag_actions(struct dir_info *, struct dir_ent *);
extern void *get_frag_action(void *);
extern int eval_exclude_actions(char *, char *, char *, struct stat *, int,
struct dir_ent *);
extern void eval_actions(struct dir_info *, struct dir_ent *);
extern int eval_empty_actions(struct dir_info *, struct dir_ent *dir_ent);
extern void eval_move_actions(struct dir_info *, struct dir_ent *);
extern int eval_prune_actions(struct dir_info *, struct dir_ent *);
extern void do_move_actions();
extern int read_bytes(int, void *, int);
extern int actions();
extern int move_actions();
extern int empty_actions();
extern int read_action_file(char *, int);
extern int exclude_actions();
extern int prune_actions();
#endif

View File

@@ -0,0 +1,35 @@
#!/bin/bash
export LZMA_LIBDIR=$PWD/../../LIB/LZMA
export LZ4_LIBDIR=$PWD/../../LIB/LZ4
export ZSTD_LIBDIR=$PWD/../../LIB/ZSTD
export LZO_LIBDIR=$PWD/../../LIB/LZO
if [ -e /lib64/libz.a ]; then
export VTZLIB=/lib64/libz.a
elif [ -e /lib/libz.a ]; then
export VTZLIB=/lib/libz.a
elif [ -e /usr/lib/libz.a ]; then
export VTZLIB=/usr/lib/libz.a
fi
rm -f unsquashfs
make clean
make -e unsquashfs
if [ -e unsquashfs ]; then
strip --strip-all unsquashfs
echo -e "\n========== SUCCESS ============\n"
else
echo -e "\n========== FAILED ============\n"
fi
if uname -a | egrep -q 'x86_64|amd64'; then
name=unsquashfs_64
else
name=unsquashfs_32
fi
rm -f ../../$name
cp -a unsquashfs ../../$name

View File

@@ -0,0 +1,642 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* caches-queues-lists.c
*/
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "error.h"
#include "caches-queues-lists.h"
extern int add_overflow(int, int);
extern int multiply_overflow(int, int);
#define TRUE 1
#define FALSE 0
struct queue *queue_init(int size)
{
struct queue *queue = malloc(sizeof(struct queue));
if(queue == NULL)
MEM_ERROR();
if(add_overflow(size, 1) ||
multiply_overflow(size + 1, sizeof(void *)))
BAD_ERROR("Size too large in queue_init\n");
queue->data = malloc(sizeof(void *) * (size + 1));
if(queue->data == NULL)
MEM_ERROR();
queue->size = size + 1;
queue->readp = queue->writep = 0;
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->empty, NULL);
pthread_cond_init(&queue->full, NULL);
return queue;
}
void queue_put(struct queue *queue, void *data)
{
int nextp;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
while((nextp = (queue->writep + 1) % queue->size) == queue->readp)
pthread_cond_wait(&queue->full, &queue->mutex);
queue->data[queue->writep] = data;
queue->writep = nextp;
pthread_cond_signal(&queue->empty);
pthread_cleanup_pop(1);
}
void *queue_get(struct queue *queue)
{
void *data;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
while(queue->readp == queue->writep)
pthread_cond_wait(&queue->empty, &queue->mutex);
data = queue->data[queue->readp];
queue->readp = (queue->readp + 1) % queue->size;
pthread_cond_signal(&queue->full);
pthread_cleanup_pop(1);
return data;
}
int queue_empty(struct queue *queue)
{
int empty;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
empty = queue->readp == queue->writep;
pthread_cleanup_pop(1);
return empty;
}
void queue_flush(struct queue *queue)
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
queue->readp = queue->writep;
pthread_cleanup_pop(1);
}
void dump_queue(struct queue *queue)
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
printf("\tMax size %d, size %d%s\n", queue->size - 1,
queue->readp <= queue->writep ? queue->writep - queue->readp :
queue->size - queue->readp + queue->writep,
queue->readp == queue->writep ? " (EMPTY)" :
((queue->writep + 1) % queue->size) == queue->readp ?
" (FULL)" : "");
pthread_cleanup_pop(1);
}
/* define seq queue hash tables */
#define CALCULATE_SEQ_HASH(N) CALCULATE_HASH(N)
/* Called with the seq queue mutex held */
INSERT_HASH_TABLE(seq, struct seq_queue, CALCULATE_SEQ_HASH, sequence, seq)
/* Called with the cache mutex held */
REMOVE_HASH_TABLE(seq, struct seq_queue, CALCULATE_SEQ_HASH, sequence, seq);
struct seq_queue *seq_queue_init()
{
struct seq_queue *queue = malloc(sizeof(struct seq_queue));
if(queue == NULL)
MEM_ERROR();
memset(queue, 0, sizeof(struct seq_queue));
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->wait, NULL);
return queue;
}
void seq_queue_put(struct seq_queue *queue, struct file_buffer *entry)
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
insert_seq_hash_table(queue, entry);
if(entry->fragment)
queue->fragment_count ++;
else
queue->block_count ++;
if(entry->sequence == queue->sequence)
pthread_cond_signal(&queue->wait);
pthread_cleanup_pop(1);
}
struct file_buffer *seq_queue_get(struct seq_queue *queue)
{
/*
* Return next buffer from queue in sequence order (queue->sequence). If
* found return it, otherwise wait for it to arrive.
*/
int hash = CALCULATE_SEQ_HASH(queue->sequence);
struct file_buffer *entry;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
while(1) {
for(entry = queue->hash_table[hash]; entry;
entry = entry->seq_next)
if(entry->sequence == queue->sequence)
break;
if(entry) {
/*
* found the buffer in the queue, decrement the
* appropriate count, and remove from hash list
*/
if(entry->fragment)
queue->fragment_count --;
else
queue->block_count --;
remove_seq_hash_table(queue, entry);
queue->sequence ++;
break;
}
/* entry not found, wait for it to arrive */
pthread_cond_wait(&queue->wait, &queue->mutex);
}
pthread_cleanup_pop(1);
return entry;
}
void seq_queue_flush(struct seq_queue *queue)
{
int i;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
for(i = 0; i < HASH_SIZE; i++)
queue->hash_table[i] = NULL;
queue->fragment_count = queue->block_count = 0;
pthread_cleanup_pop(1);
}
void dump_seq_queue(struct seq_queue *queue, int fragment_queue)
{
int size;
pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
pthread_mutex_lock(&queue->mutex);
size = fragment_queue ? queue->fragment_count : queue->block_count;
printf("\tMax size unlimited, size %d%s\n", size,
size == 0 ? " (EMPTY)" : "");
pthread_cleanup_pop(1);
}
/* define cache hash tables */
#define CALCULATE_CACHE_HASH(N) CALCULATE_HASH(llabs(N))
/* Called with the cache mutex held */
INSERT_HASH_TABLE(cache, struct cache, CALCULATE_CACHE_HASH, index, hash)
/* Called with the cache mutex held */
REMOVE_HASH_TABLE(cache, struct cache, CALCULATE_CACHE_HASH, index, hash);
/* define cache free list */
/* Called with the cache mutex held */
INSERT_LIST(free, struct file_buffer)
/* Called with the cache mutex held */
REMOVE_LIST(free, struct file_buffer)
struct cache *cache_init(int buffer_size, int max_buffers, int noshrink_lookup,
int first_freelist)
{
struct cache *cache = malloc(sizeof(struct cache));
if(cache == NULL)
MEM_ERROR();
cache->max_buffers = max_buffers;
cache->buffer_size = buffer_size;
cache->count = 0;
cache->used = 0;
cache->free_list = NULL;
/*
* The cache will grow up to max_buffers in size in response to
* an increase in readhead/number of buffers in flight. But
* once the outstanding buffers gets returned, we can either elect
* to shrink the cache, or to put the freed blocks onto a free list.
*
* For the caches where we want to do lookup (fragment/writer),
* a don't shrink policy is best, for the reader cache it
* makes no sense to keep buffers around longer than necessary as
* we don't do any lookup on those blocks.
*/
cache->noshrink_lookup = noshrink_lookup;
/*
* The default use freelist before growing cache policy behaves
* poorly with appending - with many duplicates the caches
* do not grow due to the fact that large queues of outstanding
* fragments/writer blocks do not occur, leading to small caches
* and un-uncessary performance loss to frequent cache
* replacement in the small caches. Therefore with appending
* change the policy to grow the caches before reusing blocks
* from the freelist
*/
cache->first_freelist = first_freelist;
memset(cache->hash_table, 0, sizeof(struct file_buffer *) * 65536);
pthread_mutex_init(&cache->mutex, NULL);
pthread_cond_init(&cache->wait_for_free, NULL);
pthread_cond_init(&cache->wait_for_unlock, NULL);
return cache;
}
struct file_buffer *cache_lookup(struct cache *cache, long long index)
{
/* Lookup block in the cache, if found return with usage count
* incremented, if not found return NULL */
int hash = CALCULATE_CACHE_HASH(index);
struct file_buffer *entry;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next)
if(entry->index == index)
break;
if(entry) {
/* found the block in the cache, increment used count and
* if necessary remove from free list so it won't disappear
*/
if(entry->used == 0) {
remove_free_list(&cache->free_list, entry);
cache->used ++;
}
entry->used ++;
}
pthread_cleanup_pop(1);
return entry;
}
static struct file_buffer *cache_freelist(struct cache *cache)
{
struct file_buffer *entry = cache->free_list;
remove_free_list(&cache->free_list, entry);
/* a block on the free_list is hashed */
remove_cache_hash_table(cache, entry);
cache->used ++;
return entry;
}
static struct file_buffer *cache_alloc(struct cache *cache)
{
struct file_buffer *entry = malloc(sizeof(struct file_buffer) +
cache->buffer_size);
if(entry == NULL)
MEM_ERROR();
entry->cache = cache;
entry->free_prev = entry->free_next = NULL;
cache->count ++;
return entry;
}
static struct file_buffer *_cache_get(struct cache *cache, long long index,
int hash)
{
/* Get a free block out of the cache indexed on index. */
struct file_buffer *entry = NULL;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
while(1) {
if(cache->noshrink_lookup) {
/* first try to get a block from the free list */
if(cache->first_freelist && cache->free_list)
entry = cache_freelist(cache);
else if(cache->count < cache->max_buffers) {
entry = cache_alloc(cache);
cache->used ++;
} else if(!cache->first_freelist && cache->free_list)
entry = cache_freelist(cache);
} else { /* shrinking non-lookup cache */
if(cache->count < cache->max_buffers) {
entry = cache_alloc(cache);
if(cache->count > cache->max_count)
cache->max_count = cache->count;
}
}
if(entry)
break;
/* wait for a block */
pthread_cond_wait(&cache->wait_for_free, &cache->mutex);
}
/* initialise block and if hash is set insert into the hash table */
entry->used = 1;
entry->locked = FALSE;
entry->wait_on_unlock = FALSE;
entry->error = FALSE;
if(hash) {
entry->index = index;
insert_cache_hash_table(cache, entry);
}
pthread_cleanup_pop(1);
return entry;
}
struct file_buffer *cache_get(struct cache *cache, long long index)
{
return _cache_get(cache, index, 1);
}
struct file_buffer *cache_get_nohash(struct cache *cache)
{
return _cache_get(cache, 0, 0);
}
void cache_hash(struct file_buffer *entry, long long index)
{
struct cache *cache = entry->cache;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
entry->index = index;
insert_cache_hash_table(cache, entry);
pthread_cleanup_pop(1);
}
void cache_block_put(struct file_buffer *entry)
{
struct cache *cache;
/*
* Finished with this cache entry, once the usage count reaches zero it
* can be reused.
*
* If noshrink_lookup is set, put the block onto the free list.
* As blocks remain accessible via the hash table they can be found
* getting a new lease of life before they are reused.
*
* if noshrink_lookup is not set then shrink the cache.
*/
if(entry == NULL)
return;
cache = entry->cache;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
entry->used --;
if(entry->used == 0) {
if(cache->noshrink_lookup) {
insert_free_list(&cache->free_list, entry);
cache->used --;
} else {
free(entry);
cache->count --;
}
/* One or more threads may be waiting on this block */
pthread_cond_signal(&cache->wait_for_free);
}
pthread_cleanup_pop(1);
}
void dump_cache(struct cache *cache)
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
if(cache->noshrink_lookup)
printf("\tMax buffers %d, Current size %d, Used %d, %s\n",
cache->max_buffers, cache->count, cache->used,
cache->free_list ? "Free buffers" : "No free buffers");
else
printf("\tMax buffers %d, Current size %d, Maximum historical "
"size %d\n", cache->max_buffers, cache->count,
cache->max_count);
pthread_cleanup_pop(1);
}
struct file_buffer *cache_get_nowait(struct cache *cache, long long index)
{
struct file_buffer *entry = NULL;
/*
* block doesn't exist, create it, but return it with the
* locked flag set, so nothing tries to use it while it doesn't
* contain data.
*
* If there's no space in the cache then return NULL.
*/
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
/* first try to get a block from the free list */
if(cache->first_freelist && cache->free_list)
entry = cache_freelist(cache);
else if(cache->count < cache->max_buffers) {
entry = cache_alloc(cache);
cache->used ++;
} else if(!cache->first_freelist && cache->free_list)
entry = cache_freelist(cache);
if(entry) {
/* initialise block and insert into the hash table */
entry->used = 1;
entry->locked = TRUE;
entry->wait_on_unlock = FALSE;
entry->error = FALSE;
entry->index = index;
insert_cache_hash_table(cache, entry);
}
pthread_cleanup_pop(1);
return entry;
}
struct file_buffer *cache_lookup_nowait(struct cache *cache, long long index,
char *locked)
{
/*
* Lookup block in the cache, if found return it with the locked flag
* indicating whether it is currently locked. In both cases increment
* the used count.
*
* If it doesn't exist in the cache return NULL;
*/
int hash = CALCULATE_CACHE_HASH(index);
struct file_buffer *entry;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
/* first check if the entry already exists */
for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next)
if(entry->index == index)
break;
if(entry) {
if(entry->used == 0) {
remove_free_list(&cache->free_list, entry);
cache->used ++;
}
entry->used ++;
*locked = entry->locked;
}
pthread_cleanup_pop(1);
return entry;
}
void cache_wait_unlock(struct file_buffer *buffer)
{
struct cache *cache = buffer->cache;
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
while(buffer->locked) {
/*
* another thread is filling this in, wait until it
* becomes unlocked. Used has been incremented to ensure it
* doesn't get reused. By definition a block can't be
* locked and unused, and so we don't need to worry
* about it being on the freelist now, but, it may
* become unused when unlocked unless used is
* incremented
*/
buffer->wait_on_unlock = TRUE;
pthread_cond_wait(&cache->wait_for_unlock, &cache->mutex);
}
pthread_cleanup_pop(1);
}
void cache_unlock(struct file_buffer *entry)
{
struct cache *cache = entry->cache;
/*
* Unlock this locked cache entry. If anything is waiting for this
* to become unlocked, wake it up.
*/
pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
pthread_mutex_lock(&cache->mutex);
entry->locked = FALSE;
if(entry->wait_on_unlock) {
entry->wait_on_unlock = FALSE;
pthread_cond_broadcast(&cache->wait_for_unlock);
}
pthread_cleanup_pop(1);
}

View File

@@ -0,0 +1,199 @@
#ifndef CACHES_QUEUES_LISTS_H
#define CACHES_QUEUES_LISTS_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* caches-queues-lists.h
*/
#define INSERT_LIST(NAME, TYPE) \
void insert_##NAME##_list(TYPE **list, TYPE *entry) { \
if(*list) { \
entry->NAME##_next = *list; \
entry->NAME##_prev = (*list)->NAME##_prev; \
(*list)->NAME##_prev->NAME##_next = entry; \
(*list)->NAME##_prev = entry; \
} else { \
*list = entry; \
entry->NAME##_prev = entry->NAME##_next = entry; \
} \
}
#define REMOVE_LIST(NAME, TYPE) \
void remove_##NAME##_list(TYPE **list, TYPE *entry) { \
if(entry->NAME##_prev == entry && entry->NAME##_next == entry) { \
/* only this entry in the list */ \
*list = NULL; \
} else if(entry->NAME##_prev != NULL && entry->NAME##_next != NULL) { \
/* more than one entry in the list */ \
entry->NAME##_next->NAME##_prev = entry->NAME##_prev; \
entry->NAME##_prev->NAME##_next = entry->NAME##_next; \
if(*list == entry) \
*list = entry->NAME##_next; \
} \
entry->NAME##_prev = entry->NAME##_next = NULL; \
}
#define INSERT_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \
void insert_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \
{ \
int hash = HASH_FUNCTION(entry->FIELD); \
\
entry->LINK##_next = container->hash_table[hash]; \
container->hash_table[hash] = entry; \
entry->LINK##_prev = NULL; \
if(entry->LINK##_next) \
entry->LINK##_next->LINK##_prev = entry; \
}
#define REMOVE_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \
void remove_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \
{ \
if(entry->LINK##_prev) \
entry->LINK##_prev->LINK##_next = entry->LINK##_next; \
else \
container->hash_table[HASH_FUNCTION(entry->FIELD)] = \
entry->LINK##_next; \
if(entry->LINK##_next) \
entry->LINK##_next->LINK##_prev = entry->LINK##_prev; \
\
entry->LINK##_prev = entry->LINK##_next = NULL; \
}
#define HASH_SIZE 65536
#define CALCULATE_HASH(n) ((n) & 0xffff)
/* struct describing a cache entry passed between threads */
struct file_buffer {
long long index;
long long sequence;
long long file_size;
union {
long long block;
unsigned short checksum;
};
struct cache *cache;
union {
struct file_info *dupl_start;
struct file_buffer *hash_next;
};
union {
int duplicate;
struct file_buffer *hash_prev;
};
union {
struct {
struct file_buffer *free_next;
struct file_buffer *free_prev;
};
struct {
struct file_buffer *seq_next;
struct file_buffer *seq_prev;
};
};
int size;
int c_byte;
char used;
char fragment;
char error;
char locked;
char wait_on_unlock;
char noD;
char data[0] __attribute__((aligned));
};
/* struct describing queues used to pass data between threads */
struct queue {
int size;
int readp;
int writep;
pthread_mutex_t mutex;
pthread_cond_t empty;
pthread_cond_t full;
void **data;
};
/*
* struct describing seq_queues used to pass data between the read
* thread and the deflate and main threads
*/
struct seq_queue {
int fragment_count;
int block_count;
long long sequence;
struct file_buffer *hash_table[HASH_SIZE];
pthread_mutex_t mutex;
pthread_cond_t wait;
};
/* Cache status struct. Caches are used to keep
track of memory buffers passed between different threads */
struct cache {
int max_buffers;
int count;
int buffer_size;
int noshrink_lookup;
int first_freelist;
union {
int used;
int max_count;
};
pthread_mutex_t mutex;
pthread_cond_t wait_for_free;
pthread_cond_t wait_for_unlock;
struct file_buffer *free_list;
struct file_buffer *hash_table[HASH_SIZE];
};
extern struct queue *queue_init(int);
extern void queue_put(struct queue *, void *);
extern void *queue_get(struct queue *);
extern int queue_empty(struct queue *);
extern void queue_flush(struct queue *);
extern void dump_queue(struct queue *);
extern struct seq_queue *seq_queue_init();
extern void seq_queue_put(struct seq_queue *, struct file_buffer *);
extern void dump_seq_queue(struct seq_queue *, int);
extern struct file_buffer *seq_queue_get(struct seq_queue *);
extern void seq_queue_flush(struct seq_queue *);
extern struct cache *cache_init(int, int, int, int);
extern struct file_buffer *cache_lookup(struct cache *, long long);
extern struct file_buffer *cache_get(struct cache *, long long);
extern struct file_buffer *cache_get_nohash(struct cache *);
extern void cache_hash(struct file_buffer *, long long);
extern void cache_block_put(struct file_buffer *);
extern void dump_cache(struct cache *);
extern struct file_buffer *cache_get_nowait(struct cache *, long long);
extern struct file_buffer *cache_lookup_nowait(struct cache *, long long,
char *);
extern void cache_wait_unlock(struct file_buffer *);
extern void cache_unlock(struct file_buffer *);
extern int first_freelist;
#endif

View File

@@ -0,0 +1,145 @@
/*
*
* Copyright (c) 2009, 2010, 2011
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* compressor.c
*/
#include <stdio.h>
#include <string.h>
#include "compressor.h"
#include "squashfs_fs.h"
#ifndef GZIP_SUPPORT
static struct compressor gzip_comp_ops = {
ZLIB_COMPRESSION, "gzip"
};
#else
extern struct compressor gzip_comp_ops;
#endif
#ifndef LZMA_SUPPORT
static struct compressor lzma_comp_ops = {
LZMA_COMPRESSION, "lzma"
};
#else
extern struct compressor lzma_comp_ops;
#endif
#ifndef LZO_SUPPORT
static struct compressor lzo_comp_ops = {
LZO_COMPRESSION, "lzo"
};
#else
extern struct compressor lzo_comp_ops;
#endif
#ifndef LZ4_SUPPORT
static struct compressor lz4_comp_ops = {
LZ4_COMPRESSION, "lz4"
};
#else
extern struct compressor lz4_comp_ops;
#endif
#ifndef XZ_SUPPORT
static struct compressor xz_comp_ops = {
XZ_COMPRESSION, "xz"
};
#else
extern struct compressor xz_comp_ops;
#endif
#ifndef ZSTD_SUPPORT
static struct compressor zstd_comp_ops = {
ZSTD_COMPRESSION, "zstd"
};
#else
extern struct compressor zstd_comp_ops;
#endif
static struct compressor unknown_comp_ops = {
0, "unknown"
};
struct compressor *compressor[] = {
&gzip_comp_ops,
&lzma_comp_ops,
&lzo_comp_ops,
&lz4_comp_ops,
&xz_comp_ops,
&zstd_comp_ops,
&unknown_comp_ops
};
struct compressor *lookup_compressor(char *name)
{
int i;
for(i = 0; compressor[i]->id; i++)
if(strcmp(compressor[i]->name, name) == 0)
break;
return compressor[i];
}
struct compressor *lookup_compressor_id(int id)
{
int i;
for(i = 0; compressor[i]->id; i++)
if(id == compressor[i]->id)
break;
return compressor[i];
}
void display_compressors(char *indent, char *def_comp)
{
int i;
for(i = 0; compressor[i]->id; i++)
if(compressor[i]->supported)
fprintf(stderr, "%s\t%s%s\n", indent,
compressor[i]->name,
strcmp(compressor[i]->name, def_comp) == 0 ?
" (default)" : "");
}
void display_compressor_usage(char *def_comp)
{
int i;
for(i = 0; compressor[i]->id; i++)
if(compressor[i]->supported) {
char *str = strcmp(compressor[i]->name, def_comp) == 0 ?
" (default)" : "";
if(compressor[i]->usage) {
fprintf(stderr, "\t%s%s\n",
compressor[i]->name, str);
compressor[i]->usage();
} else
fprintf(stderr, "\t%s (no options)%s\n",
compressor[i]->name, str);
}
}

View File

@@ -0,0 +1,124 @@
#ifndef COMPRESSOR_H
#define COMPRESSOR_H
/*
*
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* compressor.h
*/
struct compressor {
int id;
char *name;
int supported;
int (*init)(void **, int, int);
int (*compress)(void *, void *, void *, int, int, int *);
int (*uncompress)(void *, void *, int, int, int *);
int (*options)(char **, int);
int (*options_post)(int);
void *(*dump_options)(int, int *);
int (*extract_options)(int, void *, int);
int (*check_options)(int, void *, int);
void (*display_options)(void *, int);
void (*usage)();
};
extern struct compressor *lookup_compressor(char *);
extern struct compressor *lookup_compressor_id(int);
extern void display_compressors(char *, char *);
extern void display_compressor_usage(char *);
static inline int compressor_init(struct compressor *comp, void **stream,
int block_size, int datablock)
{
if(comp->init == NULL)
return 0;
return comp->init(stream, block_size, datablock);
}
static inline int compressor_compress(struct compressor *comp, void *strm,
void *dest, void *src, int size, int block_size, int *error)
{
return comp->compress(strm, dest, src, size, block_size, error);
}
static inline int compressor_uncompress(struct compressor *comp, void *dest,
void *src, int size, int block_size, int *error)
{
return comp->uncompress(dest, src, size, block_size, error);
}
/*
* For the following functions please see the lzo, lz4 or xz
* compressors for commented examples of how they are used.
*/
static inline int compressor_options(struct compressor *comp, char *argv[],
int argc)
{
if(comp->options == NULL)
return -1;
return comp->options(argv, argc);
}
static inline int compressor_options_post(struct compressor *comp, int block_size)
{
if(comp->options_post == NULL)
return 0;
return comp->options_post(block_size);
}
static inline void *compressor_dump_options(struct compressor *comp,
int block_size, int *size)
{
if(comp->dump_options == NULL)
return NULL;
return comp->dump_options(block_size, size);
}
static inline int compressor_extract_options(struct compressor *comp,
int block_size, void *buffer, int size)
{
if(comp->extract_options == NULL)
return size ? -1 : 0;
return comp->extract_options(block_size, buffer, size);
}
static inline int compressor_check_options(struct compressor *comp,
int block_size, void *buffer, int size)
{
if(comp->check_options == NULL)
return 0;
return comp->check_options(block_size, buffer, size);
}
static inline void compressor_display_options(struct compressor *comp,
void *buffer, int size)
{
if(comp->display_options != NULL)
comp->display_options(buffer, size);
}
#endif

View File

@@ -0,0 +1,106 @@
#ifndef ERROR_H
#define ERROR_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2012, 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* error.h
*/
extern int exit_on_error;
extern void prep_exit();
extern void progressbar_error(char *fmt, ...);
extern void progressbar_info(char *fmt, ...);
#ifdef SQUASHFS_TRACE
#define TRACE(s, args...) \
do { \
progressbar_info("squashfs: "s, ## args);\
} while(0)
#else
#define TRACE(s, args...)
#endif
#define INFO(s, args...) \
do {\
if(!silent)\
progressbar_info(s, ## args);\
} while(0)
#define ERROR(s, args...) \
do {\
progressbar_error(s, ## args); \
} while(0)
#define ERROR_START(s, args...) \
do { \
disable_progress_bar(); \
fprintf(stderr, s, ## args); \
} while(0)
#define ERROR_EXIT(s, args...) \
do {\
if (exit_on_error) { \
fprintf(stderr, "\n"); \
EXIT_MKSQUASHFS(); \
} else { \
fprintf(stderr, s, ## args); \
enable_progress_bar(); \
} \
} while(0)
#define EXIT_MKSQUASHFS() \
do {\
prep_exit();\
exit(1);\
} while(0)
#define BAD_ERROR(s, args...) \
do {\
progressbar_error("FATAL ERROR:" s, ##args); \
EXIT_MKSQUASHFS();\
} while(0)
#define EXIT_UNSQUASH(s, args...) BAD_ERROR(s, ##args)
#define EXIT_UNSQUASH_IGNORE(s, args...) \
do {\
if(ignore_errors) \
ERROR(s, ##args); \
else \
BAD_ERROR(s, ##args); \
} while(0)
#define EXIT_UNSQUASH_STRICT(s, args...) \
do {\
if(!strict_errors) \
ERROR(s, ##args); \
else \
BAD_ERROR(s, ##args); \
} while(0)
#define MEM_ERROR() \
do {\
progressbar_error("FATAL ERROR: Out of memory (%s)\n", \
__func__); \
EXIT_MKSQUASHFS();\
} while(0)
#endif

View File

@@ -0,0 +1,32 @@
#ifndef FNMATCH_COMPAT
#define FNMATCH_COMPAT
/*
* Squashfs
*
* Copyright (c) 2015
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* fnmatch_compat.h
*/
#include <fnmatch.h>
#ifndef FNM_EXTMATCH
#define FNM_EXTMATCH 0
#endif
#endif

View File

@@ -0,0 +1,500 @@
/*
* Copyright (c) 2009, 2010, 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* gzip_wrapper.c
*
* Support for ZLIB compression http://www.zlib.net
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <zlib.h>
#include "squashfs_fs.h"
#include "gzip_wrapper.h"
#include "compressor.h"
static struct strategy strategy[] = {
{ "default", Z_DEFAULT_STRATEGY, 0 },
{ "filtered", Z_FILTERED, 0 },
{ "huffman_only", Z_HUFFMAN_ONLY, 0 },
{ "run_length_encoded", Z_RLE, 0 },
{ "fixed", Z_FIXED, 0 },
{ NULL, 0, 0 }
};
static int strategy_count = 0;
/* default compression level */
static int compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL;
/* default window size */
static int window_size = GZIP_DEFAULT_WINDOW_SIZE;
/*
* This function is called by the options parsing code in mksquashfs.c
* to parse any -X compressor option.
*
* This function returns:
* >=0 (number of additional args parsed) on success
* -1 if the option was unrecognised, or
* -2 if the option was recognised, but otherwise bad in
* some way (e.g. invalid parameter)
*
* Note: this function sets internal compressor state, but does not
* pass back the results of the parsing other than success/failure.
* The gzip_dump_options() function is called later to get the options in
* a format suitable for writing to the filesystem.
*/
static int gzip_options(char *argv[], int argc)
{
if(strcmp(argv[0], "-Xcompression-level") == 0) {
if(argc < 2) {
fprintf(stderr, "gzip: -Xcompression-level missing "
"compression level\n");
fprintf(stderr, "gzip: -Xcompression-level it "
"should be 1 >= n <= 9\n");
goto failed;
}
compression_level = atoi(argv[1]);
if(compression_level < 1 || compression_level > 9) {
fprintf(stderr, "gzip: -Xcompression-level invalid, it "
"should be 1 >= n <= 9\n");
goto failed;
}
return 1;
} else if(strcmp(argv[0], "-Xwindow-size") == 0) {
if(argc < 2) {
fprintf(stderr, "gzip: -Xwindow-size missing window "
" size\n");
fprintf(stderr, "gzip: -Xwindow-size <window-size>\n");
goto failed;
}
window_size = atoi(argv[1]);
if(window_size < 8 || window_size > 15) {
fprintf(stderr, "gzip: -Xwindow-size invalid, it "
"should be 8 >= n <= 15\n");
goto failed;
}
return 1;
} else if(strcmp(argv[0], "-Xstrategy") == 0) {
char *name;
int i;
if(argc < 2) {
fprintf(stderr, "gzip: -Xstrategy missing "
"strategies\n");
goto failed;
}
name = argv[1];
while(name[0] != '\0') {
for(i = 0; strategy[i].name; i++) {
int n = strlen(strategy[i].name);
if((strncmp(name, strategy[i].name, n) == 0) &&
(name[n] == '\0' ||
name[n] == ',')) {
if(strategy[i].selected == 0) {
strategy[i].selected = 1;
strategy_count++;
}
name += name[n] == ',' ? n + 1 : n;
break;
}
}
if(strategy[i].name == NULL) {
fprintf(stderr, "gzip: -Xstrategy unrecognised "
"strategy\n");
goto failed;
}
}
return 1;
}
return -1;
failed:
return -2;
}
/*
* This function is called after all options have been parsed.
* It is used to do post-processing on the compressor options using
* values that were not expected to be known at option parse time.
*
* This function returns 0 on successful post processing, or
* -1 on error
*/
static int gzip_options_post(int block_size)
{
if(strategy_count == 1 && strategy[0].selected) {
strategy_count = 0;
strategy[0].selected = 0;
}
return 0;
}
/*
* This function is called by mksquashfs to dump the parsed
* compressor options in a format suitable for writing to the
* compressor options field in the filesystem (stored immediately
* after the superblock).
*
* This function returns a pointer to the compression options structure
* to be stored (and the size), or NULL if there are no compression
* options
*
*/
static void *gzip_dump_options(int block_size, int *size)
{
static struct gzip_comp_opts comp_opts;
int i, strategies = 0;
/*
* If default compression options of:
* compression-level: 8 and
* window-size: 15 and
* strategy_count == 0 then
* don't store a compression options structure (this is compatible
* with the legacy implementation of GZIP for Squashfs)
*/
if(compression_level == GZIP_DEFAULT_COMPRESSION_LEVEL &&
window_size == GZIP_DEFAULT_WINDOW_SIZE &&
strategy_count == 0)
return NULL;
for(i = 0; strategy[i].name; i++)
strategies |= strategy[i].selected << i;
comp_opts.compression_level = compression_level;
comp_opts.window_size = window_size;
comp_opts.strategy = strategies;
SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
*size = sizeof(comp_opts);
return &comp_opts;
}
/*
* This function is a helper specifically for the append mode of
* mksquashfs. Its purpose is to set the internal compressor state
* to the stored compressor options in the passed compressor options
* structure.
*
* In effect this function sets up the compressor options
* to the same state they were when the filesystem was originally
* generated, this is to ensure on appending, the compressor uses
* the same compression options that were used to generate the
* original filesystem.
*
* Note, even if there are no compressor options, this function is still
* called with an empty compressor structure (size == 0), to explicitly
* set the default options, this is to ensure any user supplied
* -X options on the appending mksquashfs command line are over-ridden
*
* This function returns 0 on sucessful extraction of options, and
* -1 on error
*/
static int gzip_extract_options(int block_size, void *buffer, int size)
{
struct gzip_comp_opts *comp_opts = buffer;
int i;
if(size == 0) {
/* Set default values */
compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL;
window_size = GZIP_DEFAULT_WINDOW_SIZE;
strategy_count = 0;
return 0;
}
/* we expect a comp_opts structure of sufficient size to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* Check comp_opts structure for correctness */
if(comp_opts->compression_level < 1 ||
comp_opts->compression_level > 9) {
fprintf(stderr, "gzip: bad compression level in "
"compression options structure\n");
goto failed;
}
compression_level = comp_opts->compression_level;
if(comp_opts->window_size < 8 ||
comp_opts->window_size > 15) {
fprintf(stderr, "gzip: bad window size in "
"compression options structure\n");
goto failed;
}
window_size = comp_opts->window_size;
strategy_count = 0;
for(i = 0; strategy[i].name; i++) {
if((comp_opts->strategy >> i) & 1) {
strategy[i].selected = 1;
strategy_count ++;
} else
strategy[i].selected = 0;
}
return 0;
failed:
fprintf(stderr, "gzip: error reading stored compressor options from "
"filesystem!\n");
return -1;
}
static void gzip_display_options(void *buffer, int size)
{
struct gzip_comp_opts *comp_opts = buffer;
int i, printed;
/* we expect a comp_opts structure of sufficient size to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* Check comp_opts structure for correctness */
if(comp_opts->compression_level < 1 ||
comp_opts->compression_level > 9) {
fprintf(stderr, "gzip: bad compression level in "
"compression options structure\n");
goto failed;
}
printf("\tcompression-level %d\n", comp_opts->compression_level);
if(comp_opts->window_size < 8 ||
comp_opts->window_size > 15) {
fprintf(stderr, "gzip: bad window size in "
"compression options structure\n");
goto failed;
}
printf("\twindow-size %d\n", comp_opts->window_size);
for(i = 0, printed = 0; strategy[i].name; i++) {
if((comp_opts->strategy >> i) & 1) {
if(printed)
printf(", ");
else
printf("\tStrategies selected: ");
printf("%s", strategy[i].name);
printed = 1;
}
}
if(!printed)
printf("\tStrategies selected: default\n");
else
printf("\n");
return;
failed:
fprintf(stderr, "gzip: error reading stored compressor options from "
"filesystem!\n");
}
/*
* This function is called by mksquashfs to initialise the
* compressor, before compress() is called.
*
* This function returns 0 on success, and
* -1 on error
*/
static int gzip_init(void **strm, int block_size, int datablock)
{
int i, j, res;
struct gzip_stream *stream;
if(!datablock || !strategy_count) {
stream = malloc(sizeof(*stream) + sizeof(struct gzip_strategy));
if(stream == NULL)
goto failed;
stream->strategies = 1;
stream->strategy[0].strategy = Z_DEFAULT_STRATEGY;
} else {
stream = malloc(sizeof(*stream) +
sizeof(struct gzip_strategy) * strategy_count);
if(stream == NULL)
goto failed;
memset(stream->strategy, 0, sizeof(struct gzip_strategy) *
strategy_count);
stream->strategies = strategy_count;
for(i = 0, j = 0; strategy[i].name; i++) {
if(!strategy[i].selected)
continue;
stream->strategy[j].strategy = strategy[i].strategy;
if(j) {
stream->strategy[j].buffer = malloc(block_size);
if(stream->strategy[j].buffer == NULL)
goto failed2;
}
j++;
}
}
stream->stream.zalloc = Z_NULL;
stream->stream.zfree = Z_NULL;
stream->stream.opaque = 0;
res = deflateInit2(&stream->stream, compression_level, Z_DEFLATED,
window_size, 8, stream->strategy[0].strategy);
if(res != Z_OK)
goto failed2;
*strm = stream;
return 0;
failed2:
for(i = 1; i < stream->strategies; i++)
free(stream->strategy[i].buffer);
free(stream);
failed:
return -1;
}
static int gzip_compress(void *strm, void *d, void *s, int size, int block_size,
int *error)
{
int i, res;
struct gzip_stream *stream = strm;
struct gzip_strategy *selected = NULL;
stream->strategy[0].buffer = d;
for(i = 0; i < stream->strategies; i++) {
struct gzip_strategy *strategy = &stream->strategy[i];
res = deflateReset(&stream->stream);
if(res != Z_OK)
goto failed;
stream->stream.next_in = s;
stream->stream.avail_in = size;
stream->stream.next_out = strategy->buffer;
stream->stream.avail_out = block_size;
if(stream->strategies > 1) {
res = deflateParams(&stream->stream,
compression_level, strategy->strategy);
if(res != Z_OK)
goto failed;
}
res = deflate(&stream->stream, Z_FINISH);
strategy->length = stream->stream.total_out;
if(res == Z_STREAM_END) {
if(!selected || selected->length > strategy->length)
selected = strategy;
} else if(res != Z_OK)
goto failed;
}
if(!selected)
/*
* Output buffer overflow. Return out of buffer space
*/
return 0;
if(selected->buffer != d)
memcpy(d, selected->buffer, selected->length);
return (int) selected->length;
failed:
/*
* All other errors return failure, with the compressor
* specific error code in *error
*/
*error = res;
return -1;
}
static int gzip_uncompress(void *d, void *s, int size, int outsize, int *error)
{
int res;
unsigned long bytes = outsize;
res = uncompress(d, &bytes, s, size);
if(res == Z_OK)
return (int) bytes;
else {
*error = res;
return -1;
}
}
static void gzip_usage()
{
fprintf(stderr, "\t -Xcompression-level <compression-level>\n");
fprintf(stderr, "\t\t<compression-level> should be 1 .. 9 (default "
"%d)\n", GZIP_DEFAULT_COMPRESSION_LEVEL);
fprintf(stderr, "\t -Xwindow-size <window-size>\n");
fprintf(stderr, "\t\t<window-size> should be 8 .. 15 (default "
"%d)\n", GZIP_DEFAULT_WINDOW_SIZE);
fprintf(stderr, "\t -Xstrategy strategy1,strategy2,...,strategyN\n");
fprintf(stderr, "\t\tCompress using strategy1,strategy2,...,strategyN"
" in turn\n");
fprintf(stderr, "\t\tand choose the best compression.\n");
fprintf(stderr, "\t\tAvailable strategies: default, filtered, "
"huffman_only,\n\t\trun_length_encoded and fixed\n");
}
struct compressor gzip_comp_ops = {
.init = gzip_init,
.compress = gzip_compress,
.uncompress = gzip_uncompress,
.options = gzip_options,
.options_post = gzip_options_post,
.dump_options = gzip_dump_options,
.extract_options = gzip_extract_options,
.display_options = gzip_display_options,
.usage = gzip_usage,
.id = ZLIB_COMPRESSION,
.name = "gzip",
.supported = 1
};

View File

@@ -0,0 +1,75 @@
#ifndef GZIP_WRAPPER_H
#define GZIP_WRAPPER_H
/*
* Squashfs
*
* Copyright (c) 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* gzip_wrapper.h
*
*/
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
extern unsigned int inswap_le16(unsigned short);
extern unsigned int inswap_le32(unsigned int);
#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
(s)->compression_level = inswap_le32((s)->compression_level); \
(s)->window_size = inswap_le16((s)->window_size); \
(s)->strategy = inswap_le16((s)->strategy); \
}
#else
#define SQUASHFS_INSWAP_COMP_OPTS(s)
#endif
/* Default compression */
#define GZIP_DEFAULT_COMPRESSION_LEVEL 9
#define GZIP_DEFAULT_WINDOW_SIZE 15
struct gzip_comp_opts {
int compression_level;
short window_size;
short strategy;
};
struct strategy {
char *name;
int strategy;
int selected;
};
struct gzip_strategy {
int strategy;
int length;
void *buffer;
};
struct gzip_stream {
z_stream stream;
int strategies;
struct gzip_strategy strategy[0];
};
#endif

View File

@@ -0,0 +1,191 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* info.c
*/
#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include "squashfs_fs.h"
#include "mksquashfs.h"
#include "error.h"
#include "progressbar.h"
#include "caches-queues-lists.h"
static int silent = 0;
static struct dir_ent *ent = NULL;
pthread_t info_thread;
void disable_info()
{
ent = NULL;
}
void update_info(struct dir_ent *dir_ent)
{
ent = dir_ent;
}
void print_filename()
{
struct dir_ent *dir_ent = ent;
if(dir_ent == NULL)
return;
if(dir_ent->our_dir->subpath[0] != '\0')
INFO("%s/%s\n", dir_ent->our_dir->subpath, dir_ent->name);
else
INFO("/%s\n", dir_ent->name);
}
void dump_state()
{
disable_progress_bar();
printf("Queue and Cache status dump\n");
printf("===========================\n");
printf("file buffer queue (reader thread -> deflate thread(s))\n");
dump_queue(to_deflate);
printf("uncompressed fragment queue (reader thread -> fragment"
" thread(s))\n");
dump_queue(to_process_frag);
printf("processed fragment queue (fragment thread(s) -> main"
" thread)\n");
dump_seq_queue(to_main, 1);
printf("compressed block queue (deflate thread(s) -> main thread)\n");
dump_seq_queue(to_main, 0);
printf("uncompressed packed fragment queue (main thread -> fragment"
" deflate thread(s))\n");
dump_queue(to_frag);
if(!reproducible) {
printf("locked frag queue (compressed frags waiting while multi-block"
" file is written)\n");
dump_queue(locked_fragment);
printf("compressed block queue (main & fragment deflate threads(s) ->"
" writer thread)\n");
dump_queue(to_writer);
} else {
printf("compressed fragment queue (fragment deflate threads(s) ->"
"fragment order thread)\n");
dump_seq_queue(to_order, 0);
printf("compressed block queue (main & fragment order threads ->"
" writer thread)\n");
dump_queue(to_writer);
}
printf("read cache (uncompressed blocks read by reader thread)\n");
dump_cache(reader_buffer);
printf("block write cache (compressed blocks waiting for the writer"
" thread)\n");
dump_cache(bwriter_buffer);
printf("fragment write cache (compressed fragments waiting for the"
" writer thread)\n");
dump_cache(fwriter_buffer);
printf("fragment cache (frags waiting to be compressed by fragment"
" deflate thread(s))\n");
dump_cache(fragment_buffer);
printf("fragment reserve cache (avoids pipeline stall if frag cache"
" full in dup check)\n");
dump_cache(reserve_cache);
enable_progress_bar();
}
void *info_thrd(void *arg)
{
sigset_t sigmask;
struct timespec timespec = { .tv_sec = 1, .tv_nsec = 0 };
int sig, waiting = 0;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGQUIT);
sigaddset(&sigmask, SIGHUP);
while(1) {
if(waiting)
sig = sigtimedwait(&sigmask, NULL, &timespec);
else
sig = sigwaitinfo(&sigmask, NULL);
if(sig == -1) {
switch(errno) {
case EAGAIN:
/* interval timed out */
waiting = 0;
/* FALLTHROUGH */
case EINTR:
/* if waiting, the wait will be longer, but
that's OK */
continue;
default:
BAD_ERROR("sigtimedwait/sigwaitinfo failed "
"because %s\n", strerror(errno));
}
}
if(sig == SIGQUIT && !waiting) {
print_filename();
/* set one second interval period, if ^\ received
within then, dump queue and cache status */
waiting = 1;
} else
dump_state();
}
}
void init_info()
{
pthread_create(&info_thread, NULL, info_thrd, NULL);
}

View File

@@ -0,0 +1,30 @@
#ifndef INFO_H
#define INFO_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* info.h
*/
extern void disable_info();
extern void update_info(struct dir_ent *);
extern void init_info();
#endif

View File

@@ -0,0 +1,286 @@
/*
* Copyright (c) 2013, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* lz4_wrapper.c
*
* Support for LZ4 compression http://fastcompression.blogspot.com/p/lz4.html
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <lz4.h>
#include <lz4hc.h>
#include "squashfs_fs.h"
#include "lz4_wrapper.h"
#include "compressor.h"
/* LZ4 1.7.0 introduced new functions, and since r131,
* the older functions produce deprecated warnings.
*
* There are still too many distros using older versions
* to switch to the newer functions, but, the deprecated
* functions may completely disappear. This is a mess.
*
* Support both by checking the library version and
* using shadow definitions
*/
/* Earlier (but > 1.7.0) versions don't define this */
#ifndef LZ4HC_CLEVEL_MAX
#define LZ4HC_CLEVEL_MAX 12
#endif
#if LZ4_VERSION_NUMBER >= 10700
#define COMPRESS(src, dest, size, max) LZ4_compress_default(src, dest, size, max)
#define COMPRESS_HC(src, dest, size, max) LZ4_compress_HC(src, dest, size, max, LZ4HC_CLEVEL_MAX)
#else
#define COMPRESS(src, dest, size, max) LZ4_compress_limitedOutput(src, dest, size, max)
#define COMPRESS_HC(src, dest, size, max) LZ4_compressHC_limitedOutput(src, dest, size, max)
#endif
static int hc = 0;
/*
* This function is called by the options parsing code in mksquashfs.c
* to parse any -X compressor option.
*
* This function returns:
* >=0 (number of additional args parsed) on success
* -1 if the option was unrecognised, or
* -2 if the option was recognised, but otherwise bad in
* some way (e.g. invalid parameter)
*
* Note: this function sets internal compressor state, but does not
* pass back the results of the parsing other than success/failure.
* The lz4_dump_options() function is called later to get the options in
* a format suitable for writing to the filesystem.
*/
static int lz4_options(char *argv[], int argc)
{
if(strcmp(argv[0], "-Xhc") == 0) {
hc = 1;
return 0;
}
return -1;
}
/*
* This function is called by mksquashfs to dump the parsed
* compressor options in a format suitable for writing to the
* compressor options field in the filesystem (stored immediately
* after the superblock).
*
* This function returns a pointer to the compression options structure
* to be stored (and the size), or NULL if there are no compression
* options
*
* Currently LZ4 always returns a comp_opts structure, with
* the version indicating LZ4_LEGACY stream fomat. This is to
* easily accomodate changes in the kernel code to different
* stream formats
*/
static void *lz4_dump_options(int block_size, int *size)
{
static struct lz4_comp_opts comp_opts;
comp_opts.version = LZ4_LEGACY;
comp_opts.flags = hc ? LZ4_HC : 0;
SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
*size = sizeof(comp_opts);
return &comp_opts;
}
/*
* This function is a helper specifically for the append mode of
* mksquashfs. Its purpose is to set the internal compressor state
* to the stored compressor options in the passed compressor options
* structure.
*
* In effect this function sets up the compressor options
* to the same state they were when the filesystem was originally
* generated, this is to ensure on appending, the compressor uses
* the same compression options that were used to generate the
* original filesystem.
*
* Note, even if there are no compressor options, this function is still
* called with an empty compressor structure (size == 0), to explicitly
* set the default options, this is to ensure any user supplied
* -X options on the appending mksquashfs command line are over-ridden
*
* This function returns 0 on sucessful extraction of options, and
* -1 on error
*/
static int lz4_extract_options(int block_size, void *buffer, int size)
{
struct lz4_comp_opts *comp_opts = buffer;
/* we expect a comp_opts structure to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* we expect the stream format to be LZ4_LEGACY */
if(comp_opts->version != LZ4_LEGACY) {
fprintf(stderr, "lz4: unknown LZ4 version\n");
goto failed;
}
/*
* Check compression flags, currently only LZ4_HC ("high compression")
* can be set.
*/
if(comp_opts->flags == LZ4_HC)
hc = 1;
else if(comp_opts->flags != 0) {
fprintf(stderr, "lz4: unknown LZ4 flags\n");
goto failed;
}
return 0;
failed:
fprintf(stderr, "lz4: error reading stored compressor options from "
"filesystem!\n");
return -1;
}
/*
* This function is a helper specifically for unsquashfs.
* Its purpose is to check that the compression options are
* understood by this version of LZ4.
*
* This is important for LZ4 because the format understood by the
* Linux kernel may change from the already obsolete legacy format
* currently supported.
*
* If this does happen, then this version of LZ4 will not be able to decode
* the newer format. So we need to check for this.
*
* This function returns 0 on sucessful checking of options, and
* -1 on error
*/
static int lz4_check_options(int block_size, void *buffer, int size)
{
struct lz4_comp_opts *comp_opts = buffer;
/* we expect a comp_opts structure to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* we expect the stream format to be LZ4_LEGACY */
if(comp_opts->version != LZ4_LEGACY) {
fprintf(stderr, "lz4: unknown LZ4 version\n");
goto failed;
}
return 0;
failed:
fprintf(stderr, "lz4: error reading stored compressor options from "
"filesystem!\n");
return -1;
}
static void lz4_display_options(void *buffer, int size)
{
struct lz4_comp_opts *comp_opts = buffer;
/* check passed comp opts struct is of the correct length */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* we expect the stream format to be LZ4_LEGACY */
if(comp_opts->version != LZ4_LEGACY) {
fprintf(stderr, "lz4: unknown LZ4 version\n");
goto failed;
}
/*
* Check compression flags, currently only LZ4_HC ("high compression")
* can be set.
*/
if(comp_opts->flags & ~LZ4_FLAGS_MASK) {
fprintf(stderr, "lz4: unknown LZ4 flags\n");
goto failed;
}
if(comp_opts->flags & LZ4_HC)
printf("\tHigh Compression option specified (-Xhc)\n");
return;
failed:
fprintf(stderr, "lz4: error reading stored compressor options from "
"filesystem!\n");
}
static int lz4_compress(void *strm, void *dest, void *src, int size,
int block_size, int *error)
{
return 0;
}
static int lz4_uncompress(void *dest, void *src, int size, int outsize,
int *error)
{
int res = LZ4_decompress_safe(src, dest, size, outsize);
if(res < 0) {
*error = res;
return -1;
}
return res;
}
static void lz4_usage()
{
fprintf(stderr, "\t -Xhc\n");
fprintf(stderr, "\t\tCompress using LZ4 High Compression\n");
}
struct compressor lz4_comp_ops = {
.compress = lz4_compress,
.uncompress = lz4_uncompress,
.options = lz4_options,
.dump_options = lz4_dump_options,
.extract_options = lz4_extract_options,
.check_options = lz4_check_options,
.display_options = lz4_display_options,
.usage = lz4_usage,
.id = LZ4_COMPRESSION,
.name = "lz4",
.supported = 1
};

View File

@@ -0,0 +1,61 @@
#ifndef LZ4_WRAPPER_H
#define LZ4_WRAPPER_H
/*
* Squashfs
*
* Copyright (c) 2013
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* lz4_wrapper.h
*
*/
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
extern unsigned int inswap_le32(unsigned int);
#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
(s)->version = inswap_le32((s)->version); \
(s)->flags = inswap_le32((s)->flags); \
}
#else
#define SQUASHFS_INSWAP_COMP_OPTS(s)
#endif
/*
* Define the various stream formats recognised.
* Currently omly legacy stream format is supported by the
* kernel
*/
#define LZ4_LEGACY 1
#define LZ4_FLAGS_MASK 1
/* Define the compression flags recognised. */
#define LZ4_HC 1
struct lz4_comp_opts {
int version;
int flags;
};
#endif

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2009, 2010, 2013
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* lzma_wrapper.c
*
* Support for LZMA1 compression using LZMA SDK (4.65 used in
* development, other versions may work) http://www.7-zip.org/sdk.html
*/
#include <LzmaLib.h>
#include "squashfs_fs.h"
#include "compressor.h"
#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8)
static int lzma_compress(void *strm, void *dest, void *src, int size, int block_size,
int *error)
{
return 0;
}
static int lzma_uncompress(void *dest, void *src, int size, int outsize,
int *error)
{
unsigned char *s = src;
size_t outlen, inlen = size - LZMA_HEADER_SIZE;
int res;
outlen = s[LZMA_PROPS_SIZE] |
(s[LZMA_PROPS_SIZE + 1] << 8) |
(s[LZMA_PROPS_SIZE + 2] << 16) |
(s[LZMA_PROPS_SIZE + 3] << 24);
if(outlen > outsize) {
*error = 0;
return -1;
}
res = LzmaUncompress(dest, &outlen, src + LZMA_HEADER_SIZE, &inlen, src,
LZMA_PROPS_SIZE);
if(res == SZ_OK)
return outlen;
else {
*error = res;
return -1;
}
}
struct compressor lzma_comp_ops = {
.init = NULL,
.compress = lzma_compress,
.uncompress = lzma_uncompress,
.options = NULL,
.usage = NULL,
.id = LZMA_COMPRESSION,
.name = "lzma",
.supported = 1
};

View File

@@ -0,0 +1,163 @@
/*
* Copyright (c) 2010, 2013
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* lzma_xz_wrapper.c
*
* Support for LZMA1 compression using XZ Utils liblzma http://tukaani.org/xz/
*/
#include <stdio.h>
#include <string.h>
#include <lzma.h>
#include "squashfs_fs.h"
#include "compressor.h"
#define LZMA_PROPS_SIZE 5
#define LZMA_UNCOMP_SIZE 8
#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMP_SIZE)
#define LZMA_OPTIONS 5
#define MEMLIMIT (32 * 1024 * 1024)
static int lzma_compress(void *dummy, void *dest, void *src, int size,
int block_size, int *error)
{
unsigned char *d = (unsigned char *) dest;
lzma_options_lzma opt;
lzma_stream strm = LZMA_STREAM_INIT;
int res;
lzma_lzma_preset(&opt, LZMA_OPTIONS);
opt.dict_size = block_size;
res = lzma_alone_encoder(&strm, &opt);
if(res != LZMA_OK) {
lzma_end(&strm);
goto failed;
}
strm.next_out = dest;
strm.avail_out = block_size;
strm.next_in = src;
strm.avail_in = size;
res = lzma_code(&strm, LZMA_FINISH);
lzma_end(&strm);
if(res == LZMA_STREAM_END) {
/*
* Fill in the 8 byte little endian uncompressed size field in
* the LZMA header. 8 bytes is excessively large for squashfs
* but this is the standard LZMA header and which is expected by
* the kernel code
*/
d[LZMA_PROPS_SIZE] = size & 255;
d[LZMA_PROPS_SIZE + 1] = (size >> 8) & 255;
d[LZMA_PROPS_SIZE + 2] = (size >> 16) & 255;
d[LZMA_PROPS_SIZE + 3] = (size >> 24) & 255;
d[LZMA_PROPS_SIZE + 4] = 0;
d[LZMA_PROPS_SIZE + 5] = 0;
d[LZMA_PROPS_SIZE + 6] = 0;
d[LZMA_PROPS_SIZE + 7] = 0;
return (int) strm.total_out;
}
if(res == LZMA_OK)
/*
* Output buffer overflow. Return out of buffer space
*/
return 0;
failed:
/*
* All other errors return failure, with the compressor
* specific error code in *error
*/
*error = res;
return -1;
}
static int lzma_uncompress(void *dest, void *src, int size, int outsize,
int *error)
{
lzma_stream strm = LZMA_STREAM_INIT;
int uncompressed_size = 0, res;
unsigned char lzma_header[LZMA_HEADER_SIZE];
res = lzma_alone_decoder(&strm, MEMLIMIT);
if(res != LZMA_OK) {
lzma_end(&strm);
goto failed;
}
memcpy(lzma_header, src, LZMA_HEADER_SIZE);
uncompressed_size = lzma_header[LZMA_PROPS_SIZE] |
(lzma_header[LZMA_PROPS_SIZE + 1] << 8) |
(lzma_header[LZMA_PROPS_SIZE + 2] << 16) |
(lzma_header[LZMA_PROPS_SIZE + 3] << 24);
if(uncompressed_size > outsize) {
res = 0;
goto failed;
}
memset(lzma_header + LZMA_PROPS_SIZE, 255, LZMA_UNCOMP_SIZE);
strm.next_out = dest;
strm.avail_out = outsize;
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);
goto failed;
}
strm.next_in = src + LZMA_HEADER_SIZE;
strm.avail_in = size - 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))
return uncompressed_size;
failed:
*error = res;
return -1;
}
struct compressor lzma_comp_ops = {
.init = NULL,
.compress = lzma_compress,
.uncompress = lzma_uncompress,
.options = NULL,
.usage = NULL,
.id = LZMA_COMPRESSION,
.name = "lzma",
.supported = 1
};

View File

@@ -0,0 +1,365 @@
/*
* Copyright (c) 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* lzo_wrapper.c
*
* Support for LZO compression http://www.oberhumer.com/opensource/lzo
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <lzo/lzoconf.h>
#include <lzo/lzo1x.h>
#include "squashfs_fs.h"
#include "lzo_wrapper.h"
#include "compressor.h"
static struct lzo_algorithm lzo[] = {
{ "lzo1x_1", LZO1X_1_MEM_COMPRESS, lzo1x_1_compress },
{ "lzo1x_1_11", LZO1X_1_11_MEM_COMPRESS, lzo1x_1_11_compress },
{ "lzo1x_1_12", LZO1X_1_12_MEM_COMPRESS, lzo1x_1_12_compress },
{ "lzo1x_1_15", LZO1X_1_15_MEM_COMPRESS, lzo1x_1_15_compress },
{ "lzo1x_999", LZO1X_999_MEM_COMPRESS, lzo1x_999_wrapper },
{ NULL, 0, NULL }
};
/* default LZO compression algorithm and compression level */
static int algorithm = SQUASHFS_LZO1X_999;
static int compression_level = SQUASHFS_LZO1X_999_COMP_DEFAULT;
/* user specified compression level */
static int user_comp_level = -1;
/*
* This function is called by the options parsing code in mksquashfs.c
* to parse any -X compressor option.
*
* This function returns:
* >=0 (number of additional args parsed) on success
* -1 if the option was unrecognised, or
* -2 if the option was recognised, but otherwise bad in
* some way (e.g. invalid parameter)
*
* Note: this function sets internal compressor state, but does not
* pass back the results of the parsing other than success/failure.
* The lzo_dump_options() function is called later to get the options in
* a format suitable for writing to the filesystem.
*/
static int lzo_options(char *argv[], int argc)
{
(void)argv;
(void)argc;
return 1;
}
/*
* This function is called after all options have been parsed.
* It is used to do post-processing on the compressor options using
* values that were not expected to be known at option parse time.
*
* In this case the LZO algorithm may not be known until after the
* compression level has been set (-Xalgorithm used after -Xcompression-level)
*
* This function returns 0 on successful post processing, or
* -1 on error
*/
static int lzo_options_post(int block_size)
{
/*
* Use of compression level only makes sense for
* LZO1X_999 algorithm
*/
if(user_comp_level != -1) {
if(algorithm != SQUASHFS_LZO1X_999) {
fprintf(stderr, "lzo: -Xcompression-level not "
"supported by selected %s algorithm\n",
lzo[algorithm].name);
fprintf(stderr, "lzo: -Xcompression-level is only "
"applicable for the lzo1x_999 algorithm\n");
goto failed;
}
compression_level = user_comp_level;
}
return 0;
failed:
return -1;
}
/*
* This function is called by mksquashfs to dump the parsed
* compressor options in a format suitable for writing to the
* compressor options field in the filesystem (stored immediately
* after the superblock).
*
* This function returns a pointer to the compression options structure
* to be stored (and the size), or NULL if there are no compression
* options
*
*/
static void *lzo_dump_options(int block_size, int *size)
{
static struct lzo_comp_opts comp_opts;
/*
* If default compression options of SQUASHFS_LZO1X_999 and
* compression level of SQUASHFS_LZO1X_999_COMP_DEFAULT then
* don't store a compression options structure (this is compatible
* with the legacy implementation of LZO for Squashfs)
*/
if(algorithm == SQUASHFS_LZO1X_999 &&
compression_level == SQUASHFS_LZO1X_999_COMP_DEFAULT)
return NULL;
comp_opts.algorithm = algorithm;
comp_opts.compression_level = algorithm == SQUASHFS_LZO1X_999 ?
compression_level : 0;
SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
*size = sizeof(comp_opts);
return &comp_opts;
}
/*
* This function is a helper specifically for the append mode of
* mksquashfs. Its purpose is to set the internal compressor state
* to the stored compressor options in the passed compressor options
* structure.
*
* In effect this function sets up the compressor options
* to the same state they were when the filesystem was originally
* generated, this is to ensure on appending, the compressor uses
* the same compression options that were used to generate the
* original filesystem.
*
* Note, even if there are no compressor options, this function is still
* called with an empty compressor structure (size == 0), to explicitly
* set the default options, this is to ensure any user supplied
* -X options on the appending mksquashfs command line are over-ridden
*
* This function returns 0 on sucessful extraction of options, and
* -1 on error
*/
static int lzo_extract_options(int block_size, void *buffer, int size)
{
struct lzo_comp_opts *comp_opts = buffer;
if(size == 0) {
/* Set default values */
algorithm = SQUASHFS_LZO1X_999;
compression_level = SQUASHFS_LZO1X_999_COMP_DEFAULT;
return 0;
}
/* we expect a comp_opts structure of sufficient size to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* Check comp_opts structure for correctness */
switch(comp_opts->algorithm) {
case SQUASHFS_LZO1X_1:
case SQUASHFS_LZO1X_1_11:
case SQUASHFS_LZO1X_1_12:
case SQUASHFS_LZO1X_1_15:
if(comp_opts->compression_level != 0) {
fprintf(stderr, "lzo: bad compression level in "
"compression options structure\n");
goto failed;
}
break;
case SQUASHFS_LZO1X_999:
if(comp_opts->compression_level < 1 ||
comp_opts->compression_level > 9) {
fprintf(stderr, "lzo: bad compression level in "
"compression options structure\n");
goto failed;
}
compression_level = comp_opts->compression_level;
break;
default:
fprintf(stderr, "lzo: bad algorithm in compression options "
"structure\n");
goto failed;
}
algorithm = comp_opts->algorithm;
return 0;
failed:
fprintf(stderr, "lzo: error reading stored compressor options from "
"filesystem!\n");
return -1;
}
static void lzo_display_options(void *buffer, int size)
{
struct lzo_comp_opts *comp_opts = buffer;
/* we expect a comp_opts structure of sufficient size to be present */
if(size < sizeof(*comp_opts))
goto failed;
SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
/* Check comp_opts structure for correctness */
switch(comp_opts->algorithm) {
case SQUASHFS_LZO1X_1:
case SQUASHFS_LZO1X_1_11:
case SQUASHFS_LZO1X_1_12:
case SQUASHFS_LZO1X_1_15:
printf("\talgorithm %s\n", lzo[comp_opts->algorithm].name);
break;
case SQUASHFS_LZO1X_999:
if(comp_opts->compression_level < 1 ||
comp_opts->compression_level > 9) {
fprintf(stderr, "lzo: bad compression level in "
"compression options structure\n");
goto failed;
}
printf("\talgorithm %s\n", lzo[comp_opts->algorithm].name);
printf("\tcompression level %d\n",
comp_opts->compression_level);
break;
default:
fprintf(stderr, "lzo: bad algorithm in compression options "
"structure\n");
goto failed;
}
return;
failed:
fprintf(stderr, "lzo: error reading stored compressor options from "
"filesystem!\n");
}
/*
* This function is called by mksquashfs to initialise the
* compressor, before compress() is called.
*
* This function returns 0 on success, and
* -1 on error
*/
static int squashfs_lzo_init(void **strm, int block_size, int datablock)
{
struct lzo_stream *stream;
stream = *strm = malloc(sizeof(struct lzo_stream));
if(stream == NULL)
goto failed;
stream->workspace = malloc(lzo[algorithm].size);
if(stream->workspace == NULL)
goto failed2;
stream->buffer = malloc(LZO_MAX_EXPANSION(block_size));
if(stream->buffer != NULL)
return 0;
free(stream->workspace);
failed2:
free(stream);
failed:
return -1;
}
static int lzo_compress(void *strm, void *dest, void *src, int size,
int block_size, int *error)
{
return 0;
}
static int lzo_uncompress(void *dest, void *src, int size, int outsize,
int *error)
{
int res;
lzo_uint outlen = outsize;
res = lzo1x_decompress_safe(src, size, dest, &outlen, NULL);
if(res != LZO_E_OK) {
*error = res;
return -1;
}
return outlen;
}
static void lzo_usage()
{
int i;
fprintf(stderr, "\t -Xalgorithm <algorithm>\n");
fprintf(stderr, "\t\tWhere <algorithm> is one of:\n");
for(i = 0; lzo[i].name; i++)
fprintf(stderr, "\t\t\t%s%s\n", lzo[i].name,
i == SQUASHFS_LZO1X_999 ? " (default)" : "");
fprintf(stderr, "\t -Xcompression-level <compression-level>\n");
fprintf(stderr, "\t\t<compression-level> should be 1 .. 9 (default "
"%d)\n", SQUASHFS_LZO1X_999_COMP_DEFAULT);
fprintf(stderr, "\t\tOnly applies to lzo1x_999 algorithm\n");
}
/*
* Helper function for lzo1x_999 compression algorithm.
* All other lzo1x_xxx compressors do not take a compression level,
* so we need to wrap lzo1x_999 to pass the compression level which
* is applicable to it
*/
int lzo1x_999_wrapper(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst,
lzo_uintp compsize, lzo_voidp workspace)
{
return lzo1x_999_compress_level(src, src_len, dst, compsize,
workspace, NULL, 0, 0, compression_level);
}
struct compressor lzo_comp_ops = {
.init = squashfs_lzo_init,
.compress = lzo_compress,
.uncompress = lzo_uncompress,
.options = lzo_options,
.options_post = lzo_options_post,
.dump_options = lzo_dump_options,
.extract_options = lzo_extract_options,
.display_options = lzo_display_options,
.usage = lzo_usage,
.id = LZO_COMPRESSION,
.name = "lzo",
.supported = 1
};

View File

@@ -0,0 +1,78 @@
#ifndef LZO_WRAPPER_H
#define LZO_WRAPPER_H
/*
* Squashfs
*
* Copyright (c) 2013
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* lzo_wrapper.h
*
*/
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
extern unsigned int inswap_le32(unsigned int);
#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
(s)->algorithm = inswap_le32((s)->algorithm); \
(s)->compression_level = inswap_le32((s)->compression_level); \
}
#else
#define SQUASHFS_INSWAP_COMP_OPTS(s)
#endif
/* Define the compression flags recognised. */
#define SQUASHFS_LZO1X_1 0
#define SQUASHFS_LZO1X_1_11 1
#define SQUASHFS_LZO1X_1_12 2
#define SQUASHFS_LZO1X_1_15 3
#define SQUASHFS_LZO1X_999 4
/* Default compression level used by SQUASHFS_LZO1X_999 */
#define SQUASHFS_LZO1X_999_COMP_DEFAULT 8
struct lzo_comp_opts {
int algorithm;
int compression_level;
};
struct lzo_algorithm {
char *name;
int size;
int (*compress) (const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp,
lzo_voidp);
};
struct lzo_stream {
void *workspace;
void *buffer;
};
#define LZO_MAX_EXPANSION(size) (size + (size / 16) + 64 + 3)
int lzo1x_999_wrapper(const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp,
lzo_voidp);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,165 @@
#ifndef MKSQUASHFS_H
#define MKSQUASHFS_H
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
* 2012, 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* mksquashfs.h
*
*/
struct dir_info {
char *pathname;
char *subpath;
unsigned int count;
unsigned int directory_count;
int depth;
unsigned int excluded;
char dir_is_ldir;
struct dir_ent *dir_ent;
struct dir_ent *list;
DIR *linuxdir;
};
struct dir_ent {
char *name;
char *source_name;
char *nonstandard_pathname;
struct inode_info *inode;
struct dir_info *dir;
struct dir_info *our_dir;
struct dir_ent *next;
};
struct inode_info {
struct stat buf;
struct inode_info *next;
squashfs_inode inode;
unsigned int inode_number;
unsigned int nlink;
int pseudo_id;
char type;
char read;
char root_entry;
char pseudo_file;
char no_fragments;
char always_use_fragments;
char noD;
char noF;
char symlink[0];
};
/* in memory file info */
struct file_info {
long long file_size;
long long bytes;
long long start;
unsigned int *block_list;
struct file_info *next;
struct fragment *fragment;
unsigned short checksum;
unsigned short fragment_checksum;
char have_frag_checksum;
char have_checksum;
};
/* fragment block data structures */
struct fragment {
unsigned int index;
int offset;
int size;
};
/* in memory uid tables */
#define ID_ENTRIES 256
#define ID_HASH(id) (id & (ID_ENTRIES - 1))
#define ISA_UID 1
#define ISA_GID 2
struct id {
unsigned int id;
int index;
char flags;
struct id *next;
};
/* fragment to file mapping used when appending */
struct append_file {
struct file_info *file;
struct append_file *next;
};
#define PSEUDO_FILE_OTHER 1
#define PSEUDO_FILE_PROCESS 2
#define IS_PSEUDO(a) ((a)->pseudo_file)
#define IS_PSEUDO_PROCESS(a) ((a)->pseudo_file & PSEUDO_FILE_PROCESS)
#define IS_PSEUDO_OTHER(a) ((a)->pseudo_file & PSEUDO_FILE_OTHER)
/*
* Amount of physical memory to use by default, and the default queue
* ratios
*/
#define SQUASHFS_TAKE 4
#define SQUASHFS_READQ_MEM 4
#define SQUASHFS_BWRITEQ_MEM 4
#define SQUASHFS_FWRITEQ_MEM 4
/*
* Lowest amount of physical memory considered viable for Mksquashfs
* to run in Mbytes
*/
#define SQUASHFS_LOWMEM 64
/* offset of data in compressed metadata blocks (allowing room for
* compressed size */
#define BLOCK_OFFSET 2
#ifdef REPRODUCIBLE_DEFAULT
#define NOREP_STR
#define REP_STR " (default)"
#define REP_DEF 1
#else
#define NOREP_STR " (default)"
#define REP_STR
#define REP_DEF 0
#endif
extern struct cache *reader_buffer, *fragment_buffer, *reserve_cache;
struct cache *bwriter_buffer, *fwriter_buffer;
extern struct queue *to_reader, *to_deflate, *to_writer, *from_writer,
*to_frag, *locked_fragment, *to_process_frag;
extern struct append_file **file_mapping;
extern struct seq_queue *to_main, *to_order;
extern pthread_mutex_t fragment_mutex, dup_mutex;
extern struct squashfs_fragment_entry *fragment_table;
extern struct compressor *comp;
extern int block_size;
extern struct file_info *dupl[];
extern int read_fs_bytes(int, long long, int, void *);
extern void add_file(long long, long long, long long, unsigned int *, int,
unsigned int, int, int);
extern struct id *create_id(unsigned int);
extern unsigned int get_uid(unsigned int);
extern unsigned int get_guid(unsigned int);
extern int read_bytes(int, void *, int);
extern unsigned short get_checksum_mem(char *, int);
extern int reproducible;
#endif

View File

@@ -0,0 +1,371 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* process_fragments.c
*/
#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "caches-queues-lists.h"
#include "squashfs_fs.h"
#include "mksquashfs.h"
#include "error.h"
#include "progressbar.h"
#include "info.h"
#include "compressor.h"
#include "process_fragments.h"
#define FALSE 0
#define TRUE 1
extern struct queue *to_process_frag;
extern struct seq_queue *to_main;
extern int sparse_files;
extern long long start_offset;
/*
* Compute 16 bit BSD checksum over the data, and check for sparseness
*/
static int checksum_sparse(struct file_buffer *file_buffer)
{
unsigned char *b = (unsigned char *) file_buffer->data;
unsigned short chksum = 0;
int bytes = file_buffer->size, sparse = TRUE, value;
while(bytes --) {
chksum = (chksum & 1) ? (chksum >> 1) | 0x8000 : chksum >> 1;
value = *b++;
if(value) {
sparse = FALSE;
chksum += value;
}
}
file_buffer->checksum = chksum;
return sparse;
}
static int read_filesystem(int fd, long long byte, int bytes, void *buff)
{
off_t off = byte;
TRACE("read_filesystem: reading from position 0x%llx, bytes %d\n",
byte, bytes);
if(lseek(fd, start_offset + off, SEEK_SET) == -1) {
ERROR("read_filesystem: Lseek on destination failed because %s, "
"offset=0x%llx\n", strerror(errno), start_offset + off);
return 0;
} else if(read_bytes(fd, buff, bytes) < bytes) {
ERROR("Read on destination failed\n");
return 0;
}
return 1;
}
static struct file_buffer *get_fragment(struct fragment *fragment,
char *data_buffer, int fd)
{
struct squashfs_fragment_entry *disk_fragment;
struct file_buffer *buffer, *compressed_buffer;
long long start_block;
int res, size, index = fragment->index;
char locked;
/*
* Lookup fragment block in cache.
* If the fragment block doesn't exist, then get the compressed version
* from the writer cache or off disk, and decompress it.
*
* This routine has two things which complicate the code:
*
* 1. Multiple threads can simultaneously lookup/create the
* same buffer. This means a buffer needs to be "locked"
* when it is being filled in, to prevent other threads from
* using it when it is not ready. This is because we now do
* fragment duplicate checking in parallel.
* 2. We have two caches which need to be checked for the
* presence of fragment blocks: the normal fragment cache
* and a "reserve" cache. The reserve cache is used to
* prevent an unnecessary pipeline stall when the fragment cache
* is full of fragments waiting to be compressed.
*/
pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
pthread_mutex_lock(&dup_mutex);
again:
buffer = cache_lookup_nowait(fragment_buffer, index, &locked);
if(buffer) {
pthread_mutex_unlock(&dup_mutex);
if(locked)
/* got a buffer being filled in. Wait for it */
cache_wait_unlock(buffer);
goto finished;
}
/* not in fragment cache, is it in the reserve cache? */
buffer = cache_lookup_nowait(reserve_cache, index, &locked);
if(buffer) {
pthread_mutex_unlock(&dup_mutex);
if(locked)
/* got a buffer being filled in. Wait for it */
cache_wait_unlock(buffer);
goto finished;
}
/* in neither cache, try to get it from the fragment cache */
buffer = cache_get_nowait(fragment_buffer, index);
if(!buffer) {
/*
* no room, get it from the reserve cache, this is
* dimensioned so it will always have space (no more than
* processors + 1 can have an outstanding reserve buffer)
*/
buffer = cache_get_nowait(reserve_cache, index);
if(!buffer) {
/* failsafe */
ERROR("no space in reserve cache\n");
goto again;
}
}
pthread_mutex_unlock(&dup_mutex);
compressed_buffer = cache_lookup(fwriter_buffer, index);
pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
pthread_mutex_lock(&fragment_mutex);
disk_fragment = &fragment_table[index];
size = SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment->size);
start_block = disk_fragment->start_block;
pthread_cleanup_pop(1);
if(SQUASHFS_COMPRESSED_BLOCK(disk_fragment->size)) {
int error;
char *data;
if(compressed_buffer)
data = compressed_buffer->data;
else {
res = read_filesystem(fd, start_block, size, data_buffer);
if(res == 0) {
ERROR("Failed to read fragment from output"
" filesystem\n");
BAD_ERROR("Output filesystem corrupted?\n");
}
data = data_buffer;
}
res = compressor_uncompress(comp, buffer->data, data, size,
block_size, &error);
if(res == -1)
BAD_ERROR("%s uncompress failed with error code %d\n",
comp->name, error);
} else if(compressed_buffer)
memcpy(buffer->data, compressed_buffer->data, size);
else {
res = read_filesystem(fd, start_block, size, buffer->data);
if(res == 0) {
ERROR("Failed to read fragment from output "
"filesystem\n");
BAD_ERROR("Output filesystem corrupted?\n");
}
}
cache_unlock(buffer);
cache_block_put(compressed_buffer);
finished:
pthread_cleanup_pop(0);
return buffer;
}
struct file_buffer *get_fragment_cksum(struct file_info *file,
char *data_buffer, int fd, unsigned short *checksum)
{
struct file_buffer *frag_buffer;
struct append_file *append;
int index = file->fragment->index;
frag_buffer = get_fragment(file->fragment, data_buffer, fd);
pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
for(append = file_mapping[index]; append; append = append->next) {
int offset = append->file->fragment->offset;
int size = append->file->fragment->size;
char *data = frag_buffer->data + offset;
unsigned short cksum = get_checksum_mem(data, size);
if(file == append->file)
*checksum = cksum;
pthread_mutex_lock(&dup_mutex);
append->file->fragment_checksum = cksum;
append->file->have_frag_checksum = TRUE;
pthread_mutex_unlock(&dup_mutex);
}
pthread_cleanup_pop(0);
return frag_buffer;
}
void *frag_thrd(void *destination_file)
{
sigset_t sigmask, old_mask;
char *data_buffer;
int fd;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGINT);
sigaddset(&sigmask, SIGTERM);
sigaddset(&sigmask, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask);
fd = open(destination_file, O_RDONLY);
if(fd == -1)
BAD_ERROR("frag_thrd: can't open destination for reading\n");
data_buffer = malloc(SQUASHFS_FILE_MAX_SIZE);
if(data_buffer == NULL)
MEM_ERROR();
pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
while(1) {
struct file_buffer *file_buffer = queue_get(to_process_frag);
struct file_buffer *buffer;
int sparse = checksum_sparse(file_buffer);
struct file_info *dupl_ptr;
long long file_size;
unsigned short checksum;
char flag;
int res;
if(sparse_files && sparse) {
file_buffer->c_byte = 0;
file_buffer->fragment = FALSE;
} else
file_buffer->c_byte = file_buffer->size;
/*
* Specutively pull into the fragment cache any fragment blocks
* which contain fragments which *this* fragment may be
* be a duplicate.
*
* By ensuring the fragment block is in cache ahead of time
* should eliminate the parallelisation stall when the
* main thread needs to read the fragment block to do a
* duplicate check on it.
*
* If this is a fragment belonging to a larger file
* (with additional blocks) then ignore it. Here we're
* interested in the "low hanging fruit" of files which
* consist of only a fragment
*/
if(file_buffer->file_size != file_buffer->size) {
seq_queue_put(to_main, file_buffer);
continue;
}
file_size = file_buffer->file_size;
pthread_mutex_lock(&dup_mutex);
dupl_ptr = dupl[DUP_HASH(file_size)];
pthread_mutex_unlock(&dup_mutex);
file_buffer->dupl_start = dupl_ptr;
file_buffer->duplicate = FALSE;
for(; dupl_ptr; dupl_ptr = dupl_ptr->next) {
if(file_size != dupl_ptr->file_size ||
file_size != dupl_ptr->fragment->size)
continue;
pthread_mutex_lock(&dup_mutex);
flag = dupl_ptr->have_frag_checksum;
checksum = dupl_ptr->fragment_checksum;
pthread_mutex_unlock(&dup_mutex);
/*
* If we have the checksum and it matches then
* read in the fragment block.
*
* If we *don't* have the checksum, then we are
* appending, and the fragment block is on the
* "old" filesystem. Read it in and checksum
* the entire fragment buffer
*/
if(!flag) {
buffer = get_fragment_cksum(dupl_ptr,
data_buffer, fd, &checksum);
if(checksum != file_buffer->checksum) {
cache_block_put(buffer);
continue;
}
} else if(checksum == file_buffer->checksum)
buffer = get_fragment(dupl_ptr->fragment,
data_buffer, fd);
else
continue;
res = memcmp(file_buffer->data, buffer->data +
dupl_ptr->fragment->offset, file_size);
cache_block_put(buffer);
if(res == 0) {
struct file_buffer *dup = malloc(sizeof(*dup));
if(dup == NULL)
MEM_ERROR();
memcpy(dup, file_buffer, sizeof(*dup));
cache_block_put(file_buffer);
dup->dupl_start = dupl_ptr;
dup->duplicate = TRUE;
file_buffer = dup;
break;
}
}
seq_queue_put(to_main, file_buffer);
}
pthread_cleanup_pop(0);
}

View File

@@ -0,0 +1,30 @@
#ifndef PROCESS_FRAGMENTS_H
#define PROCESS_FRAGMENTS_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* process_fragments.h
*/
#define DUP_HASH(a) (a & 0xffff)
extern void *frag_thrd(void *);
#endif

View File

@@ -0,0 +1,259 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2012, 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* progressbar.c
*/
#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include "error.h"
#define FALSE 0
#define TRUE 1
/* flag whether progressbar display is enabled or not */
int display_progress_bar = FALSE;
/* flag whether the progress bar is temporarily disbled */
int temp_disabled = FALSE;
int rotate = 0;
int cur_uncompressed = 0, estimated_uncompressed = 0;
int columns;
pthread_t progress_thread;
pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
static void sigwinch_handler()
{
struct winsize winsize;
if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
if(isatty(STDOUT_FILENO))
ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
"columns\n");
columns = 80;
} else
columns = winsize.ws_col;
}
static void sigalrm_handler()
{
rotate = (rotate + 1) % 4;
}
void inc_progress_bar()
{
cur_uncompressed ++;
}
void dec_progress_bar(int count)
{
cur_uncompressed -= count;
}
void progress_bar_size(int count)
{
estimated_uncompressed += count;
}
static void progress_bar(long long current, long long max, int columns)
{
char rotate_list[] = { '|', '/', '-', '\\' };
int max_digits, used, hashes, spaces;
static int tty = -1;
if(max == 0)
return;
max_digits = floor(log10(max)) + 1;
used = max_digits * 2 + 11;
hashes = (current * (columns - used)) / max;
spaces = columns - used - hashes;
if((current > max) || (columns - used < 0))
return;
if(tty == -1)
tty = isatty(STDOUT_FILENO);
if(!tty) {
static long long previous = -1;
/* Updating much more frequently than this results in huge
* log files. */
if((current % 100) != 0 && current != max)
return;
/* Don't update just to rotate the spinner. */
if(current == previous)
return;
previous = current;
}
printf("\r[");
while (hashes --)
putchar('=');
putchar(rotate_list[rotate]);
while(spaces --)
putchar(' ');
printf("] %*lld/%*lld", max_digits, current, max_digits, max);
printf(" %3lld%%", current * 100 / max);
fflush(stdout);
}
void enable_progress_bar()
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar)
progress_bar(cur_uncompressed, estimated_uncompressed, columns);
temp_disabled = FALSE;
pthread_cleanup_pop(1);
}
void disable_progress_bar()
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar)
printf("\n");
temp_disabled = TRUE;
pthread_cleanup_pop(1);
}
void set_progressbar_state(int state)
{
pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar != state) {
if(display_progress_bar && !temp_disabled) {
progress_bar(cur_uncompressed, estimated_uncompressed,
columns);
printf("\n");
}
display_progress_bar = state;
}
pthread_cleanup_pop(1);
}
void *progress_thrd(void *arg)
{
struct timespec requested_time, remaining;
struct itimerval itimerval;
struct winsize winsize;
if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
if(isatty(STDOUT_FILENO))
ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
"columns\n");
columns = 80;
} else
columns = winsize.ws_col;
signal(SIGWINCH, sigwinch_handler);
signal(SIGALRM, sigalrm_handler);
itimerval.it_value.tv_sec = 0;
itimerval.it_value.tv_usec = 250000;
itimerval.it_interval.tv_sec = 0;
itimerval.it_interval.tv_usec = 250000;
setitimer(ITIMER_REAL, &itimerval, NULL);
requested_time.tv_sec = 0;
requested_time.tv_nsec = 250000000;
while(1) {
int res = nanosleep(&requested_time, &remaining);
if(res == -1 && errno != EINTR)
BAD_ERROR("nanosleep failed in progress thread\n");
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar && !temp_disabled)
progress_bar(cur_uncompressed, estimated_uncompressed,
columns);
pthread_mutex_unlock(&progress_mutex);
}
}
void init_progress_bar()
{
pthread_create(&progress_thread, NULL, progress_thrd, NULL);
}
void progressbar_error(char *fmt, ...)
{
va_list ap;
pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar && !temp_disabled)
fprintf(stderr, "\n");
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
pthread_cleanup_pop(1);
}
void progressbar_info(char *fmt, ...)
{
va_list ap;
pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
pthread_mutex_lock(&progress_mutex);
if(display_progress_bar && !temp_disabled)
printf("\n");
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
pthread_cleanup_pop(1);
}

View File

@@ -0,0 +1,34 @@
#ifndef PROGRESSBAR_H
#define PROGRESSBAR_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2012, 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* progressbar.h
*/
extern void inc_progress_bar();
extern void dec_progress_bar(int count);
extern void progress_bar_size(int count);
extern void enable_progress_bar();
extern void disable_progress_bar();
extern void init_progress_bar();
extern void set_progressbar_state(int);
#endif

View File

@@ -0,0 +1,559 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2012, 2014, 2017, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* pseudo.c
*/
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <ctype.h>
#include "pseudo.h"
#include "error.h"
#include "progressbar.h"
#define TRUE 1
#define FALSE 0
extern int read_file(char *filename, char *type, int (parse_line)(char *));
struct pseudo_dev **pseudo_file = NULL;
struct pseudo *pseudo = NULL;
int pseudo_count = 0;
static char *get_component(char *target, char **targname)
{
char *start;
start = target;
while(*target != '/' && *target != '\0')
target ++;
*targname = strndup(start, target - start);
while(*target == '/')
target ++;
return target;
}
/*
* Add pseudo device target to the set of pseudo devices. Pseudo_dev
* describes the pseudo device attributes.
*/
struct pseudo *add_pseudo(struct pseudo *pseudo, struct pseudo_dev *pseudo_dev,
char *target, char *alltarget)
{
char *targname;
int i;
target = get_component(target, &targname);
if(pseudo == NULL) {
pseudo = malloc(sizeof(struct pseudo));
if(pseudo == NULL)
MEM_ERROR();
pseudo->names = 0;
pseudo->count = 0;
pseudo->name = NULL;
}
for(i = 0; i < pseudo->names; i++)
if(strcmp(pseudo->name[i].name, targname) == 0)
break;
if(i == pseudo->names) {
/* allocate new name entry */
pseudo->names ++;
pseudo->name = realloc(pseudo->name, (i + 1) *
sizeof(struct pseudo_entry));
if(pseudo->name == NULL)
MEM_ERROR();
pseudo->name[i].name = targname;
if(target[0] == '\0') {
/* at leaf pathname component */
pseudo->name[i].pseudo = NULL;
pseudo->name[i].pathname = strdup(alltarget);
pseudo->name[i].dev = pseudo_dev;
} else {
/* recurse adding child components */
pseudo->name[i].dev = NULL;
pseudo->name[i].pseudo = add_pseudo(NULL, pseudo_dev,
target, alltarget);
}
} else {
/* existing matching entry */
free(targname);
if(pseudo->name[i].pseudo == NULL) {
/* No sub-directory which means this is the leaf
* component of a pre-existing pseudo file.
*/
if(target[0] != '\0') {
/*
* entry must exist as either a 'd' type or
* 'm' type pseudo file
*/
if(pseudo->name[i].dev->type == 'd' ||
pseudo->name[i].dev->type == 'm')
/* recurse adding child components */
pseudo->name[i].pseudo =
add_pseudo(NULL, pseudo_dev,
target, alltarget);
else {
ERROR_START("%s already exists as a "
"non directory.",
pseudo->name[i].name);
ERROR_EXIT(". Ignoring %s!\n",
alltarget);
}
} else if(memcmp(pseudo_dev, pseudo->name[i].dev,
sizeof(struct pseudo_dev)) != 0) {
ERROR_START("%s already exists as a different "
"pseudo definition.", alltarget);
ERROR_EXIT(" Ignoring!\n");
} else {
ERROR_START("%s already exists as an identical "
"pseudo definition!", alltarget);
ERROR_EXIT(" Ignoring!\n");
}
} else {
if(target[0] == '\0') {
/*
* sub-directory exists, which means we can only
* add a pseudo file of type 'd' or type 'm'
*/
if(pseudo->name[i].dev == NULL &&
(pseudo_dev->type == 'd' ||
pseudo_dev->type == 'm')) {
pseudo->name[i].pathname =
strdup(alltarget);
pseudo->name[i].dev = pseudo_dev;
} else {
ERROR_START("%s already exists as a "
"different pseudo definition.",
pseudo->name[i].name);
ERROR_EXIT(" Ignoring %s!\n",
alltarget);
}
} else
/* recurse adding child components */
add_pseudo(pseudo->name[i].pseudo, pseudo_dev,
target, alltarget);
}
}
return pseudo;
}
/*
* Find subdirectory in pseudo directory referenced by pseudo, matching
* filename. If filename doesn't exist or if filename is a leaf file
* return NULL
*/
struct pseudo *pseudo_subdir(char *filename, struct pseudo *pseudo)
{
int i;
if(pseudo == NULL)
return NULL;
for(i = 0; i < pseudo->names; i++)
if(strcmp(filename, pseudo->name[i].name) == 0)
return pseudo->name[i].pseudo;
return NULL;
}
struct pseudo_entry *pseudo_readdir(struct pseudo *pseudo)
{
if(pseudo == NULL)
return NULL;
while(pseudo->count < pseudo->names) {
if(pseudo->name[pseudo->count].dev != NULL)
return &pseudo->name[pseudo->count++];
else
pseudo->count++;
}
return NULL;
}
int pseudo_exec_file(struct pseudo_dev *dev, int *child)
{
int res, pipefd[2];
res = pipe(pipefd);
if(res == -1) {
ERROR("Executing dynamic pseudo file, pipe failed\n");
return 0;
}
*child = fork();
if(*child == -1) {
ERROR("Executing dynamic pseudo file, fork failed\n");
goto failed;
}
if(*child == 0) {
close(pipefd[0]);
close(STDOUT_FILENO);
res = dup(pipefd[1]);
if(res == -1)
exit(EXIT_FAILURE);
execl("/bin/sh", "sh", "-c", dev->command, (char *) NULL);
exit(EXIT_FAILURE);
}
close(pipefd[1]);
return pipefd[0];
failed:
close(pipefd[0]);
close(pipefd[1]);
return 0;
}
void add_pseudo_file(struct pseudo_dev *dev)
{
pseudo_file = realloc(pseudo_file, (pseudo_count + 1) *
sizeof(struct pseudo_dev *));
if(pseudo_file == NULL)
MEM_ERROR();
dev->pseudo_id = pseudo_count;
pseudo_file[pseudo_count ++] = dev;
}
struct pseudo_dev *get_pseudo_file(int pseudo_id)
{
return pseudo_file[pseudo_id];
}
int read_pseudo_def(char *def)
{
int n, bytes;
int quoted = 0;
unsigned int major = 0, minor = 0, mode;
char type, *ptr;
char suid[100], sgid[100]; /* overflow safe */
char *filename, *name;
char *orig_def = def;
long long uid, gid;
struct pseudo_dev *dev;
/*
* Scan for filename, don't use sscanf() and "%s" because
* that can't handle filenames with spaces.
*
* Filenames with spaces should either escape (backslash) the
* space or use double quotes.
*/
filename = malloc(strlen(def) + 1);
if(filename == NULL)
MEM_ERROR();
for(name = filename; (quoted || !isspace(*def)) && *def != '\0';) {
if(*def == '"') {
quoted = !quoted;
def ++;
continue;
}
if(*def == '\\') {
def ++;
if (*def == '\0')
break;
}
*name ++ = *def ++;
}
*name = '\0';
/* Skip any leading slashes (/) */
for(name = filename; *name == '/'; name ++);
if(*name == '\0') {
ERROR("Not enough or invalid arguments in pseudo file "
"definition \"%s\"\n", orig_def);
goto error;
}
n = sscanf(def, " %c %o %99s %99s %n", &type, &mode, suid, sgid,
&bytes);
def += bytes;
if(n < 4) {
ERROR("Not enough or invalid arguments in pseudo file "
"definition \"%s\"\n", orig_def);
switch(n) {
case -1:
/* FALLTHROUGH */
case 0:
/* FALLTHROUGH */
case 1:
ERROR("Couldn't parse filename, type or octal mode\n");
ERROR("If the filename has spaces, either quote it, or "
"backslash the spaces\n");
break;
case 2:
ERROR("Read filename, type and mode, but failed to "
"read or match uid\n");
break;
default:
ERROR("Read filename, type, mode and uid, but failed "
"to read or match gid\n");
break;
}
goto error;
}
switch(type) {
case 'b':
/* FALLTHROUGH */
case 'c':
n = sscanf(def, "%u %u %n", &major, &minor, &bytes);
def += bytes;
if(n < 2) {
ERROR("Not enough or invalid arguments in %s device "
"pseudo file definition \"%s\"\n", type == 'b' ?
"block" : "character", orig_def);
if(n < 1)
ERROR("Read filename, type, mode, uid and gid, "
"but failed to read or match major\n");
else
ERROR("Read filename, type, mode, uid, gid "
"and major, but failed to read or "
"match minor\n");
goto error;
}
if(major > 0xfff) {
ERROR("Major %d out of range\n", major);
goto error;
}
if(minor > 0xfffff) {
ERROR("Minor %d out of range\n", minor);
goto error;
}
/* FALLTHROUGH */
case 'd':
/* FALLTHROUGH */
case 'm':
/*
* Check for trailing junk after expected arguments
*/
if(def[0] != '\0') {
ERROR("Unexpected tailing characters in pseudo file "
"definition \"%s\"\n", orig_def);
goto error;
}
break;
case 'f':
if(def[0] == '\0') {
ERROR("Not enough arguments in dynamic file pseudo "
"definition \"%s\"\n", orig_def);
ERROR("Expected command, which can be an executable "
"or a piece of shell script\n");
goto error;
}
break;
case 's':
if(def[0] == '\0') {
ERROR("Not enough arguments in symlink pseudo "
"definition \"%s\"\n", orig_def);
ERROR("Expected symlink\n");
goto error;
}
if(strlen(def) > 65535) {
ERROR("Symlink pseudo definition %s is greater than 65535"
" bytes!\n", def);
goto error;
}
break;
default:
ERROR("Unsupported type %c\n", type);
goto error;
}
if(mode > 07777) {
ERROR("Mode %o out of range\n", mode);
goto error;
}
uid = strtoll(suid, &ptr, 10);
if(*ptr == '\0') {
if(uid < 0 || uid > ((1LL << 32) - 1)) {
ERROR("Uid %s out of range\n", suid);
goto error;
}
} else {
struct passwd *pwuid = getpwnam(suid);
if(pwuid)
uid = pwuid->pw_uid;
else {
ERROR("Uid %s invalid uid or unknown user\n", suid);
goto error;
}
}
gid = strtoll(sgid, &ptr, 10);
if(*ptr == '\0') {
if(gid < 0 || gid > ((1LL << 32) - 1)) {
ERROR("Gid %s out of range\n", sgid);
goto error;
}
} else {
struct group *grgid = getgrnam(sgid);
if(grgid)
gid = grgid->gr_gid;
else {
ERROR("Gid %s invalid uid or unknown user\n", sgid);
goto error;
}
}
switch(type) {
case 'b':
mode |= S_IFBLK;
break;
case 'c':
mode |= S_IFCHR;
break;
case 'd':
mode |= S_IFDIR;
break;
case 'f':
mode |= S_IFREG;
break;
case 's':
/* permissions on symlinks are always rwxrwxrwx */
mode = 0777 | S_IFLNK;
break;
}
dev = malloc(sizeof(struct pseudo_dev));
if(dev == NULL)
MEM_ERROR();
dev->type = type;
dev->mode = mode;
dev->uid = uid;
dev->gid = gid;
dev->major = major;
dev->minor = minor;
if(type == 'f') {
dev->command = strdup(def);
add_pseudo_file(dev);
}
if(type == 's')
dev->symlink = strdup(def);
pseudo = add_pseudo(pseudo, dev, name, name);
free(filename);
return TRUE;
error:
ERROR("Pseudo definitions should be of the format\n");
ERROR("\tfilename d mode uid gid\n");
ERROR("\tfilename m mode uid gid\n");
ERROR("\tfilename b mode uid gid major minor\n");
ERROR("\tfilename c mode uid gid major minor\n");
ERROR("\tfilename f mode uid gid command\n");
ERROR("\tfilename s mode uid gid symlink\n");
free(filename);
return FALSE;
}
int read_pseudo_file(char *filename)
{
return read_file(filename, "pseudo", read_pseudo_def);
}
struct pseudo *get_pseudo()
{
return pseudo;
}
#ifdef SQUASHFS_TRACE
static void dump_pseudo(struct pseudo *pseudo, char *string)
{
int i, res;
char *path;
for(i = 0; i < pseudo->names; i++) {
struct pseudo_entry *entry = &pseudo->name[i];
if(string) {
res = asprintf(&path, "%s/%s", string, entry->name);
if(res == -1)
BAD_ERROR("asprintf failed in dump_pseudo\n");
} else
path = entry->name;
if(entry->dev)
ERROR("%s %c 0%o %d %d %d %d\n", path, entry->dev->type,
entry->dev->mode & ~S_IFMT, entry->dev->uid,
entry->dev->gid, entry->dev->major,
entry->dev->minor);
if(entry->pseudo)
dump_pseudo(entry->pseudo, path);
if(string)
free(path);
}
}
void dump_pseudos()
{
if (pseudo)
dump_pseudo(pseudo, NULL);
}
#else
void dump_pseudos()
{
}
#endif

View File

@@ -0,0 +1,61 @@
#ifndef PSEUDO_H
#define PSEUDO_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* pseudo.h
*/
struct pseudo_dev {
char type;
unsigned int mode;
unsigned int uid;
unsigned int gid;
unsigned int major;
unsigned int minor;
int pseudo_id;
union {
char *command;
char *symlink;
};
};
struct pseudo_entry {
char *name;
char *pathname;
struct pseudo *pseudo;
struct pseudo_dev *dev;
};
struct pseudo {
int names;
int count;
struct pseudo_entry *name;
};
extern int read_pseudo_def(char *);
extern int read_pseudo_file(char *);
extern struct pseudo *pseudo_subdir(char *, struct pseudo *);
extern struct pseudo_entry *pseudo_readdir(struct pseudo *);
extern struct pseudo_dev *get_pseudo_file(int);
extern int pseudo_exec_file(struct pseudo_dev *, int *);
extern struct pseudo *get_pseudo();
extern void dump_pseudos();
#endif

View File

@@ -0,0 +1,150 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2012
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* read_file.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "error.h"
#define TRUE 1
#define FALSE 0
#define MAX_LINE 16384
/*
* Read file, passing each line to parse_line() for
* parsing.
*
* Lines can be split across multiple lines using "\".
*
* Blank lines and comment lines indicated by # are supported.
*/
int read_file(char *filename, char *type, int (parse_line)(char *))
{
FILE *fd;
char *def, *err, *line = NULL;
int res, size = 0;
fd = fopen(filename, "r");
if(fd == NULL) {
ERROR("Could not open %s device file \"%s\" because %s\n",
type, filename, strerror(errno));
return FALSE;
}
while(1) {
int total = 0;
while(1) {
int len;
if(total + (MAX_LINE + 1) > size) {
line = realloc(line, size += (MAX_LINE + 1));
if(line == NULL)
MEM_ERROR();
}
err = fgets(line + total, MAX_LINE + 1, fd);
if(err == NULL)
break;
len = strlen(line + total);
total += len;
if(len == MAX_LINE && line[total - 1] != '\n') {
/* line too large */
ERROR("Line too long when reading "
"%s file \"%s\", larger than "
"%d bytes\n", type, filename, MAX_LINE);
goto failed;
}
/*
* Remove '\n' terminator if it exists (the last line
* in the file may not be '\n' terminated)
*/
if(len && line[total - 1] == '\n') {
line[-- total] = '\0';
len --;
}
/*
* If no line continuation then jump out to
* process line. Note, we have to be careful to
* check for "\\" (backslashed backslash) and to
* ensure we don't look at the previous line
*/
if(len == 0 || line[total - 1] != '\\' || (len >= 2 &&
strcmp(line + total - 2, "\\\\") == 0))
break;
else
total --;
}
if(err == NULL) {
if(ferror(fd)) {
ERROR("Reading %s file \"%s\" failed "
"because %s\n", type, filename,
strerror(errno));
goto failed;
}
/*
* At EOF, normally we'll be finished, but, have to
* check for special case where we had "\" line
* continuation and then hit EOF immediately afterwards
*/
if(total == 0)
break;
else
line[total] = '\0';
}
/* Skip any leading whitespace */
for(def = line; isspace(*def); def ++);
/* if line is now empty after skipping characters, skip it */
if(*def == '\0')
continue;
/* if comment line, skip */
if(*def == '#')
continue;
res = parse_line(def);
if(res == FALSE)
goto failed;
}
fclose(fd);
free(line);
return TRUE;
failed:
fclose(fd);
free(line);
return FALSE;
}

View File

@@ -0,0 +1,996 @@
/*
* Read a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2012, 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* read_fs.c
*/
#define TRUE 1
#define FALSE 0
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <limits.h>
#include <dirent.h>
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#include <stdlib.h>
#include "squashfs_fs.h"
#include "squashfs_swap.h"
#include "compressor.h"
#include "xattr.h"
#include "error.h"
#include "mksquashfs.h"
int read_block(int fd, long long start, long long *next, int expected,
void *block)
{
unsigned short c_byte;
int res, compressed;
int outlen = expected ? expected : SQUASHFS_METADATA_SIZE;
/* Read block size */
res = read_fs_bytes(fd, start, 2, &c_byte);
if(res == 0)
return 0;
SQUASHFS_INSWAP_SHORTS(&c_byte, 1);
compressed = SQUASHFS_COMPRESSED(c_byte);
c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
/*
* The block size should not be larger than
* the uncompressed size (or max uncompressed size if
* expected is 0)
*/
if (c_byte > outlen)
return 0;
if(compressed) {
char buffer[c_byte];
int error;
res = read_fs_bytes(fd, start + 2, c_byte, buffer);
if(res == 0)
return 0;
res = compressor_uncompress(comp, block, buffer, c_byte,
outlen, &error);
if(res == -1) {
ERROR("%s uncompress failed with error code %d\n",
comp->name, error);
return 0;
}
} else {
res = read_fs_bytes(fd, start + 2, c_byte, block);
if(res == 0)
return 0;
res = c_byte;
}
if(next)
*next = start + 2 + c_byte;
/*
* if expected, then check the (uncompressed) return data
* is of the expected size
*/
if(expected && expected != res)
return 0;
else
return res;
}
#define NO_BYTES(SIZE) \
(bytes - (cur_ptr - inode_table) < (SIZE))
#define NO_INODE_BYTES(INODE) NO_BYTES(sizeof(struct INODE))
unsigned char *scan_inode_table(int fd, long long start, long long end,
long long root_inode_start, int root_inode_offset,
struct squashfs_super_block *sBlk, union squashfs_inode_header
*dir_inode, unsigned int *root_inode_block, unsigned int
*root_inode_size, long long *uncompressed_file, unsigned int
*uncompressed_directory, int *file_count, int *sym_count, int
*dev_count, int *dir_count, int *fifo_count, int *sock_count,
unsigned int *id_table)
{
unsigned char *cur_ptr;
unsigned char *inode_table = NULL;
int byte, files = 0;
unsigned int directory_start_block, bytes = 0, size = 0;
struct squashfs_base_inode_header base;
TRACE("scan_inode_table: start 0x%llx, end 0x%llx, root_inode_start "
"0x%llx\n", start, end, root_inode_start);
*root_inode_block = UINT_MAX;
while(start < end) {
if(start == root_inode_start) {
TRACE("scan_inode_table: read compressed block 0x%llx "
"containing root inode\n", start);
*root_inode_block = bytes;
}
if(size - bytes < SQUASHFS_METADATA_SIZE) {
inode_table = realloc(inode_table, size
+= SQUASHFS_METADATA_SIZE);
if(inode_table == NULL)
MEM_ERROR();
}
TRACE("scan_inode_table: reading block 0x%llx\n", start);
byte = read_block(fd, start, &start, 0, inode_table + bytes);
if(byte == 0)
goto corrupted;
bytes += byte;
/* If this is not the last metadata block in the inode table
* then it should be SQUASHFS_METADATA_SIZE in size.
* Note, we can't use expected in read_block() above for this
* because we don't know if this is the last block until
* after reading.
*/
if(start != end && byte != SQUASHFS_METADATA_SIZE)
goto corrupted;
}
/*
* We expect to have found the metadata block containing the
* root inode in the above inode_table metadata block scan. If it
* hasn't been found then the filesystem is corrupted
*/
if(*root_inode_block == UINT_MAX)
goto corrupted;
/*
* The number of bytes available after the root inode medata block
* should be at least the root inode offset + the size of a
* regular directory inode, if not the filesystem is corrupted
*
* +-----------------------+-----------------------+
* | | directory |
* | | inode |
* +-----------------------+-----------------------+
* ^ ^ ^
* *root_inode_block root_inode_offset bytes
*/
if((bytes - *root_inode_block) < (root_inode_offset +
sizeof(struct squashfs_dir_inode_header)))
goto corrupted;
/*
* Read last inode entry which is the root directory inode, and obtain
* the last directory start block index. This is used when calculating
* the total uncompressed directory size. The directory bytes in the
* last * block will be counted as normal.
*
* Note, the previous check ensures the following calculation won't
* underflow, and we won't access beyond the buffer
*/
*root_inode_size = bytes - (*root_inode_block + root_inode_offset);
bytes = *root_inode_block + root_inode_offset;
SQUASHFS_SWAP_DIR_INODE_HEADER(inode_table + bytes, &dir_inode->dir);
if(dir_inode->base.inode_type == SQUASHFS_DIR_TYPE)
directory_start_block = dir_inode->dir.start_block;
else if(dir_inode->base.inode_type == SQUASHFS_LDIR_TYPE) {
if(*root_inode_size < sizeof(struct squashfs_ldir_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_LDIR_INODE_HEADER(inode_table + bytes,
&dir_inode->ldir);
directory_start_block = dir_inode->ldir.start_block;
} else
/* bad type, corrupted filesystem */
goto corrupted;
get_uid(id_table[dir_inode->base.uid]);
get_guid(id_table[dir_inode->base.guid]);
/* allocate fragment to file mapping table */
file_mapping = calloc(sBlk->fragments, sizeof(struct append_file *));
if(file_mapping == NULL)
MEM_ERROR();
for(cur_ptr = inode_table; cur_ptr < inode_table + bytes; files ++) {
if(NO_INODE_BYTES(squashfs_base_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_BASE_INODE_HEADER(cur_ptr, &base);
TRACE("scan_inode_table: processing inode @ byte position "
"0x%x, type 0x%x\n",
(unsigned int) (cur_ptr - inode_table),
base.inode_type);
get_uid(id_table[base.uid]);
get_guid(id_table[base.guid]);
switch(base.inode_type) {
case SQUASHFS_FILE_TYPE: {
struct squashfs_reg_inode_header inode;
int frag_bytes, blocks, i;
long long start, file_bytes = 0;
unsigned int *block_list;
if(NO_INODE_BYTES(squashfs_reg_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_REG_INODE_HEADER(cur_ptr, &inode);
frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ?
0 : inode.file_size % sBlk->block_size;
blocks = inode.fragment == SQUASHFS_INVALID_FRAG ?
(inode.file_size + sBlk->block_size - 1) >>
sBlk->block_log : inode.file_size >>
sBlk->block_log;
start = inode.start_block;
TRACE("scan_inode_table: regular file, file_size %d, "
"blocks %d\n", inode.file_size, blocks);
if(NO_BYTES(blocks * sizeof(unsigned int)))
/* corrupted filesystem */
goto corrupted;
block_list = malloc(blocks * sizeof(unsigned int));
if(block_list == NULL)
MEM_ERROR();
cur_ptr += sizeof(inode);
SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks);
*uncompressed_file += inode.file_size;
(*file_count) ++;
for(i = 0; i < blocks; i++)
file_bytes +=
SQUASHFS_COMPRESSED_SIZE_BLOCK
(block_list[i]);
if(inode.fragment != SQUASHFS_INVALID_FRAG &&
inode.fragment >= sBlk->fragments) {
free(block_list);
goto corrupted;
}
add_file(start, inode.file_size, file_bytes,
block_list, blocks, inode.fragment,
inode.offset, frag_bytes);
cur_ptr += blocks * sizeof(unsigned int);
break;
}
case SQUASHFS_LREG_TYPE: {
struct squashfs_lreg_inode_header inode;
int frag_bytes, blocks, i;
long long start, file_bytes = 0;
unsigned int *block_list;
if(NO_INODE_BYTES(squashfs_lreg_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_LREG_INODE_HEADER(cur_ptr, &inode);
frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ?
0 : inode.file_size % sBlk->block_size;
blocks = inode.fragment == SQUASHFS_INVALID_FRAG ?
(inode.file_size + sBlk->block_size - 1) >>
sBlk->block_log : inode.file_size >>
sBlk->block_log;
start = inode.start_block;
TRACE("scan_inode_table: extended regular "
"file, file_size %lld, blocks %d\n",
inode.file_size, blocks);
if(NO_BYTES(blocks * sizeof(unsigned int)))
/* corrupted filesystem */
goto corrupted;
block_list = malloc(blocks * sizeof(unsigned int));
if(block_list == NULL)
MEM_ERROR();
cur_ptr += sizeof(inode);
SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks);
*uncompressed_file += inode.file_size;
(*file_count) ++;
for(i = 0; i < blocks; i++)
file_bytes +=
SQUASHFS_COMPRESSED_SIZE_BLOCK
(block_list[i]);
if(inode.fragment != SQUASHFS_INVALID_FRAG &&
inode.fragment >= sBlk->fragments) {
free(block_list);
goto corrupted;
}
add_file(start, inode.file_size, file_bytes,
block_list, blocks, inode.fragment,
inode.offset, frag_bytes);
cur_ptr += blocks * sizeof(unsigned int);
break;
}
case SQUASHFS_SYMLINK_TYPE:
case SQUASHFS_LSYMLINK_TYPE: {
struct squashfs_symlink_inode_header inode;
if(NO_INODE_BYTES(squashfs_symlink_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_SYMLINK_INODE_HEADER(cur_ptr, &inode);
(*sym_count) ++;
if (inode.inode_type == SQUASHFS_LSYMLINK_TYPE) {
if(NO_BYTES(inode.symlink_size +
sizeof(unsigned int)))
/* corrupted filesystem */
goto corrupted;
cur_ptr += sizeof(inode) + inode.symlink_size +
sizeof(unsigned int);
} else {
if(NO_BYTES(inode.symlink_size))
/* corrupted filesystem */
goto corrupted;
cur_ptr += sizeof(inode) + inode.symlink_size;
}
break;
}
case SQUASHFS_DIR_TYPE: {
struct squashfs_dir_inode_header dir_inode;
if(NO_INODE_BYTES(squashfs_dir_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_DIR_INODE_HEADER(cur_ptr, &dir_inode);
if(dir_inode.start_block < directory_start_block)
*uncompressed_directory += dir_inode.file_size;
(*dir_count) ++;
cur_ptr += sizeof(struct squashfs_dir_inode_header);
break;
}
case SQUASHFS_LDIR_TYPE: {
struct squashfs_ldir_inode_header dir_inode;
int i;
if(NO_INODE_BYTES(squashfs_ldir_inode_header))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_LDIR_INODE_HEADER(cur_ptr, &dir_inode);
if(dir_inode.start_block < directory_start_block)
*uncompressed_directory += dir_inode.file_size;
(*dir_count) ++;
cur_ptr += sizeof(struct squashfs_ldir_inode_header);
for(i = 0; i < dir_inode.i_count; i++) {
struct squashfs_dir_index index;
if(NO_BYTES(sizeof(index)))
/* corrupted filesystem */
goto corrupted;
SQUASHFS_SWAP_DIR_INDEX(cur_ptr, &index);
if(NO_BYTES(index.size + 1))
/* corrupted filesystem */
goto corrupted;
cur_ptr += sizeof(index) + index.size + 1;
}
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE:
if(NO_INODE_BYTES(squashfs_dev_inode_header))
/* corrupted filesystem */
goto corrupted;
(*dev_count) ++;
cur_ptr += sizeof(struct squashfs_dev_inode_header);
break;
case SQUASHFS_LBLKDEV_TYPE:
case SQUASHFS_LCHRDEV_TYPE:
if(NO_INODE_BYTES(squashfs_ldev_inode_header))
/* corrupted filesystem */
goto corrupted;
(*dev_count) ++;
cur_ptr += sizeof(struct squashfs_ldev_inode_header);
break;
case SQUASHFS_FIFO_TYPE:
if(NO_INODE_BYTES(squashfs_ipc_inode_header))
/* corrupted filesystem */
goto corrupted;
(*fifo_count) ++;
cur_ptr += sizeof(struct squashfs_ipc_inode_header);
break;
case SQUASHFS_LFIFO_TYPE:
if(NO_INODE_BYTES(squashfs_lipc_inode_header))
/* corrupted filesystem */
goto corrupted;
(*fifo_count) ++;
cur_ptr += sizeof(struct squashfs_lipc_inode_header);
break;
case SQUASHFS_SOCKET_TYPE:
if(NO_INODE_BYTES(squashfs_ipc_inode_header))
/* corrupted filesystem */
goto corrupted;
(*sock_count) ++;
cur_ptr += sizeof(struct squashfs_ipc_inode_header);
break;
case SQUASHFS_LSOCKET_TYPE:
if(NO_INODE_BYTES(squashfs_lipc_inode_header))
/* corrupted filesystem */
goto corrupted;
(*sock_count) ++;
cur_ptr += sizeof(struct squashfs_lipc_inode_header);
break;
default:
ERROR("Unknown inode type %d in scan_inode_table!\n",
base.inode_type);
goto corrupted;
}
}
printf("Read existing filesystem, %d inodes scanned\n", files);
return inode_table;
corrupted:
ERROR("scan_inode_table: filesystem corruption detected in "
"scanning metadata\n");
free(inode_table);
return NULL;
}
struct compressor *read_super(int fd, struct squashfs_super_block *sBlk, char *source)
{
int res, bytes = 0;
char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned));
res = read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block),
sBlk);
if(res == 0) {
ERROR("Can't find a SQUASHFS superblock on %s\n",
source);
ERROR("Wrong filesystem or filesystem is corrupted!\n");
goto failed_mount;
}
SQUASHFS_INSWAP_SUPER_BLOCK(sBlk);
if(sBlk->s_magic != SQUASHFS_MAGIC) {
if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP)
ERROR("Pre 4.0 big-endian filesystem on %s, appending"
" to this is unsupported\n", source);
else {
ERROR("Can't find a SQUASHFS superblock on %s\n",
source);
ERROR("Wrong filesystem or filesystem is corrupted!\n");
}
goto failed_mount;
}
/* Check the MAJOR & MINOR versions */
if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) {
if(sBlk->s_major < 4)
ERROR("Filesystem on %s is a SQUASHFS %d.%d filesystem."
" Appending\nto SQUASHFS %d.%d filesystems is "
"not supported. Please convert it to a "
"SQUASHFS 4 filesystem\n", source,
sBlk->s_major,
sBlk->s_minor, sBlk->s_major, sBlk->s_minor);
else
ERROR("Filesystem on %s is %d.%d, which is a later "
"filesystem version than I support\n",
source, sBlk->s_major, sBlk->s_minor);
goto failed_mount;
}
/* Check the compression type */
comp = lookup_compressor_id(sBlk->compression);
if(!comp->supported) {
ERROR("Filesystem on %s uses %s compression, this is "
"unsupported by this version\n", source, comp->name);
ERROR("Compressors available:\n");
display_compressors("", "");
goto failed_mount;
}
/*
* Read extended superblock information from disk.
*
* Read compressor specific options from disk if present, and pass
* to compressor to set compressor options.
*
* Note, if there's no compressor options present, the compressor
* is still called to set the default options (the defaults may have
* been changed by the user specifying options on the command
* line which need to be over-ridden).
*
* Compressor_extract_options is also used to ensure that
* we know how decompress a filesystem compressed with these
* compression options.
*/
if(SQUASHFS_COMP_OPTS(sBlk->flags)) {
bytes = read_block(fd, sizeof(*sBlk), NULL, 0, buffer);
if(bytes == 0) {
ERROR("Failed to read compressor options from append "
"filesystem\n");
ERROR("Filesystem corrupted?\n");
goto failed_mount;
}
}
res = compressor_extract_options(comp, sBlk->block_size, buffer, bytes);
if(res == -1) {
ERROR("Compressor failed to set compressor options\n");
goto failed_mount;
}
printf("Found a valid %sSQUASHFS superblock on %s.\n",
SQUASHFS_EXPORTABLE(sBlk->flags) ? "exportable " : "", source);
printf("\tCompression used %s\n", comp->name);
printf("\tInodes are %scompressed\n",
SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : "");
printf("\tData is %scompressed\n",
SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : "");
printf("\tFragments are %scompressed\n",
SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk->flags) ? "un" : "");
printf("\tXattrs are %scompressed\n",
SQUASHFS_UNCOMPRESSED_XATTRS(sBlk->flags) ? "un" : "");
printf("\tFragments are %spresent in the filesystem\n",
SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not " : "");
printf("\tAlways-use-fragments option is %sspecified\n",
SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not ");
printf("\tDuplicates are %sremoved\n",
SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not ");
printf("\tXattrs are %sstored\n",
SQUASHFS_NO_XATTRS(sBlk->flags) ? "not " : "");
printf("\tFilesystem size %.2f Kbytes (%.2f Mbytes)\n",
sBlk->bytes_used / 1024.0, sBlk->bytes_used
/ (1024.0 * 1024.0));
printf("\tBlock size %d\n", sBlk->block_size);
printf("\tNumber of fragments %d\n", sBlk->fragments);
printf("\tNumber of inodes %d\n", sBlk->inodes);
printf("\tNumber of ids %d\n", sBlk->no_ids);
TRACE("sBlk->inode_table_start %llx\n", sBlk->inode_table_start);
TRACE("sBlk->directory_table_start %llx\n",
sBlk->directory_table_start);
TRACE("sBlk->id_table_start %llx\n", sBlk->id_table_start);
TRACE("sBlk->fragment_table_start %llx\n", sBlk->fragment_table_start);
TRACE("sBlk->lookup_table_start %llx\n", sBlk->lookup_table_start);
TRACE("sBlk->xattr_id_table_start %llx\n", sBlk->xattr_id_table_start);
printf("\n");
return comp;
failed_mount:
return NULL;
}
unsigned char *squashfs_readdir(int fd, int root_entries,
unsigned int directory_start_block, int offset, int size,
unsigned int *last_directory_block, struct squashfs_super_block *sBlk,
void (push_directory_entry)(char *, squashfs_inode, int, int))
{
struct squashfs_dir_header dirh;
char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]
__attribute__ ((aligned));
struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
unsigned char *directory_table = NULL;
int byte, bytes = 0, dir_count;
long long start = sBlk->directory_table_start + directory_start_block,
last_start_block = start;
size += offset;
directory_table = malloc((size + SQUASHFS_METADATA_SIZE * 2 - 1) &
~(SQUASHFS_METADATA_SIZE - 1));
if(directory_table == NULL)
MEM_ERROR();
while(bytes < size) {
int expected = (size - bytes) >= SQUASHFS_METADATA_SIZE ?
SQUASHFS_METADATA_SIZE : 0;
TRACE("squashfs_readdir: reading block 0x%llx, bytes read so "
"far %d\n", start, bytes);
last_start_block = start;
byte = read_block(fd, start, &start, expected, directory_table + bytes);
if(byte == 0) {
ERROR("Failed to read directory\n");
ERROR("Filesystem corrupted?\n");
free(directory_table);
return NULL;
}
bytes += byte;
}
if(!root_entries)
goto all_done;
bytes = offset;
while(bytes < size) {
SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh);
dir_count = dirh.count + 1;
/* dir_count should never be larger than SQUASHFS_DIR_COUNT */
if(dir_count > SQUASHFS_DIR_COUNT) {
ERROR("File system corrupted: too many entries in directory\n");
free(directory_table);
return NULL;
}
TRACE("squashfs_readdir: Read directory header @ byte position "
"0x%x, 0x%x directory entries\n", bytes, dir_count);
bytes += sizeof(dirh);
while(dir_count--) {
SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire);
bytes += sizeof(*dire);
/* size should never be SQUASHFS_NAME_LEN or larger */
if(dire->size >= SQUASHFS_NAME_LEN) {
ERROR("File system corrupted: filename too long\n");
free(directory_table);
return NULL;
}
memcpy(dire->name, directory_table + bytes,
dire->size + 1);
dire->name[dire->size + 1] = '\0';
TRACE("squashfs_readdir: pushing directory entry %s, "
"inode %x:%x, type 0x%x\n", dire->name,
dirh.start_block, dire->offset, dire->type);
push_directory_entry(dire->name,
SQUASHFS_MKINODE(dirh.start_block,
dire->offset), dirh.inode_number +
dire->inode_number, dire->type);
bytes += dire->size + 1;
}
}
all_done:
*last_directory_block = (unsigned int) last_start_block -
sBlk->directory_table_start;
return directory_table;
}
unsigned int *read_id_table(int fd, struct squashfs_super_block *sBlk)
{
int indexes = SQUASHFS_ID_BLOCKS(sBlk->no_ids);
long long index[indexes];
int bytes = SQUASHFS_ID_BYTES(sBlk->no_ids);
unsigned int *id_table;
int res, i;
id_table = malloc(bytes);
if(id_table == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, sBlk->id_table_start,
SQUASHFS_ID_BLOCK_BYTES(sBlk->no_ids), index);
if(res == 0) {
ERROR("Failed to read id table index\n");
ERROR("Filesystem corrupted?\n");
free(id_table);
return NULL;
}
SQUASHFS_INSWAP_ID_BLOCKS(index, indexes);
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, index[i], NULL, expected,
((unsigned char *) id_table) +
(i * SQUASHFS_METADATA_SIZE));
TRACE("Read id table block %d, from 0x%llx, length %d\n", i,
index[i], length);
if(length == 0) {
ERROR("Failed to read id table block %d, from 0x%llx, "
"length %d\n", i, index[i], length);
ERROR("Filesystem corrupted?\n");
free(id_table);
return NULL;
}
}
SQUASHFS_INSWAP_INTS(id_table, sBlk->no_ids);
for(i = 0; i < sBlk->no_ids; i++) {
TRACE("Adding id %d to id tables\n", id_table[i]);
create_id(id_table[i]);
}
return id_table;
}
struct squashfs_fragment_entry *read_fragment_table(int fd, struct squashfs_super_block *sBlk)
{
int res, i;
int bytes = SQUASHFS_FRAGMENT_BYTES(sBlk->fragments);
int indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments);
long long fragment_table_index[indexes];
struct squashfs_fragment_entry *fragment_table;
TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
"from 0x%llx\n", sBlk->fragments, indexes,
sBlk->fragment_table_start);
fragment_table = malloc(bytes);
if(fragment_table == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, sBlk->fragment_table_start,
SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments),
fragment_table_index);
if(res == 0) {
ERROR("Failed to read fragment table index\n");
ERROR("Filesystem corrupted?\n");
free(fragment_table);
return NULL;
}
SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index, indexes);
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, fragment_table_index[i], NULL,
expected, ((unsigned char *) fragment_table) +
(i * SQUASHFS_METADATA_SIZE));
TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
i, fragment_table_index[i], length);
if(length == 0) {
ERROR("Failed to read fragment table block %d, from "
"0x%llx, length %d\n", i,
fragment_table_index[i], length);
ERROR("Filesystem corrupted?\n");
free(fragment_table);
return NULL;
}
}
for(i = 0; i < sBlk->fragments; i++)
SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]);
return fragment_table;
}
squashfs_inode *read_inode_lookup_table(int fd, struct squashfs_super_block *sBlk)
{
int lookup_bytes = SQUASHFS_LOOKUP_BYTES(sBlk->inodes);
int indexes = SQUASHFS_LOOKUP_BLOCKS(sBlk->inodes);
long long index[indexes];
int res, i;
squashfs_inode *inode_lookup_table;
inode_lookup_table = malloc(lookup_bytes);
if(inode_lookup_table == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, sBlk->lookup_table_start,
SQUASHFS_LOOKUP_BLOCK_BYTES(sBlk->inodes), index);
if(res == 0) {
ERROR("Failed to read inode lookup table index\n");
ERROR("Filesystem corrupted?\n");
free(inode_lookup_table);
return NULL;
}
SQUASHFS_INSWAP_LONG_LONGS(index, indexes);
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
lookup_bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, index[i], NULL, expected,
((unsigned char *) inode_lookup_table) +
(i * SQUASHFS_METADATA_SIZE));
TRACE("Read inode lookup table block %d, from 0x%llx, length "
"%d\n", i, index[i], length);
if(length == 0) {
ERROR("Failed to read inode lookup table block %d, "
"from 0x%llx, length %d\n", i, index[i],
length);
ERROR("Filesystem corrupted?\n");
free(inode_lookup_table);
return NULL;
}
}
SQUASHFS_INSWAP_LONG_LONGS(inode_lookup_table, sBlk->inodes);
return inode_lookup_table;
}
long long read_filesystem(char *root_name, int fd, struct squashfs_super_block *sBlk,
char **cinode_table, char **data_cache, char **cdirectory_table,
char **directory_data_cache, unsigned int *last_directory_block,
unsigned int *inode_dir_offset, unsigned int *inode_dir_file_size,
unsigned int *root_inode_size, unsigned int *inode_dir_start_block,
int *file_count, int *sym_count, int *dev_count, int *dir_count,
int *fifo_count, int *sock_count, long long *uncompressed_file,
unsigned int *uncompressed_inode, unsigned int *uncompressed_directory,
unsigned int *inode_dir_inode_number,
unsigned int *inode_dir_parent_inode,
void (push_directory_entry)(char *, squashfs_inode, int, int),
struct squashfs_fragment_entry **fragment_table,
squashfs_inode **inode_lookup_table)
{
unsigned char *inode_table = NULL, *directory_table = NULL;
long long start = sBlk->inode_table_start;
long long end = sBlk->directory_table_start;
long long root_inode_start = start +
SQUASHFS_INODE_BLK(sBlk->root_inode);
unsigned int root_inode_offset =
SQUASHFS_INODE_OFFSET(sBlk->root_inode);
unsigned int root_inode_block;
union squashfs_inode_header inode;
unsigned int *id_table = NULL;
int res;
printf("Scanning existing filesystem...\n");
if(get_xattrs(fd, sBlk) == 0)
goto error;
if(sBlk->fragments > 0) {
*fragment_table = read_fragment_table(fd, sBlk);
if(*fragment_table == NULL)
goto error;
}
if(sBlk->lookup_table_start != SQUASHFS_INVALID_BLK) {
*inode_lookup_table = read_inode_lookup_table(fd, sBlk);
if(*inode_lookup_table == NULL)
goto error;
}
id_table = read_id_table(fd, sBlk);
if(id_table == NULL)
goto error;
inode_table = scan_inode_table(fd, start, end, root_inode_start,
root_inode_offset, sBlk, &inode, &root_inode_block,
root_inode_size, uncompressed_file, uncompressed_directory,
file_count, sym_count, dev_count, dir_count, fifo_count,
sock_count, id_table);
if(inode_table == NULL)
goto error;
*uncompressed_inode = root_inode_block;
if(inode.base.inode_type == SQUASHFS_DIR_TYPE ||
inode.base.inode_type == SQUASHFS_LDIR_TYPE) {
if(inode.base.inode_type == SQUASHFS_DIR_TYPE) {
*inode_dir_start_block = inode.dir.start_block;
*inode_dir_offset = inode.dir.offset;
*inode_dir_file_size = inode.dir.file_size - 3;
*inode_dir_inode_number = inode.dir.inode_number;
*inode_dir_parent_inode = inode.dir.parent_inode;
} else {
*inode_dir_start_block = inode.ldir.start_block;
*inode_dir_offset = inode.ldir.offset;
*inode_dir_file_size = inode.ldir.file_size - 3;
*inode_dir_inode_number = inode.ldir.inode_number;
*inode_dir_parent_inode = inode.ldir.parent_inode;
}
directory_table = squashfs_readdir(fd, !root_name,
*inode_dir_start_block, *inode_dir_offset,
*inode_dir_file_size, last_directory_block, sBlk,
push_directory_entry);
if(directory_table == NULL)
goto error;
root_inode_start -= start;
*cinode_table = malloc(root_inode_start);
if(*cinode_table == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, start, root_inode_start, *cinode_table);
if(res == 0) {
ERROR("Failed to read inode table\n");
ERROR("Filesystem corrupted?\n");
goto error;
}
*cdirectory_table = malloc(*last_directory_block);
if(*cdirectory_table == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, sBlk->directory_table_start,
*last_directory_block, *cdirectory_table);
if(res == 0) {
ERROR("Failed to read directory table\n");
ERROR("Filesystem corrupted?\n");
goto error;
}
*data_cache = malloc(root_inode_offset + *root_inode_size);
if(*data_cache == NULL)
MEM_ERROR();
memcpy(*data_cache, inode_table + root_inode_block,
root_inode_offset + *root_inode_size);
*directory_data_cache = malloc(*inode_dir_offset +
*inode_dir_file_size);
if(*directory_data_cache == NULL)
MEM_ERROR();
memcpy(*directory_data_cache, directory_table,
*inode_dir_offset + *inode_dir_file_size);
free(id_table);
free(inode_table);
free(directory_table);
return sBlk->inode_table_start;
}
error:
free(id_table);
free(inode_table);
free(directory_table);
return 0;
}

View File

@@ -0,0 +1,34 @@
#ifndef READ_FS_H
#define READ_FS_H
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* read_fs.h
*
*/
extern struct compressor *read_super(int, struct squashfs_super_block *,
char *);
extern long long read_filesystem(char *, int, struct squashfs_super_block *,
char **, char **, char **, char **, unsigned int *, unsigned int *,
unsigned int *, unsigned int *, unsigned int *, int *, int *, int *, int *,
int *, int *, long long *, unsigned int *, unsigned int *, unsigned int *,
unsigned int *, void (push_directory_entry)(char *, squashfs_inode, int, int),
struct squashfs_fragment_entry **, squashfs_inode **);
#endif

View File

@@ -0,0 +1,428 @@
/*
* Read a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2010, 2012, 2013, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* read_xattrs.c
*/
/*
* Common xattr read code shared between mksquashfs and unsquashfs
*/
#define TRUE 1
#define FALSE 0
#include <stdio.h>
#include <string.h>
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#include "squashfs_fs.h"
#include "squashfs_swap.h"
#include "xattr.h"
#include "error.h"
#include <stdlib.h>
extern int read_fs_bytes(int, long long, int, void *);
extern int read_block(int, long long, long long *, int, void *);
static struct hash_entry {
long long start;
unsigned int offset;
struct hash_entry *next;
} *hash_table[65536];
static struct squashfs_xattr_id *xattr_ids;
static void *xattrs = NULL;
static long long xattr_table_start;
/*
* Prefix lookup table, storing mapping to/from prefix string and prefix id
*/
struct prefix prefix_table[] = {
{ "user.", SQUASHFS_XATTR_USER },
{ "trusted.", SQUASHFS_XATTR_TRUSTED },
{ "security.", SQUASHFS_XATTR_SECURITY },
{ "", -1 }
};
/*
* store mapping from location of compressed block in fs ->
* location of uncompressed block in memory
*/
static void save_xattr_block(long long start, int offset)
{
struct hash_entry *hash_entry = malloc(sizeof(*hash_entry));
int hash = start & 0xffff;
TRACE("save_xattr_block: start %lld, offset %d\n", start, offset);
if(hash_entry == NULL)
MEM_ERROR();
hash_entry->start = start;
hash_entry->offset = offset;
hash_entry->next = hash_table[hash];
hash_table[hash] = hash_entry;
}
/*
* map from location of compressed block in fs ->
* location of uncompressed block in memory
*/
static int get_xattr_block(long long start)
{
int hash = start & 0xffff;
struct hash_entry *hash_entry = hash_table[hash];
for(; hash_entry; hash_entry = hash_entry->next)
if(hash_entry->start == start)
break;
TRACE("get_xattr_block: start %lld, offset %d\n", start,
hash_entry ? hash_entry->offset : -1);
return hash_entry ? hash_entry->offset : -1;
}
/*
* construct the xattr_list entry from the fs xattr, including
* mapping name and prefix into a full name
*/
static int read_xattr_entry(struct xattr_list *xattr,
struct squashfs_xattr_entry *entry, void *name)
{
int i, len, type = entry->type & XATTR_PREFIX_MASK;
for(i = 0; prefix_table[i].type != -1; i++)
if(prefix_table[i].type == type)
break;
if(prefix_table[i].type == -1) {
ERROR("read_xattr_entry: Unrecognised xattr type %d\n", type);
return 0;
}
len = strlen(prefix_table[i].prefix);
xattr->full_name = malloc(len + entry->size + 1);
if(xattr->full_name == NULL)
MEM_ERROR();
memcpy(xattr->full_name, prefix_table[i].prefix, len);
memcpy(xattr->full_name + len, name, entry->size);
xattr->full_name[len + entry->size] = '\0';
xattr->name = xattr->full_name + len;
xattr->size = entry->size;
xattr->type = type;
return 1;
}
/*
* Read and decompress the xattr id table and the xattr metadata.
* This is cached in memory for later use by get_xattr()
*/
int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk, int flag, long long *table_start)
{
/*
* Note on overflow limits:
* Size of ids (id_table.xattr_ids) is 2^32 (unsigned int)
* Max size of bytes is 2^32*16 or 2^36
* Max indexes is (2^32*16)/8K or 2^23
* Max index_bytes is ((2^32*16)/8K)*8 or 2^26 or 64M
*/
int res, i, indexes, index_bytes;
unsigned int ids;
long long bytes;
long long *index, start, end;
struct squashfs_xattr_table id_table;
TRACE("read_xattrs_from_disk\n");
if(sBlk->xattr_id_table_start == SQUASHFS_INVALID_BLK)
return SQUASHFS_INVALID_BLK;
/*
* Read xattr id table, containing start of xattr metadata and the
* number of xattrs in the file system
*/
res = read_fs_bytes(fd, sBlk->xattr_id_table_start, sizeof(id_table),
&id_table);
if(res == 0)
return 0;
SQUASHFS_INSWAP_XATTR_TABLE(&id_table);
/*
* Compute index table values
*/
ids = id_table.xattr_ids;
xattr_table_start = id_table.xattr_table_start;
index_bytes = SQUASHFS_XATTR_BLOCK_BYTES((long long) ids);
indexes = SQUASHFS_XATTR_BLOCKS((long long) ids);
/*
* The size of the index table (index_bytes) should match the
* table start and end points
*/
if(index_bytes != (sBlk->bytes_used - (sBlk->xattr_id_table_start + sizeof(id_table)))) {
ERROR("read_xattrs_from_disk: Bad xattr_ids count in super block\n");
return 0;
}
/*
* id_table.xattr_table_start stores the start of the compressed xattr
* metadata blocks. This by definition is also the end of the previous
* filesystem table - the id lookup table.
*/
if(table_start != NULL)
*table_start = id_table.xattr_table_start;
/*
* If flag is set then return once we've read the above
* table_start. That value is necessary for sanity checking,
* but we don't actually want to extract the xattrs, and so
* stop here.
*/
if(flag)
return id_table.xattr_ids;
/*
* Allocate and read the index to the xattr id table metadata
* blocks
*/
index = malloc(index_bytes);
if(index == NULL)
MEM_ERROR();
res = read_fs_bytes(fd, sBlk->xattr_id_table_start + sizeof(id_table),
index_bytes, index);
if(res ==0)
goto failed1;
SQUASHFS_INSWAP_LONG_LONGS(index, indexes);
/*
* Allocate enough space for the uncompressed xattr id table, and
* read and decompress it
*/
bytes = SQUASHFS_XATTR_BYTES((long long) ids);
xattr_ids = malloc(bytes);
if(xattr_ids == NULL)
MEM_ERROR();
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, index[i], NULL, expected,
((unsigned char *) xattr_ids) +
((long long) i * SQUASHFS_METADATA_SIZE));
TRACE("Read xattr id table block %d, from 0x%llx, length "
"%d\n", i, index[i], length);
if(length == 0) {
ERROR("Failed to read xattr id table block %d, "
"from 0x%llx, length %d\n", i, index[i],
length);
goto failed2;
}
}
/*
* Read and decompress the xattr metadata
*
* Note the first xattr id table metadata block is immediately after
* the last xattr metadata block, so we can use index[0] to work out
* the end of the xattr metadata
*/
start = xattr_table_start;
end = index[0];
for(i = 0; start < end; i++) {
int length;
xattrs = realloc(xattrs, (i + 1) * SQUASHFS_METADATA_SIZE);
if(xattrs == NULL)
MEM_ERROR();
/* store mapping from location of compressed block in fs ->
* location of uncompressed block in memory */
save_xattr_block(start, i * SQUASHFS_METADATA_SIZE);
length = read_block(fd, start, &start, 0,
((unsigned char *) xattrs) +
(i * SQUASHFS_METADATA_SIZE));
TRACE("Read xattr block %d, length %d\n", i, length);
if(length == 0) {
ERROR("Failed to read xattr block %d\n", i);
goto failed3;
}
/*
* If this is not the last metadata block in the xattr metadata
* then it should be SQUASHFS_METADATA_SIZE in size.
* Note, we can't use expected in read_block() above for this
* because we don't know if this is the last block until
* after reading.
*/
if(start != end && length != SQUASHFS_METADATA_SIZE) {
ERROR("Xattr block %d should be %d bytes in length, "
"it is %d bytes\n", i, SQUASHFS_METADATA_SIZE,
length);
goto failed3;
}
}
/* swap if necessary the xattr id entries */
for(i = 0; i < ids; i++)
SQUASHFS_INSWAP_XATTR_ID(&xattr_ids[i]);
free(index);
return ids;
failed3:
free(xattrs);
failed2:
free(xattr_ids);
failed1:
free(index);
return 0;
}
void free_xattr(struct xattr_list *xattr_list, int count)
{
int i;
for(i = 0; i < count; i++)
free(xattr_list[i].full_name);
free(xattr_list);
}
/*
* Construct and return the list of xattr name:value pairs for the passed xattr
* id
*
* There are two users for get_xattr(), Mksquashfs uses it to read the
* xattrs from the filesystem on appending, and Unsquashfs uses it
* to retrieve the xattrs for writing to disk.
*
* Unfortunately, the two users disagree on what to do with unknown
* xattr prefixes, Mksquashfs wants to treat this as fatal otherwise
* this will cause xattrs to be be lost on appending. Unsquashfs
* on the otherhand wants to retrieve the xattrs which are known and
* to ignore the rest, this allows Unsquashfs to cope more gracefully
* with future versions which may have unknown xattrs, as long as the
* general xattr structure is adhered to, Unsquashfs should be able
* to safely ignore unknown xattrs, and to write the ones it knows about,
* this is better than completely refusing to retrieve all the xattrs.
*
* So return an error flag if any unrecognised types were found.
*/
struct xattr_list *get_xattr(int i, unsigned int *count, int *failed)
{
long long start;
struct xattr_list *xattr_list = NULL;
unsigned int offset;
void *xptr;
int j, n, res = 1;
TRACE("get_xattr\n");
if(xattr_ids[i].count == 0) {
ERROR("get_xattr: xattr count unexpectedly 0 - corrupt fs?\n");
*failed = TRUE;
*count = 0;
return NULL;
} else
*failed = FALSE;
start = SQUASHFS_XATTR_BLK(xattr_ids[i].xattr) + xattr_table_start;
offset = SQUASHFS_XATTR_OFFSET(xattr_ids[i].xattr);
xptr = xattrs + get_xattr_block(start) + offset;
TRACE("get_xattr: xattr_id %d, count %d, start %lld, offset %d\n", i,
xattr_ids[i].count, start, offset);
for(j = 0, n = 0; n < xattr_ids[i].count; n++) {
struct squashfs_xattr_entry entry;
struct squashfs_xattr_val val;
if(res != 0) {
xattr_list = realloc(xattr_list, (j + 1) *
sizeof(struct xattr_list));
if(xattr_list == NULL)
MEM_ERROR();
}
SQUASHFS_SWAP_XATTR_ENTRY(xptr, &entry);
xptr += sizeof(entry);
res = read_xattr_entry(&xattr_list[j], &entry, xptr);
if(res == 0) {
/* unknown type, skip, and set error flag */
xptr += entry.size;
SQUASHFS_SWAP_XATTR_VAL(xptr, &val);
xptr += sizeof(val) + val.vsize;
*failed = TRUE;
continue;
}
xptr += entry.size;
TRACE("get_xattr: xattr %d, type %d, size %d, name %s\n", j,
entry.type, entry.size, xattr_list[j].full_name);
if(entry.type & SQUASHFS_XATTR_VALUE_OOL) {
long long xattr;
void *ool_xptr;
xptr += sizeof(val);
SQUASHFS_SWAP_LONG_LONGS(xptr, &xattr, 1);
xptr += sizeof(xattr);
start = SQUASHFS_XATTR_BLK(xattr) + xattr_table_start;
offset = SQUASHFS_XATTR_OFFSET(xattr);
ool_xptr = xattrs + get_xattr_block(start) + offset;
SQUASHFS_SWAP_XATTR_VAL(ool_xptr, &val);
xattr_list[j].value = ool_xptr + sizeof(val);
} else {
SQUASHFS_SWAP_XATTR_VAL(xptr, &val);
xattr_list[j].value = xptr + sizeof(val);
xptr += sizeof(val) + val.vsize;
}
TRACE("get_xattr: xattr %d, vsize %d\n", j, val.vsize);
xattr_list[j++].vsize = val.vsize;
}
*count = j;
return xattr_list;
}

View File

@@ -0,0 +1,168 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* restore.c
*/
#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "caches-queues-lists.h"
#include "squashfs_fs.h"
#include "mksquashfs.h"
#include "error.h"
#include "progressbar.h"
#include "info.h"
#define FALSE 0
#define TRUE 1
extern pthread_t reader_thread, writer_thread, main_thread, order_thread;
extern pthread_t *deflator_thread, *frag_deflator_thread, *frag_thread;
extern struct queue *to_deflate, *to_writer, *to_frag, *to_process_frag;
extern struct seq_queue *to_main, *to_order;
extern void restorefs();
extern int processors;
extern int reproducible;
static int interrupted = 0;
static pthread_t restore_thread;
void *restore_thrd(void *arg)
{
sigset_t sigmask, old_mask;
int i, sig;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGINT);
sigaddset(&sigmask, SIGTERM);
sigaddset(&sigmask, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask);
while(1) {
sigwait(&sigmask, &sig);
if((sig == SIGINT || sig == SIGTERM) && !interrupted) {
ERROR("Interrupting will restore original "
"filesystem!\n");
ERROR("Interrupt again to quit\n");
interrupted = TRUE;
continue;
}
/* kill main thread/worker threads and restore */
set_progressbar_state(FALSE);
disable_info();
/* first kill the reader thread */
pthread_cancel(reader_thread);
pthread_join(reader_thread, NULL);
/*
* then flush the reader to deflator thread(s) output queue.
* The deflator thread(s) will idle
*/
queue_flush(to_deflate);
/* now kill the deflator thread(s) */
for(i = 0; i < processors; i++)
pthread_cancel(deflator_thread[i]);
for(i = 0; i < processors; i++)
pthread_join(deflator_thread[i], NULL);
/*
* then flush the reader to process fragment thread(s) output
* queue. The process fragment thread(s) will idle
*/
queue_flush(to_process_frag);
/* now kill the process fragment thread(s) */
for(i = 0; i < processors; i++)
pthread_cancel(frag_thread[i]);
for(i = 0; i < processors; i++)
pthread_join(frag_thread[i], NULL);
/*
* then flush the reader/deflator/process fragment to main
* thread output queue. The main thread will idle
*/
seq_queue_flush(to_main);
/* now kill the main thread */
pthread_cancel(main_thread);
pthread_join(main_thread, NULL);
/* then flush the main thread to fragment deflator thread(s)
* queue. The fragment deflator thread(s) will idle
*/
queue_flush(to_frag);
/* now kill the fragment deflator thread(s) */
for(i = 0; i < processors; i++)
pthread_cancel(frag_deflator_thread[i]);
for(i = 0; i < processors; i++)
pthread_join(frag_deflator_thread[i], NULL);
if(reproducible) {
/* then flush the fragment deflator_threads(s)
* to frag orderer thread. The frag orderer
* thread will idle
*/
seq_queue_flush(to_order);
/* now kill the frag orderer thread */
pthread_cancel(order_thread);
pthread_join(order_thread, NULL);
}
/*
* then flush the main thread/fragment deflator thread(s)
* to writer thread queue. The writer thread will idle
*/
queue_flush(to_writer);
/* now kill the writer thread */
pthread_cancel(writer_thread);
pthread_join(writer_thread, NULL);
TRACE("All threads cancelled\n");
restorefs();
}
}
pthread_t *init_restore_thread()
{
pthread_create(&restore_thread, NULL, restore_thrd, NULL);
return &restore_thread;
}

View File

@@ -0,0 +1,28 @@
#ifndef RESTORE_H
#define RESTORE_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* restore.h
*/
extern pthread_t *init_restore_thread();
#endif

View File

@@ -0,0 +1,363 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012,
* 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* sort.c
*/
#define TRUE 1
#define FALSE 0
#define MAX_LINE 16384
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "squashfs_fs.h"
#include "mksquashfs.h"
#include "sort.h"
#include "error.h"
#include "progressbar.h"
int mkisofs_style = -1;
struct sort_info {
dev_t st_dev;
ino_t st_ino;
int priority;
struct sort_info *next;
};
struct sort_info *sort_info_list[65536];
struct priority_entry *priority_list[65536];
extern int silent;
extern void write_file(squashfs_inode *inode, struct dir_ent *dir_ent,
int *c_size);
extern char *pathname(struct dir_ent *dir_ent);
void add_priority_list(struct dir_ent *dir, int priority)
{
struct priority_entry *new_priority_entry;
priority += 32768;
new_priority_entry = malloc(sizeof(struct priority_entry));
if(new_priority_entry == NULL)
MEM_ERROR();
new_priority_entry->dir = dir;;
new_priority_entry->next = priority_list[priority];
priority_list[priority] = new_priority_entry;
}
int get_priority(char *filename, struct stat *buf, int priority)
{
int hash = buf->st_ino & 0xffff;
struct sort_info *s;
for(s = sort_info_list[hash]; s; s = s->next)
if((s->st_dev == buf->st_dev) && (s->st_ino == buf->st_ino)) {
TRACE("returning priority %d (%s)\n", s->priority,
filename);
return s->priority;
}
TRACE("returning priority %d (%s)\n", priority, filename);
return priority;
}
#define ADD_ENTRY(buf, priority) {\
int hash = buf.st_ino & 0xffff;\
struct sort_info *s;\
if((s = malloc(sizeof(struct sort_info))) == NULL) \
MEM_ERROR(); \
s->st_dev = buf.st_dev;\
s->st_ino = buf.st_ino;\
s->priority = priority;\
s->next = sort_info_list[hash];\
sort_info_list[hash] = s;\
}
int add_sort_list(char *path, int priority, int source, char *source_path[])
{
int i, n;
struct stat buf;
TRACE("add_sort_list: filename %s, priority %d\n", path, priority);
if(strlen(path) > 1 && strcmp(path + strlen(path) - 2, "/*") == 0)
path[strlen(path) - 2] = '\0';
TRACE("add_sort_list: filename %s, priority %d\n", path, priority);
re_read:
if(path[0] == '/' || strncmp(path, "./", 2) == 0 ||
strncmp(path, "../", 3) == 0 || mkisofs_style == 1) {
if(lstat(path, &buf) == -1)
goto error;
TRACE("adding filename %s, priority %d, st_dev %d, st_ino "
"%lld\n", path, priority, (int) buf.st_dev,
(long long) buf.st_ino);
ADD_ENTRY(buf, priority);
return TRUE;
}
for(i = 0, n = 0; i < source; i++) {
char *filename;
int res = asprintf(&filename, "%s/%s", source_path[i], path);
if(res == -1)
BAD_ERROR("asprintf failed in add_sort_list\n");
res = lstat(filename, &buf);
free(filename);
if(res == -1) {
if(!(errno == ENOENT || errno == ENOTDIR))
goto error;
continue;
}
ADD_ENTRY(buf, priority);
n ++;
}
if(n == 0 && mkisofs_style == -1 && lstat(path, &buf) != -1) {
ERROR("WARNING: Mkisofs style sortlist detected! This is "
"supported but please\n");
ERROR("convert to mksquashfs style sortlist! A sortlist entry");
ERROR(" should be\neither absolute (starting with ");
ERROR("'/') start with './' or '../' (taken to be\nrelative to "
"$PWD), otherwise it ");
ERROR("is assumed the entry is relative to one\nof the source "
"directories, i.e. with ");
ERROR("\"mksquashfs test test.sqsh\",\nthe sortlist ");
ERROR("entry \"file\" is assumed to be inside the directory "
"test.\n\n");
mkisofs_style = 1;
goto re_read;
}
mkisofs_style = 0;
if(n == 1)
return TRUE;
if(n > 1) {
ERROR(" Ambiguous sortlist entry \"%s\"\n\nIt maps to more "
"than one source entry! Please use an absolute path."
"\n", path);
return FALSE;
}
error:
ERROR_START("Cannot stat sortlist entry \"%s\"\n", path);
ERROR("This is probably because you're using the wrong file\n");
ERROR("path relative to the source directories.");
ERROR_EXIT(" Ignoring");
/*
* Historical note
* Failure to stat a sortlist entry is deliberately ignored, even
* though it is an error. Squashfs release 2.2 changed the behaviour
* to treat it as a fatal error, but it was changed back to
* the original behaviour to ignore it in release 2.2-r2 following
* feedback from users at the time.
*/
return TRUE;
}
void generate_file_priorities(struct dir_info *dir, int priority,
struct stat *buf)
{
struct dir_ent *dir_ent = dir->list;
priority = get_priority(dir->pathname, buf, priority);
for(; dir_ent; dir_ent = dir_ent->next) {
struct stat *buf = &dir_ent->inode->buf;
if(dir_ent->inode->root_entry)
continue;
switch(buf->st_mode & S_IFMT) {
case S_IFREG:
add_priority_list(dir_ent,
get_priority(pathname(dir_ent), buf,
priority));
break;
case S_IFDIR:
generate_file_priorities(dir_ent->dir,
priority, buf);
break;
}
}
}
int read_sort_file(char *filename, int source, char *source_path[])
{
FILE *fd;
char line_buffer[MAX_LINE + 1]; /* overflow safe */
char sort_filename[MAX_LINE + 1]; /* overflow safe */
char *line, *name;
int n, priority, res;
if((fd = fopen(filename, "r")) == NULL) {
ERROR("Failed to open sort file \"%s\" because %s\n",
filename, strerror(errno));
return FALSE;
}
while(fgets(line = line_buffer, MAX_LINE + 1, fd) != NULL) {
int len = strlen(line);
if(len == MAX_LINE && line[len - 1] != '\n') {
/* line too large */
ERROR("Line too long when reading "
"sort file \"%s\", larger than %d "
"bytes\n", filename, MAX_LINE);
goto failed;
}
/*
* Remove '\n' terminator if it exists (the last line
* in the file may not be '\n' terminated)
*/
if(len && line[len - 1] == '\n')
line[len - 1] = '\0';
/* Skip any leading whitespace */
while(isspace(*line))
line ++;
/* if comment line, skip */
if(*line == '#')
continue;
/*
* Scan for filename, don't use sscanf() and "%s" because
* that can't handle filenames with spaces
*/
for(name = sort_filename; !isspace(*line) && *line != '\0';) {
if(*line == '\\') {
line ++;
if (*line == '\0')
break;
}
*name ++ = *line ++;
}
*name = '\0';
/*
* if filename empty, then line was empty of anything but
* whitespace or a backslash character. Skip empy lines
*/
if(sort_filename[0] == '\0')
continue;
/*
* Scan the rest of the line, we expect a decimal number
* which is the filename priority
*/
errno = 0;
res = sscanf(line, "%d%n", &priority, &n);
if((res < 1 || errno) && errno != ERANGE) {
if(errno == 0)
/* No error, assume EOL or match failure */
ERROR("Sort file \"%s\", can't find priority "
"in entry \"%s\", EOL or match "
"failure\n", filename, line_buffer);
else
/* Some other failure not ERANGE */
ERROR("Sscanf failed reading sort file \"%s\" "
"because %s\n", filename,
strerror(errno));
goto failed;
} else if((errno == ERANGE) ||
(priority < -32768 || priority > 32767)) {
ERROR("Sort file \"%s\", entry \"%s\" has priority "
"outside range of -32767:32768.\n", filename,
line_buffer);
goto failed;
}
/* Skip any trailing whitespace */
line += n;
while(isspace(*line))
line ++;
if(*line != '\0') {
ERROR("Sort file \"%s\", trailing characters after "
"priority in entry \"%s\"\n", filename,
line_buffer);
goto failed;
}
res = add_sort_list(sort_filename, priority, source,
source_path);
if(res == FALSE)
goto failed;
}
if(ferror(fd)) {
ERROR("Reading sort file \"%s\" failed because %s\n", filename,
strerror(errno));
goto failed;
}
fclose(fd);
return TRUE;
failed:
fclose(fd);
return FALSE;
}
void sort_files_and_write(struct dir_info *dir)
{
int i;
struct priority_entry *entry;
squashfs_inode inode;
int duplicate_file;
for(i = 65535; i >= 0; i--)
for(entry = priority_list[i]; entry; entry = entry->next) {
TRACE("%d: %s\n", i - 32768, pathname(entry->dir));
if(entry->dir->inode->inode == SQUASHFS_INVALID_BLK) {
write_file(&inode, entry->dir, &duplicate_file);
INFO("file %s, uncompressed size %lld bytes %s"
"\n", pathname(entry->dir),
(long long)
entry->dir->inode->buf.st_size,
duplicate_file ? "DUPLICATE" : "");
entry->dir->inode->inode = inode;
entry->dir->inode->type = SQUASHFS_FILE_TYPE;
} else
INFO("file %s, uncompressed size %lld bytes "
"LINK\n", pathname(entry->dir),
(long long)
entry->dir->inode->buf.st_size);
}
}

View File

@@ -0,0 +1,37 @@
#ifndef SORT_H
#define SORT_H
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* sort.h
*/
struct priority_entry {
struct dir_ent *dir;
struct priority_entry *next;
};
extern int read_sort_file(char *, int, char *[]);
extern void sort_files_and_write(struct dir_info *);
extern void generate_file_priorities(struct dir_info *, int priority,
struct stat *);
extern struct priority_entry *priority_list[65536];
#endif

View File

@@ -0,0 +1,834 @@
#ifndef SQUASHFS_COMPAT
#define SQUASHFS_COMPAT
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* squashfs_compat.h
*/
/*
* definitions for structures on disk - layout 3.x
*/
#define SQUASHFS_CHECK 2
#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_CHECK)
/* Max number of uids and gids */
#define SQUASHFS_UIDS 256
#define SQUASHFS_GUIDS 255
struct squashfs_super_block_3 {
unsigned int s_magic;
unsigned int inodes;
unsigned int bytes_used_2;
unsigned int uid_start_2;
unsigned int guid_start_2;
unsigned int inode_table_start_2;
unsigned int directory_table_start_2;
unsigned int s_major:16;
unsigned int s_minor:16;
unsigned int block_size_1:16;
unsigned int block_log:16;
unsigned int flags:8;
unsigned int no_uids:8;
unsigned int no_guids:8;
int mkfs_time /* time of filesystem creation */;
squashfs_inode root_inode;
unsigned int block_size;
unsigned int fragments;
unsigned int fragment_table_start_2;
long long bytes_used;
long long uid_start;
long long guid_start;
long long inode_table_start;
long long directory_table_start;
long long fragment_table_start;
long long lookup_table_start;
} __attribute__ ((packed));
struct squashfs_dir_index_3 {
unsigned int index;
unsigned int start_block;
unsigned char size;
unsigned char name[0];
} __attribute__ ((packed));
struct squashfs_base_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
} __attribute__ ((packed));
struct squashfs_ipc_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
} __attribute__ ((packed));
struct squashfs_dev_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
squashfs_block start_block;
unsigned int fragment;
unsigned int offset;
unsigned int file_size;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_lreg_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
squashfs_block start_block;
unsigned int fragment;
unsigned int offset;
long long file_size;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int file_size:19;
unsigned int offset:13;
unsigned int start_block;
unsigned int parent_inode;
} __attribute__ ((packed));
struct squashfs_ldir_inode_header_3 {
unsigned int inode_type:4;
unsigned int mode:12;
unsigned int uid:8;
unsigned int guid:8;
int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int file_size:27;
unsigned int offset:13;
unsigned int start_block;
unsigned int i_count:16;
unsigned int parent_inode;
struct squashfs_dir_index_3 index[0];
} __attribute__ ((packed));
union squashfs_inode_header_3 {
struct squashfs_base_inode_header_3 base;
struct squashfs_dev_inode_header_3 dev;
struct squashfs_symlink_inode_header_3 symlink;
struct squashfs_reg_inode_header_3 reg;
struct squashfs_lreg_inode_header_3 lreg;
struct squashfs_dir_inode_header_3 dir;
struct squashfs_ldir_inode_header_3 ldir;
struct squashfs_ipc_inode_header_3 ipc;
};
struct squashfs_dir_entry_3 {
unsigned int offset:13;
unsigned int type:3;
unsigned int size:8;
int inode_number:16;
char name[0];
} __attribute__ ((packed));
struct squashfs_dir_header_3 {
unsigned int count:8;
unsigned int start_block;
unsigned int inode_number;
} __attribute__ ((packed));
struct squashfs_fragment_entry_3 {
long long start_block;
unsigned int size;
unsigned int pending;
} __attribute__ ((packed));
typedef struct squashfs_super_block_3 squashfs_super_block_3;
typedef struct squashfs_dir_index_3 squashfs_dir_index_3;
typedef struct squashfs_base_inode_header_3 squashfs_base_inode_header_3;
typedef struct squashfs_ipc_inode_header_3 squashfs_ipc_inode_header_3;
typedef struct squashfs_dev_inode_header_3 squashfs_dev_inode_header_3;
typedef struct squashfs_symlink_inode_header_3 squashfs_symlink_inode_header_3;
typedef struct squashfs_reg_inode_header_3 squashfs_reg_inode_header_3;
typedef struct squashfs_lreg_inode_header_3 squashfs_lreg_inode_header_3;
typedef struct squashfs_dir_inode_header_3 squashfs_dir_inode_header_3;
typedef struct squashfs_ldir_inode_header_3 squashfs_ldir_inode_header_3;
typedef struct squashfs_dir_entry_3 squashfs_dir_entry_3;
typedef struct squashfs_dir_header_3 squashfs_dir_header_3;
typedef struct squashfs_fragment_entry_3 squashfs_fragment_entry_3;
/*
* macros to convert each packed bitfield structure from little endian to big
* endian and vice versa. These are needed when creating or using a filesystem
* on a machine with different byte ordering to the target architecture.
*
*/
#define SQUASHFS_SWAP_START \
int bits;\
int b_pos;\
unsigned long long val;\
unsigned char *s;\
unsigned char *d;
#define SQUASHFS_SWAP_SUPER_BLOCK_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block_3));\
SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
SQUASHFS_SWAP((s)->flags, d, 288, 8);\
SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\
}
#define SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, n)\
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 8);\
SQUASHFS_SWAP((s)->guid, d, 24, 8);\
SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
#define SQUASHFS_SWAP_BASE_INODE_HEADER_3(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_ipc_inode_header_3))\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
}
#define SQUASHFS_SWAP_DEV_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_dev_inode_header_3)); \
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_symlink_inode_header_3));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_reg_inode_header_3));\
SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
SQUASHFS_SWAP((s)->offset, d, 192, 32);\
SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
}
#define SQUASHFS_SWAP_LREG_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_lreg_inode_header_3));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
SQUASHFS_SWAP((s)->offset, d, 224, 32);\
SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_dir_inode_header_3));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
SQUASHFS_SWAP((s)->offset, d, 147, 13);\
SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
}
#define SQUASHFS_SWAP_LDIR_INODE_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
sizeof(struct squashfs_ldir_inode_header_3));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
SQUASHFS_SWAP((s)->offset, d, 155, 13);\
SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
}
#define SQUASHFS_SWAP_DIR_INDEX_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_3));\
SQUASHFS_SWAP((s)->index, d, 0, 32);\
SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
SQUASHFS_SWAP((s)->size, d, 64, 8);\
}
#define SQUASHFS_SWAP_DIR_HEADER_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_3));\
SQUASHFS_SWAP((s)->count, d, 0, 8);\
SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
}
#define SQUASHFS_SWAP_DIR_ENTRY_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_3));\
SQUASHFS_SWAP((s)->offset, d, 0, 13);\
SQUASHFS_SWAP((s)->type, d, 13, 3);\
SQUASHFS_SWAP((s)->size, d, 16, 8);\
SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
}
#define SQUASHFS_SWAP_INODE_T_3(s, d) SQUASHFS_SWAP_LONG_LONGS_3(s, d, 1)
#define SQUASHFS_SWAP_SHORTS_3(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 2);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
16)\
SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
}
#define SQUASHFS_SWAP_INTS_3(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 4);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
32)\
SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
}
#define SQUASHFS_SWAP_LONG_LONGS_3(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 8);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
64)\
SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
}
#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * bits / 8);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
bits)\
SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
}
#define SQUASHFS_SWAP_FRAGMENT_INDEXES_3(s, d, n) SQUASHFS_SWAP_LONG_LONGS_3(s, d, n)
#define SQUASHFS_SWAP_LOOKUP_BLOCKS_3(s, d, n) SQUASHFS_SWAP_LONG_LONGS_3(s, d, n)
#define SQUASHFS_SWAP_FRAGMENT_ENTRY_3(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_3));\
SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
SQUASHFS_SWAP((s)->size, d, 64, 32);\
}
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES_3(A) ((A) * sizeof(struct squashfs_fragment_entry_3))
#define SQUASHFS_FRAGMENT_INDEX_3(A) (SQUASHFS_FRAGMENT_BYTES_3(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET_3(A) (SQUASHFS_FRAGMENT_BYTES_3(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES_3(A) ((SQUASHFS_FRAGMENT_BYTES_3(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES_3(A) (SQUASHFS_FRAGMENT_INDEXES_3(A) *\
sizeof(long long))
/* inode lookup table defines */
#define SQUASHFS_LOOKUP_BYTES_3(A) ((A) * sizeof(squashfs_inode))
#define SQUASHFS_LOOKUP_BLOCK_3(A) (SQUASHFS_LOOKUP_BYTES_3(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_OFFSET_3(A) (SQUASHFS_LOOKUP_BYTES_3(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCKS_3(A) ((SQUASHFS_LOOKUP_BYTES_3(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_BYTES_3(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
sizeof(long long))
/*
* definitions for structures on disk - layout 1.x
*/
#define SQUASHFS_TYPES 5
#define SQUASHFS_IPC_TYPE 0
struct squashfs_base_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_ipc_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned int type:4;
unsigned int offset:4;
} __attribute__ ((packed));
struct squashfs_dev_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
int mtime;
unsigned int start_block;
unsigned int file_size:32;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned int file_size:19;
unsigned int offset:13;
int mtime;
unsigned int start_block:24;
} __attribute__ ((packed));
union squashfs_inode_header_1 {
struct squashfs_base_inode_header_1 base;
struct squashfs_dev_inode_header_1 dev;
struct squashfs_symlink_inode_header_1 symlink;
struct squashfs_reg_inode_header_1 reg;
struct squashfs_dir_inode_header_1 dir;
struct squashfs_ipc_inode_header_1 ipc;
};
typedef struct squashfs_dir_index_1 squashfs_dir_index_1;
typedef struct squashfs_base_inode_header_1 squashfs_base_inode_header_1;
typedef struct squashfs_ipc_inode_header_1 squashfs_ipc_inode_header_1;
typedef struct squashfs_dev_inode_header_1 squashfs_dev_inode_header_1;
typedef struct squashfs_symlink_inode_header_1 squashfs_symlink_inode_header_1;
typedef struct squashfs_reg_inode_header_1 squashfs_reg_inode_header_1;
typedef struct squashfs_dir_inode_header_1 squashfs_dir_inode_header_1;
#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 4);\
SQUASHFS_SWAP((s)->guid, d, 20, 4);
#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_ipc_inode_header_1));\
SQUASHFS_SWAP((s)->type, d, 24, 4);\
SQUASHFS_SWAP((s)->offset, d, 28, 4);\
}
#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_dev_inode_header_1));\
SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_symlink_inode_header_1));\
SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_reg_inode_header_1));\
SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_dir_inode_header_1));\
SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
SQUASHFS_SWAP((s)->offset, d, 43, 13);\
SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
}
/*
* definitions for structures on disk - layout 2.x
*/
struct squashfs_dir_index_2 {
unsigned int index:27;
unsigned int start_block:29;
unsigned char size;
unsigned char name[0];
} __attribute__ ((packed));
struct squashfs_base_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_ipc_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_dev_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
int mtime;
unsigned int start_block;
unsigned int fragment;
unsigned int offset;
unsigned int file_size:32;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned int file_size:19;
unsigned int offset:13;
int mtime;
unsigned int start_block:24;
} __attribute__ ((packed));
struct squashfs_ldir_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned int file_size:27;
unsigned int offset:13;
int mtime;
unsigned int start_block:24;
unsigned int i_count:16;
struct squashfs_dir_index_2 index[0];
} __attribute__ ((packed));
union squashfs_inode_header_2 {
struct squashfs_base_inode_header_2 base;
struct squashfs_dev_inode_header_2 dev;
struct squashfs_symlink_inode_header_2 symlink;
struct squashfs_reg_inode_header_2 reg;
struct squashfs_dir_inode_header_2 dir;
struct squashfs_ldir_inode_header_2 ldir;
struct squashfs_ipc_inode_header_2 ipc;
};
struct squashfs_dir_header_2 {
unsigned int count:8;
unsigned int start_block:24;
} __attribute__ ((packed));
struct squashfs_dir_entry_2 {
unsigned int offset:13;
unsigned int type:3;
unsigned int size:8;
char name[0];
} __attribute__ ((packed));
struct squashfs_fragment_entry_2 {
unsigned int start_block;
unsigned int size;
} __attribute__ ((packed));
typedef struct squashfs_dir_index_2 squashfs_dir_index_2;
typedef struct squashfs_base_inode_header_2 squashfs_base_inode_header_2;
typedef struct squashfs_ipc_inode_header_2 squashfs_ipc_inode_header_2;
typedef struct squashfs_dev_inode_header_2 squashfs_dev_inode_header_2;
typedef struct squashfs_symlink_inode_header_2 squashfs_symlink_inode_header_2;
typedef struct squashfs_reg_inode_header_2 squashfs_reg_inode_header_2;
typedef struct squashfs_lreg_inode_header_2 squashfs_lreg_inode_header_2;
typedef struct squashfs_dir_inode_header_2 squashfs_dir_inode_header_2;
typedef struct squashfs_ldir_inode_header_2 squashfs_ldir_inode_header_2;
typedef struct squashfs_dir_entry_2 squashfs_dir_entry_2;
typedef struct squashfs_dir_header_2 squashfs_dir_header_2;
typedef struct squashfs_fragment_entry_2 squashfs_fragment_entry_2;
#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 8);\
SQUASHFS_SWAP((s)->guid, d, 24, 8);\
#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_dev_inode_header_2)); \
SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_symlink_inode_header_2));\
SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_reg_inode_header_2));\
SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
SQUASHFS_SWAP((s)->offset, d, 128, 32);\
SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_dir_inode_header_2));\
SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
SQUASHFS_SWAP((s)->offset, d, 51, 13);\
SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
}
#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_ldir_inode_header_2));\
SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
SQUASHFS_SWAP((s)->offset, d, 59, 13);\
SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
}
#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
SQUASHFS_SWAP((s)->index, d, 0, 27);\
SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
SQUASHFS_SWAP((s)->size, d, 56, 8);\
}
#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
SQUASHFS_SWAP((s)->count, d, 0, 8);\
SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
}
#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
SQUASHFS_SWAP((s)->offset, d, 0, 13);\
SQUASHFS_SWAP((s)->type, d, 13, 3);\
SQUASHFS_SWAP((s)->size, d, 16, 8);\
}
#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
SQUASHFS_SWAP((s)->size, d, 32, 32);\
}
#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS_3(s, d, n)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES_2(A) ((A) * sizeof(struct squashfs_fragment_entry_2))
#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
sizeof(int))
/*
* macros used to swap each structure entry, taking into account
* bitfields and different bitfield placing conventions on differing architectures
*/
#if __BYTE_ORDER == __BIG_ENDIAN
/* convert from little endian to big endian */
#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, tbits, b_pos)
#else
/* convert from big endian to little endian */
#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, tbits, 64 - tbits - b_pos)
#endif
#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
b_pos = pos % 8;\
val = 0;\
s = (unsigned char *)p + (pos / 8);\
d = ((unsigned char *) &val) + 7;\
for(bits = 0; bits < (tbits + b_pos); bits += 8) \
*d-- = *s++;\
value = (val >> (SHIFT));\
}
#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n);
#endif

View File

@@ -0,0 +1,499 @@
#ifndef SQUASHFS_FS
#define SQUASHFS_FS
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012,
* 2013, 2014, 2017, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* squashfs_fs.h
*/
#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#define SQUASHFS_MAJOR 4
#define SQUASHFS_MINOR 0
#define SQUASHFS_MAGIC 0x73717368
#define SQUASHFS_MAGIC_SWAP 0x68737173
#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_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
/* Max value for directory header count */
#define SQUASHFS_DIR_COUNT 256
#define SQUASHFS_INVALID ((long long) 0xffffffffffff)
#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff)
#define SQUASHFS_INVALID_XATTR ((unsigned int) 0xffffffff)
#define SQUASHFS_INVALID_BLK ((long long) -1)
#define SQUASHFS_USED_BLK ((long long) -2)
/* Filesystem flags */
#define SQUASHFS_NOI 0
#define SQUASHFS_NOD 1
#define SQUASHFS_CHECK 2
#define SQUASHFS_NOF 3
#define SQUASHFS_NO_FRAG 4
#define SQUASHFS_ALWAYS_FRAG 5
#define SQUASHFS_DUPLICATE 6
#define SQUASHFS_EXPORT 7
#define SQUASHFS_NOX 8
#define SQUASHFS_NO_XATTR 9
#define SQUASHFS_COMP_OPT 10
#define SQUASHFS_NOID 11
#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOI)
#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOD)
#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOF)
#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_FRAG)
#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_ALWAYS_FRAG)
#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_DUPLICATE)
#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
SQUASHFS_EXPORT)
#define SQUASHFS_UNCOMPRESSED_XATTRS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOX)
#define SQUASHFS_NO_XATTRS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_XATTR)
#define SQUASHFS_COMP_OPTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_COMP_OPT)
#define SQUASHFS_UNCOMPRESSED_IDS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOID)
#define SQUASHFS_MKFLAGS(noi, nod, nof, nox, noid, no_frag, always_frag, \
duplicate_checking, exportable, no_xattr, comp_opt) (noi | \
(nod << 1) | (nof << 3) | (no_frag << 4) | \
(always_frag << 5) | (duplicate_checking << 6) | \
(exportable << 7) | (nox << 8) | (no_xattr << 9) | \
(comp_opt << 10) | (noid << 11))
/* Max number of types and file types */
#define SQUASHFS_DIR_TYPE 1
#define SQUASHFS_FILE_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
/* Flag whether block is compressed or uncompressed, bit is set if block is
* uncompressed */
#define SQUASHFS_COMPRESSED_BIT (1 << 15)
#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
(B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK)
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
/*
* Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block
*/
#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16))
#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff))
#define SQUASHFS_MKINODE(A, B) ((squashfs_inode)(((squashfs_inode) (A)\
<< 16) + (B)))
/* Compute 32 bit VFS inode number from squashfs inode number */
#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \
((b) >> 2) + 1))
/* Translate between VFS mode and squashfs mode */
#define SQUASHFS_MODE(a) ((a) & 0xfff)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES(A) ((A) * \
sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
sizeof(long long))
/* inode lookup table defines */
#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(squashfs_inode))
#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
sizeof(long long))
/* uid lookup table defines */
#define SQUASHFS_ID_BYTES(A) ((A) * sizeof(unsigned int))
#define SQUASHFS_ID_BLOCK(A) (SQUASHFS_ID_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_OFFSET(A) (SQUASHFS_ID_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCKS(A) ((SQUASHFS_ID_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\
sizeof(long long))
/* xattr id lookup table defines */
#define SQUASHFS_XATTR_BYTES(A) ((A) * sizeof(struct squashfs_xattr_id))
#define SQUASHFS_XATTR_BLOCK(A) (SQUASHFS_XATTR_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_XATTR_BLOCK_OFFSET(A) (SQUASHFS_XATTR_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_XATTR_BLOCKS(A) ((SQUASHFS_XATTR_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_XATTR_BLOCK_BYTES(A) (SQUASHFS_XATTR_BLOCKS(A) *\
sizeof(long long))
#define SQUASHFS_XATTR_BLK(A) ((unsigned int) ((A) >> 16))
#define SQUASHFS_XATTR_OFFSET(A) ((unsigned int) ((A) & 0xffff))
/* cached data constants for filesystem */
#define SQUASHFS_CACHED_BLKS 8
#define SQUASHFS_MAX_FILE_SIZE_LOG 64
#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \
(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
#define SQUASHFS_MARKER_BYTE 0xff
/* meta index cache */
#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
#define SQUASHFS_META_ENTRIES 31
#define SQUASHFS_META_NUMBER 8
#define SQUASHFS_SLOTS 4
struct meta_entry {
long long data_block;
unsigned int index_block;
unsigned short offset;
unsigned short pad;
};
struct meta_index {
unsigned int inode_number;
unsigned int offset;
unsigned short entries;
unsigned short skip;
unsigned short locked;
unsigned short pad;
struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
};
/*
* definitions for structures on disk
*/
typedef long long squashfs_block;
typedef long long squashfs_inode;
#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 {
unsigned int s_magic;
unsigned int inodes;
unsigned int mkfs_time /* time of filesystem creation */;
unsigned int block_size;
unsigned int fragments;
unsigned short compression;
unsigned short block_log;
unsigned short flags;
unsigned short no_ids;
unsigned short s_major;
unsigned short s_minor;
squashfs_inode root_inode;
long long bytes_used;
long long id_table_start;
long long xattr_id_table_start;
long long inode_table_start;
long long directory_table_start;
long long fragment_table_start;
long long lookup_table_start;
};
struct squashfs_dir_index {
unsigned int index;
unsigned int start_block;
unsigned int size;
unsigned char name[0];
};
struct squashfs_base_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
};
struct squashfs_ipc_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
};
struct squashfs_lipc_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int xattr;
};
struct squashfs_dev_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int rdev;
};
struct squashfs_ldev_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int rdev;
unsigned int xattr;
};
struct squashfs_symlink_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int symlink_size;
char symlink[0];
};
struct squashfs_reg_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int start_block;
unsigned int fragment;
unsigned int offset;
unsigned int file_size;
unsigned int block_list[0];
};
struct squashfs_lreg_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
squashfs_block start_block;
long long file_size;
long long sparse;
unsigned int nlink;
unsigned int fragment;
unsigned int offset;
unsigned int xattr;
unsigned int block_list[0];
};
struct squashfs_dir_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int start_block;
unsigned int nlink;
unsigned short file_size;
unsigned short offset;
unsigned int parent_inode;
};
struct squashfs_ldir_inode_header {
unsigned short inode_type;
unsigned short mode;
unsigned short uid;
unsigned short guid;
unsigned int mtime;
unsigned int inode_number;
unsigned int nlink;
unsigned int file_size;
unsigned int start_block;
unsigned int parent_inode;
unsigned short i_count;
unsigned short offset;
unsigned int xattr;
struct squashfs_dir_index index[0];
};
union squashfs_inode_header {
struct squashfs_base_inode_header base;
struct squashfs_dev_inode_header dev;
struct squashfs_ldev_inode_header ldev;
struct squashfs_symlink_inode_header symlink;
struct squashfs_reg_inode_header reg;
struct squashfs_lreg_inode_header lreg;
struct squashfs_dir_inode_header dir;
struct squashfs_ldir_inode_header ldir;
struct squashfs_ipc_inode_header ipc;
struct squashfs_lipc_inode_header lipc;
};
struct squashfs_dir_entry {
unsigned short offset;
short inode_number;
unsigned short type;
unsigned short size;
char name[0];
};
struct squashfs_dir_header {
unsigned int count;
unsigned int start_block;
unsigned int inode_number;
};
struct squashfs_fragment_entry {
long long start_block;
unsigned int size;
unsigned int unused;
};
struct squashfs_xattr_entry {
unsigned short type;
unsigned short size;
};
struct squashfs_xattr_val {
unsigned int vsize;
};
struct squashfs_xattr_id {
long long xattr;
unsigned int count;
unsigned int size;
};
struct squashfs_xattr_table {
long long xattr_table_start;
unsigned int xattr_ids;
unsigned int unused;
};
#endif

View File

@@ -0,0 +1,423 @@
#ifndef SQUASHFS_SWAP_H
#define SQUASHFS_SWAP_H
/*
* Squashfs
*
* Copyright (c) 2008, 2009, 2010, 2013, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* squashfs_swap.h
*/
/*
* macros to convert each stucture from big endian to little endian
*/
#if __BYTE_ORDER == __BIG_ENDIAN
#include <stddef.h>
extern void swap_le16(void *, void *);
extern void swap_le32(void *, void *);
extern void swap_le64(void *, void *);
extern void swap_le16_num(void *, void *, int);
extern void swap_le32_num(void *, void *, int);
extern void swap_le64_num(void *, void *, int);
extern unsigned short inswap_le16(unsigned short);
extern unsigned int inswap_le32(unsigned int);
extern long long inswap_le64(long long);
extern void inswap_le16_num(unsigned short *, int);
extern void inswap_le32_num(unsigned int *, int);
extern void inswap_le64_num(long long *, int);
#define _SQUASHFS_SWAP_SUPER_BLOCK(s, d, SWAP_FUNC) {\
SWAP_FUNC(32, s, d, s_magic, struct squashfs_super_block);\
SWAP_FUNC(32, s, d, inodes, struct squashfs_super_block);\
SWAP_FUNC(32, s, d, mkfs_time, struct squashfs_super_block);\
SWAP_FUNC(32, s, d, block_size, struct squashfs_super_block);\
SWAP_FUNC(32, s, d, fragments, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, compression, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, block_log, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, flags, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, no_ids, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, s_major, struct squashfs_super_block);\
SWAP_FUNC(16, s, d, s_minor, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, root_inode, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, bytes_used, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, id_table_start, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, xattr_id_table_start, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, inode_table_start, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, directory_table_start, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, fragment_table_start, struct squashfs_super_block);\
SWAP_FUNC(64, s, d, lookup_table_start, struct squashfs_super_block);\
}
#define _SQUASHFS_SWAP_DIR_INDEX(s, d, SWAP_FUNC) {\
SWAP_FUNC(32, s, d, index, struct squashfs_dir_index);\
SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_index);\
SWAP_FUNC(32, s, d, size, struct squashfs_dir_index);\
}
#define _SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_base_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_base_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_base_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_base_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_base_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_base_inode_header);\
}
#define _SQUASHFS_SWAP_IPC_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_ipc_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_ipc_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_ipc_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_ipc_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_ipc_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_ipc_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_ipc_inode_header);\
}
#define _SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_lipc_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_lipc_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_lipc_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_lipc_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_lipc_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_lipc_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_lipc_inode_header);\
SWAP_FUNC(32, s, d, xattr, struct squashfs_lipc_inode_header);\
}
#define _SQUASHFS_SWAP_DEV_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_dev_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_dev_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_dev_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_dev_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_dev_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_dev_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_dev_inode_header);\
SWAP_FUNC(32, s, d, rdev, struct squashfs_dev_inode_header);\
}
#define _SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_ldev_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_ldev_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_ldev_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_ldev_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_ldev_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_ldev_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_ldev_inode_header);\
SWAP_FUNC(32, s, d, rdev, struct squashfs_ldev_inode_header);\
SWAP_FUNC(32, s, d, xattr, struct squashfs_ldev_inode_header);\
}
#define _SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_symlink_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_symlink_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_symlink_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_symlink_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_symlink_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_symlink_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_symlink_inode_header);\
SWAP_FUNC(32, s, d, symlink_size, struct squashfs_symlink_inode_header);\
}
#define _SQUASHFS_SWAP_REG_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_reg_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_reg_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_reg_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, start_block, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, fragment, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, offset, struct squashfs_reg_inode_header);\
SWAP_FUNC(32, s, d, file_size, struct squashfs_reg_inode_header);\
}
#define _SQUASHFS_SWAP_LREG_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_lreg_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_lreg_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_lreg_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_lreg_inode_header);\
SWAP_FUNC(64, s, d, start_block, struct squashfs_lreg_inode_header);\
SWAP_FUNC(64, s, d, file_size, struct squashfs_lreg_inode_header);\
SWAP_FUNC(64, s, d, sparse, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, fragment, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, offset, struct squashfs_lreg_inode_header);\
SWAP_FUNC(32, s, d, xattr, struct squashfs_lreg_inode_header);\
}
#define _SQUASHFS_SWAP_DIR_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_dir_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_dir_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_dir_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_dir_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_dir_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_dir_inode_header);\
SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_dir_inode_header);\
SWAP_FUNC(16, s, d, file_size, struct squashfs_dir_inode_header);\
SWAP_FUNC(16, s, d, offset, struct squashfs_dir_inode_header);\
SWAP_FUNC(32, s, d, parent_inode, struct squashfs_dir_inode_header);\
}
#define _SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, inode_type, struct squashfs_ldir_inode_header);\
SWAP_FUNC(16, s, d, mode, struct squashfs_ldir_inode_header);\
SWAP_FUNC(16, s, d, uid, struct squashfs_ldir_inode_header);\
SWAP_FUNC(16, s, d, guid, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, mtime, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, nlink, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, file_size, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, start_block, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, parent_inode, struct squashfs_ldir_inode_header);\
SWAP_FUNC(16, s, d, i_count, struct squashfs_ldir_inode_header);\
SWAP_FUNC(16, s, d, offset, struct squashfs_ldir_inode_header);\
SWAP_FUNC(32, s, d, xattr, struct squashfs_ldir_inode_header);\
}
#define _SQUASHFS_SWAP_DIR_ENTRY(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, offset, struct squashfs_dir_entry);\
SWAP_FUNC##S(16, s, d, inode_number, struct squashfs_dir_entry);\
SWAP_FUNC(16, s, d, type, struct squashfs_dir_entry);\
SWAP_FUNC(16, s, d, size, struct squashfs_dir_entry);\
}
#define _SQUASHFS_SWAP_DIR_HEADER(s, d, SWAP_FUNC) {\
SWAP_FUNC(32, s, d, count, struct squashfs_dir_header);\
SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_header);\
SWAP_FUNC(32, s, d, inode_number, struct squashfs_dir_header);\
}
#define _SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d, SWAP_FUNC) {\
SWAP_FUNC(64, s, d, start_block, struct squashfs_fragment_entry);\
SWAP_FUNC(32, s, d, size, struct squashfs_fragment_entry);\
}
#define _SQUASHFS_SWAP_XATTR_ENTRY(s, d, SWAP_FUNC) {\
SWAP_FUNC(16, s, d, type, struct squashfs_xattr_entry);\
SWAP_FUNC(16, s, d, size, struct squashfs_xattr_entry);\
}
#define _SQUASHFS_SWAP_XATTR_VAL(s, d, SWAP_FUNC) {\
SWAP_FUNC(32, s, d, vsize, struct squashfs_xattr_val);\
}
#define _SQUASHFS_SWAP_XATTR_ID(s, d, SWAP_FUNC) {\
SWAP_FUNC(64, s, d, xattr, struct squashfs_xattr_id);\
SWAP_FUNC(32, s, d, count, struct squashfs_xattr_id);\
SWAP_FUNC(32, s, d, size, struct squashfs_xattr_id);\
}
#define _SQUASHFS_SWAP_XATTR_TABLE(s, d, SWAP_FUNC) {\
SWAP_FUNC(64, s, d, xattr_table_start, struct squashfs_xattr_table);\
SWAP_FUNC(32, s, d, xattr_ids, struct squashfs_xattr_table);\
}
/* big endian architecture copy and swap macros */
#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) \
_SQUASHFS_SWAP_SUPER_BLOCK(s, d, SWAP_LE)
#define SQUASHFS_SWAP_DIR_INDEX(s, d) \
_SQUASHFS_SWAP_DIR_INDEX(s, d, SWAP_LE)
#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_IPC_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_DEV_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_REG_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_LREG_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_DIR_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) \
_SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_DIR_ENTRY(s, d) \
_SQUASHFS_SWAP_DIR_ENTRY(s, d, SWAP_LE)
#define SQUASHFS_SWAP_DIR_HEADER(s, d) \
_SQUASHFS_SWAP_DIR_HEADER(s, d, SWAP_LE)
#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) \
_SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d, SWAP_LE)
#define SQUASHFS_SWAP_XATTR_ENTRY(s, d) \
_SQUASHFS_SWAP_XATTR_ENTRY(s, d, SWAP_LE)
#define SQUASHFS_SWAP_XATTR_VAL(s, d) \
_SQUASHFS_SWAP_XATTR_VAL(s, d, SWAP_LE)
#define SQUASHFS_SWAP_XATTR_ID(s, d) \
_SQUASHFS_SWAP_XATTR_ID(s, d, SWAP_LE)
#define SQUASHFS_SWAP_XATTR_TABLE(s, d) \
_SQUASHFS_SWAP_XATTR_TABLE(s, d, SWAP_LE)
#define SWAP_LE(bits, s, d, field, type) \
SWAP_LE##bits(((void *)(s)) + offsetof(type, field), \
((void *)(d)) + offsetof(type, field))
#define SWAP_LES(bits, s, d, field, type) \
SWAP_LE(bits, s, d, field, type)
#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) \
SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_SWAP_ID_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_SWAP_SHORTS(s, d, n) swap_le16_num(s, d, n)
#define SQUASHFS_SWAP_INTS(s, d, n) swap_le32_num(s, d, n)
#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) swap_le64_num(s, d, n)
#define SWAP_LE16(s, d) swap_le16(s, d)
#define SWAP_LE32(s, d) swap_le32(s, d)
#define SWAP_LE64(s, d) swap_le64(s, d)
/* big endian architecture swap in-place macros */
#define SQUASHFS_INSWAP_SUPER_BLOCK(s) \
_SQUASHFS_SWAP_SUPER_BLOCK(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_DIR_INDEX(s) \
_SQUASHFS_SWAP_DIR_INDEX(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_BASE_INODE_HEADER(s) \
_SQUASHFS_SWAP_BASE_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_IPC_INODE_HEADER(s) \
_SQUASHFS_SWAP_IPC_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_LIPC_INODE_HEADER(s) \
_SQUASHFS_SWAP_LIPC_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_DEV_INODE_HEADER(s) \
_SQUASHFS_SWAP_DEV_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_LDEV_INODE_HEADER(s) \
_SQUASHFS_SWAP_LDEV_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_SYMLINK_INODE_HEADER(s) \
_SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_REG_INODE_HEADER(s) \
_SQUASHFS_SWAP_REG_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_LREG_INODE_HEADER(s) \
_SQUASHFS_SWAP_LREG_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_DIR_INODE_HEADER(s) \
_SQUASHFS_SWAP_DIR_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_LDIR_INODE_HEADER(s) \
_SQUASHFS_SWAP_LDIR_INODE_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_DIR_ENTRY(s) \
_SQUASHFS_SWAP_DIR_ENTRY(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_DIR_HEADER(s) \
_SQUASHFS_SWAP_DIR_HEADER(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_FRAGMENT_ENTRY(s) \
_SQUASHFS_SWAP_FRAGMENT_ENTRY(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_XATTR_ENTRY(s) \
_SQUASHFS_SWAP_XATTR_ENTRY(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_XATTR_VAL(s) \
_SQUASHFS_SWAP_XATTR_VAL(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_XATTR_ID(s) \
_SQUASHFS_SWAP_XATTR_ID(s, s, INSWAP_LE)
#define SQUASHFS_INSWAP_XATTR_TABLE(s) \
_SQUASHFS_SWAP_XATTR_TABLE(s, s, INSWAP_LE)
#define INSWAP_LE(bits, s, d, field, type) \
(s)->field = inswap_le##bits((s)->field)
#define INSWAP_LES(bits, s, d, field, type) \
(s)->field = (short) inswap_le##bits((unsigned short) \
(s)->field)
#define SQUASHFS_INSWAP_INODE_T(s) s = inswap_le64(s)
#define SQUASHFS_INSWAP_FRAGMENT_INDEXES(s, n) inswap_le64_num(s, n)
#define SQUASHFS_INSWAP_LOOKUP_BLOCKS(s, n) inswap_le64_num(s, n)
#define SQUASHFS_INSWAP_ID_BLOCKS(s, n) inswap_le64_num(s, n)
#define SQUASHFS_INSWAP_SHORTS(s, n) inswap_le16_num(s, n)
#define SQUASHFS_INSWAP_INTS(s, n) inswap_le32_num(s, n)
#define SQUASHFS_INSWAP_LONG_LONGS(s, n) inswap_le64_num(s, n)
#else
/* little endian architecture, just copy */
#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_super_block))
#define SQUASHFS_SWAP_DIR_INDEX(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_index))
#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_base_inode_header))
#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ipc_inode_header))
#define SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_lipc_inode_header))
#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dev_inode_header))
#define SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ldev_inode_header))
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_symlink_inode_header))
#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_reg_inode_header))
#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_lreg_inode_header))
#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_inode_header))
#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ldir_inode_header))
#define SQUASHFS_SWAP_DIR_ENTRY(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_entry))
#define SQUASHFS_SWAP_DIR_HEADER(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_header))
#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_SWAP_XATTR_ENTRY(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_entry))
#define SQUASHFS_SWAP_XATTR_VAL(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_val))
#define SQUASHFS_SWAP_XATTR_ID(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_id))
#define SQUASHFS_SWAP_XATTR_TABLE(s, d) \
SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_table))
#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) \
SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_SWAP_ID_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#define SQUASHFS_MEMCPY(s, d, n) memcpy(d, s, n)
#define SQUASHFS_SWAP_SHORTS(s, d, n) memcpy(d, s, n * sizeof(short))
#define SQUASHFS_SWAP_INTS(s, d, n) memcpy(d, s, n * sizeof(int))
#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) \
memcpy(d, s, n * sizeof(long long))
/* little endian architecture, data already in place so do nothing */
#define SQUASHFS_INSWAP_SUPER_BLOCK(s)
#define SQUASHFS_INSWAP_DIR_INDEX(s)
#define SQUASHFS_INSWAP_BASE_INODE_HEADER(s)
#define SQUASHFS_INSWAP_IPC_INODE_HEADER(s)
#define SQUASHFS_INSWAP_LIPC_INODE_HEADER(s)
#define SQUASHFS_INSWAP_DEV_INODE_HEADER(s)
#define SQUASHFS_INSWAP_LDEV_INODE_HEADER(s)
#define SQUASHFS_INSWAP_SYMLINK_INODE_HEADER(s)
#define SQUASHFS_INSWAP_REG_INODE_HEADER(s)
#define SQUASHFS_INSWAP_LREG_INODE_HEADER(s)
#define SQUASHFS_INSWAP_DIR_INODE_HEADER(s)
#define SQUASHFS_INSWAP_LDIR_INODE_HEADER(s)
#define SQUASHFS_INSWAP_DIR_ENTRY(s)
#define SQUASHFS_INSWAP_DIR_HEADER(s)
#define SQUASHFS_INSWAP_FRAGMENT_ENTRY(s)
#define SQUASHFS_INSWAP_XATTR_ENTRY(s)
#define SQUASHFS_INSWAP_XATTR_VAL(s)
#define SQUASHFS_INSWAP_XATTR_ID(s)
#define SQUASHFS_INSWAP_XATTR_TABLE(s)
#define SQUASHFS_INSWAP_INODE_T(s)
#define SQUASHFS_INSWAP_FRAGMENT_INDEXES(s, n)
#define SQUASHFS_INSWAP_LOOKUP_BLOCKS(s, n)
#define SQUASHFS_INSWAP_ID_BLOCKS(s, n)
#define SQUASHFS_INSWAP_SHORTS(s, n)
#define SQUASHFS_INSWAP_INTS(s, n)
#define SQUASHFS_INSWAP_LONG_LONGS(s, n)
#endif
#endif

View File

@@ -0,0 +1,123 @@
/*
* Copyright (c) 2009, 2010
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* swap.c
*/
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
void swap_le16(void *src, void *dest)
{
unsigned char *s = src;
unsigned char *d = dest;
d[0] = s[1];
d[1] = s[0];
}
void swap_le32(void *src, void *dest)
{
unsigned char *s = src;
unsigned char *d = dest;
d[0] = s[3];
d[1] = s[2];
d[2] = s[1];
d[3] = s[0];
}
void swap_le64(void *src, void *dest)
{
unsigned char *s = src;
unsigned char *d = dest;
d[0] = s[7];
d[1] = s[6];
d[2] = s[5];
d[3] = s[4];
d[4] = s[3];
d[5] = s[2];
d[6] = s[1];
d[7] = s[0];
}
unsigned short inswap_le16(unsigned short num)
{
return (num >> 8) |
((num & 0xff) << 8);
}
unsigned int inswap_le32(unsigned int num)
{
return (num >> 24) |
((num & 0xff0000) >> 8) |
((num & 0xff00) << 8) |
((num & 0xff) << 24);
}
long long inswap_le64(long long n)
{
unsigned long long num = n;
return (num >> 56) |
((num & 0xff000000000000LL) >> 40) |
((num & 0xff0000000000LL) >> 24) |
((num & 0xff00000000LL) >> 8) |
((num & 0xff000000) << 8) |
((num & 0xff0000) << 24) |
((num & 0xff00) << 40) |
((num & 0xff) << 56);
}
#define SWAP_LE_NUM(BITS) \
void swap_le##BITS##_num(void *s, void *d, int n) \
{\
int i;\
for(i = 0; i < n; i++, s += BITS / 8, d += BITS / 8)\
swap_le##BITS(s, d);\
}
SWAP_LE_NUM(16)
SWAP_LE_NUM(32)
SWAP_LE_NUM(64)
#define INSWAP_LE_NUM(BITS, TYPE) \
void inswap_le##BITS##_num(TYPE *s, int n) \
{\
int i;\
for(i = 0; i < n; i++)\
s[i] = inswap_le##BITS(s[i]);\
}
INSWAP_LE_NUM(16, unsigned short)
INSWAP_LE_NUM(32, unsigned int)
INSWAP_LE_NUM(64, long long)
#endif

View File

@@ -0,0 +1,411 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2011, 2012, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* unsquash-1.c
*/
#include "unsquashfs.h"
#include "squashfs_compat.h"
static unsigned int *uid_table, *guid_table;
static char *inode_table, *directory_table;
static squashfs_operations ops;
static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
{
unsigned short block_size;
int i;
TRACE("read_block_list: blocks %d\n", blocks);
for(i = 0; i < blocks; i++, block_ptr += 2) {
if(swap) {
unsigned short sblock_size;
memcpy(&sblock_size, block_ptr, sizeof(unsigned short));
SQUASHFS_SWAP_SHORTS_3((&block_size), &sblock_size, 1);
} else
memcpy(&block_size, block_ptr, sizeof(unsigned short));
block_list[i] = SQUASHFS_COMPRESSED_SIZE(block_size) |
(SQUASHFS_COMPRESSED(block_size) ? 0 :
SQUASHFS_COMPRESSED_BIT_BLOCK);
}
}
static struct inode *read_inode(unsigned int start_block, unsigned int offset)
{
static union squashfs_inode_header_1 header;
long long start = sBlk.s.inode_table_start + start_block;
int bytes = lookup_entry(inode_table_hash, start);
char *block_ptr = inode_table + bytes + offset;
static struct inode i;
TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
if(bytes == -1)
EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
start);
if(swap) {
squashfs_base_inode_header_1 sinode;
memcpy(&sinode, block_ptr, sizeof(header.base));
SQUASHFS_SWAP_BASE_INODE_HEADER_1(&header.base, &sinode,
sizeof(squashfs_base_inode_header_1));
} else
memcpy(&header.base, block_ptr, sizeof(header.base));
i.uid = (uid_t) uid_table[(header.base.inode_type - 1) /
SQUASHFS_TYPES * 16 + header.base.uid];
if(header.base.inode_type == SQUASHFS_IPC_TYPE) {
squashfs_ipc_inode_header_1 *inodep = &header.ipc;
if(swap) {
squashfs_ipc_inode_header_1 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_IPC_INODE_HEADER_1(inodep, &sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
if(inodep->type == SQUASHFS_SOCKET_TYPE) {
i.mode = S_IFSOCK | header.base.mode;
i.type = SQUASHFS_SOCKET_TYPE;
} else {
i.mode = S_IFIFO | header.base.mode;
i.type = SQUASHFS_FIFO_TYPE;
}
i.uid = (uid_t) uid_table[inodep->offset * 16 + inodep->uid];
} else {
i.mode = lookup_type[(header.base.inode_type - 1) %
SQUASHFS_TYPES + 1] | header.base.mode;
i.type = (header.base.inode_type - 1) % SQUASHFS_TYPES + 1;
}
i.xattr = SQUASHFS_INVALID_XATTR;
i.gid = header.base.guid == 15 ? i.uid :
(uid_t) guid_table[header.base.guid];
i.time = sBlk.s.mkfs_time;
i.inode_number = inode_number ++;
switch(i.type) {
case SQUASHFS_DIR_TYPE: {
squashfs_dir_inode_header_1 *inode = &header.dir;
if(swap) {
squashfs_dir_inode_header_1 sinode;
memcpy(&sinode, block_ptr, sizeof(header.dir));
SQUASHFS_SWAP_DIR_INODE_HEADER_1(inode,
&sinode);
} else
memcpy(inode, block_ptr, sizeof(header.dir));
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
i.time = inode->mtime;
break;
}
case SQUASHFS_FILE_TYPE: {
squashfs_reg_inode_header_1 *inode = &header.reg;
if(swap) {
squashfs_reg_inode_header_1 sinode;
memcpy(&sinode, block_ptr, sizeof(sinode));
SQUASHFS_SWAP_REG_INODE_HEADER_1(inode,
&sinode);
} else
memcpy(inode, block_ptr, sizeof(*inode));
i.data = inode->file_size;
i.time = inode->mtime;
i.blocks = (i.data + sBlk.s.block_size - 1) >>
sBlk.s.block_log;
i.start = inode->start_block;
i.block_ptr = block_ptr + sizeof(*inode);
i.fragment = 0;
i.frag_bytes = 0;
i.offset = 0;
i.sparse = 0;
break;
}
case SQUASHFS_SYMLINK_TYPE: {
squashfs_symlink_inode_header_1 *inodep =
&header.symlink;
if(swap) {
squashfs_symlink_inode_header_1 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.symlink = malloc(inodep->symlink_size + 1);
if(i.symlink == NULL)
EXIT_UNSQUASH("read_inode: failed to malloc "
"symlink data\n");
strncpy(i.symlink, block_ptr +
sizeof(squashfs_symlink_inode_header_1),
inodep->symlink_size);
i.symlink[inodep->symlink_size] = '\0';
i.data = inodep->symlink_size;
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE: {
squashfs_dev_inode_header_1 *inodep = &header.dev;
if(swap) {
squashfs_dev_inode_header_1 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_DEV_INODE_HEADER_1(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.data = inodep->rdev;
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE: {
i.data = 0;
break;
}
default:
EXIT_UNSQUASH("Unknown inode type %d in "
" read_inode_header_1!\n",
header.base.inode_type);
}
return &i;
}
static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
struct inode **i)
{
squashfs_dir_header_2 dirh;
char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]
__attribute__((aligned));
squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer;
long long start;
int bytes;
int dir_count, size;
struct dir_ent *new_dir;
struct dir *dir;
TRACE("squashfs_opendir: inode start block %d, offset %d\n",
block_start, offset);
*i = read_inode(block_start, offset);
dir = malloc(sizeof(struct dir));
if(dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
dir->dir_count = 0;
dir->cur_entry = 0;
dir->mode = (*i)->mode;
dir->uid = (*i)->uid;
dir->guid = (*i)->gid;
dir->mtime = (*i)->time;
dir->xattr = (*i)->xattr;
dir->dirs = NULL;
if ((*i)->data == 0)
/*
* if the directory is empty, skip the unnecessary
* lookup_entry, this fixes the corner case with
* completely empty filesystems where lookup_entry correctly
* returning -1 is incorrectly treated as an error
*/
return dir;
start = sBlk.s.directory_table_start + (*i)->start;
bytes = lookup_entry(directory_table_hash, start);
if(bytes == -1)
EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
"found!\n", block_start);
bytes += (*i)->offset;
size = (*i)->data + bytes;
while(bytes < size) {
if(swap) {
squashfs_dir_header_2 sdirh;
memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
} else
memcpy(&dirh, directory_table + bytes, sizeof(dirh));
dir_count = dirh.count + 1;
TRACE("squashfs_opendir: Read directory header @ byte position "
"%d, %d directory entries\n", bytes, dir_count);
bytes += sizeof(dirh);
/* dir_count should never be larger than SQUASHFS_DIR_COUNT */
if(dir_count > SQUASHFS_DIR_COUNT) {
ERROR("File system corrupted: too many entries in directory\n");
goto corrupted;
}
while(dir_count--) {
if(swap) {
squashfs_dir_entry_2 sdire;
memcpy(&sdire, directory_table + bytes,
sizeof(sdire));
SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
} else
memcpy(dire, directory_table + bytes,
sizeof(*dire));
bytes += sizeof(*dire);
/* size should never be SQUASHFS_NAME_LEN or larger */
if(dire->size >= SQUASHFS_NAME_LEN) {
ERROR("File system corrupted: filename too long\n");
goto corrupted;
}
memcpy(dire->name, directory_table + bytes,
dire->size + 1);
dire->name[dire->size + 1] = '\0';
TRACE("squashfs_opendir: directory entry %s, inode "
"%d:%d, type %d\n", dire->name,
dirh.start_block, dire->offset, dire->type);
if((dir->dir_count % DIR_ENT_SIZE) == 0) {
new_dir = realloc(dir->dirs, (dir->dir_count +
DIR_ENT_SIZE) * sizeof(struct dir_ent));
if(new_dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: "
"realloc failed!\n");
dir->dirs = new_dir;
}
strcpy(dir->dirs[dir->dir_count].name, dire->name);
dir->dirs[dir->dir_count].start_block =
dirh.start_block;
dir->dirs[dir->dir_count].offset = dire->offset;
dir->dirs[dir->dir_count].type = dire->type;
dir->dir_count ++;
bytes += dire->size + 1;
}
}
return dir;
corrupted:
free(dir->dirs);
free(dir);
return NULL;
}
squashfs_operations *read_filesystem_tables_1()
{
long long table_start;
/* Read uid and gid lookup tables */
/* Sanity check super block contents */
if(sBlk.no_guids) {
if(sBlk.guid_start >= sBlk.s.bytes_used) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
/* In 1.x filesystems, there should never be more than 15 gids */
if(sBlk.no_guids > 15) {
ERROR("read_filesystem_tables: gids too large in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
goto corrupted;
table_start = sBlk.guid_start;
} else {
/* no guids, guid_start should be 0 */
if(sBlk.guid_start != 0) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
table_start = sBlk.s.bytes_used;
}
if(sBlk.uid_start >= table_start) {
ERROR("read_filesystem_tables: uid start too large in super block\n");
goto corrupted;
}
/* There should be at least one uid */
if(sBlk.no_uids == 0) {
ERROR("read_filesystem_tables: uid count bad in super block\n");
goto corrupted;
}
/* In 1.x filesystems, there should never be more than 48 uids */
if(sBlk.no_uids > 48) {
ERROR("read_filesystem_tables: uids too large in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
goto corrupted;
table_start = sBlk.uid_start;
/* Read directory table */
/* Sanity check super block contents */
if(sBlk.s.directory_table_start > table_start) {
ERROR("read_filesystem_tables: directory table start too large in super block\n");
goto corrupted;
}
directory_table = read_directory_table(sBlk.s.directory_table_start,
table_start);
if(directory_table == NULL)
goto corrupted;
/* Read inode table */
/* Sanity check super block contents */
if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
ERROR("read_filesystem_tables: inode table start too large in super block\n");
goto corrupted;
}
inode_table = read_inode_table(sBlk.s.inode_table_start,
sBlk.s.directory_table_start);
if(inode_table == NULL)
goto corrupted;
return &ops;
corrupted:
ERROR("File system corruption detected\n");
return NULL;
}
static squashfs_operations ops = {
.opendir = squashfs_opendir,
.read_block_list = read_block_list,
.read_inode = read_inode
};

View File

@@ -0,0 +1,83 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* unsquash-123.c
*
* Helper functions used by unsquash-1, unsquash-2 and unsquash-3.
*/
#include "unsquashfs.h"
#include "squashfs_compat.h"
int read_ids(int ids, long long start, long long end, unsigned int **id_table)
{
/* Note on overflow limits:
* Size of ids is 2^8
* Max length is 2^8*4 or 1024
*/
int res;
int length = ids * sizeof(unsigned int);
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (end - start)) {
ERROR("read_ids: Bad inode count in super block\n");
return FALSE;
}
TRACE("read_ids: no_ids %d\n", ids);
*id_table = malloc(length);
if(*id_table == NULL) {
ERROR("read_ids: failed to allocate uid/gid table\n");
return FALSE;
}
if(swap) {
unsigned int *sid_table = malloc(length);
if(sid_table == NULL) {
ERROR("read_ids: failed to allocate uid/gid table\n");
return FALSE;
}
res = read_fs_bytes(fd, start, length, sid_table);
if(res == FALSE) {
ERROR("read_ids: failed to read uid/gid table"
"\n");
free(sid_table);
return FALSE;
}
SQUASHFS_SWAP_INTS_3((*id_table), sid_table, ids);
free(sid_table);
} else {
res = read_fs_bytes(fd, start, length, *id_table);
if(res == FALSE) {
ERROR("read_ids: failed to read uid/gid table"
"\n");
return FALSE;
}
}
return TRUE;
}

View File

@@ -0,0 +1,529 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2013, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* unsquash-2.c
*/
#include "unsquashfs.h"
#include "squashfs_compat.h"
static squashfs_fragment_entry_2 *fragment_table;
static unsigned int *uid_table, *guid_table;
static char *inode_table, *directory_table;
static squashfs_operations ops;
static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
{
TRACE("read_block_list: blocks %d\n", blocks);
if(swap) {
SQUASHFS_SWAP_INTS_3(block_list, block_ptr, blocks);
} else
memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
}
static int read_fragment_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.fragments is 2^32 (unsigned int)
* Max size of bytes is 2^32*8 or 2^35
* Max indexes is (2^32*8)/8K or 2^22
* Max length is ((2^32*8)/8K)*4 or 2^24 or 16M
*/
int res, i;
long long bytes = SQUASHFS_FRAGMENT_BYTES_2((long long) sBlk.s.fragments);
int indexes = SQUASHFS_FRAGMENT_INDEXES_2((long long) sBlk.s.fragments);
int length = SQUASHFS_FRAGMENT_INDEX_BYTES_2((long long) sBlk.s.fragments);
unsigned int *fragment_table_index;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start- sBlk.s.fragment_table_start)) {
ERROR("read_ids: Bad inode count in super block\n");
return FALSE;
}
TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
"from 0x%llx\n", sBlk.s.fragments, indexes,
sBlk.s.fragment_table_start);
fragment_table_index = malloc(length);
if(fragment_table_index == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table index\n");
fragment_table = malloc(bytes);
if(fragment_table == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table\n");
if(swap) {
unsigned int *sfragment_table_index = malloc(length);
if(sfragment_table_index == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table index\n");
res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
length, sfragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table index\n");
free(sfragment_table_index);
goto failed;
}
SQUASHFS_SWAP_FRAGMENT_INDEXES_2(fragment_table_index,
sfragment_table_index, indexes);
free(sfragment_table_index);
} else {
res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
length, fragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table index\n");
goto failed;
}
}
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, fragment_table_index[i], NULL,
expected, ((char *) fragment_table) + ((long long) i *
SQUASHFS_METADATA_SIZE));
TRACE("Read fragment table block %d, from 0x%x, length %d\n", i,
fragment_table_index[i], length);
if(length == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table block\n");
goto failed;
}
}
if(swap) {
squashfs_fragment_entry_2 sfragment;
for(i = 0; i < sBlk.s.fragments; i++) {
SQUASHFS_SWAP_FRAGMENT_ENTRY_2((&sfragment),
(&fragment_table[i]));
memcpy((char *) &fragment_table[i], (char *) &sfragment,
sizeof(squashfs_fragment_entry_2));
}
}
*table_start = fragment_table_index[0];
free(fragment_table_index);
return TRUE;
failed:
free(fragment_table_index);
return FALSE;
}
static void read_fragment(unsigned int fragment, long long *start_block, int *size)
{
TRACE("read_fragment: reading fragment %d\n", fragment);
squashfs_fragment_entry_2 *fragment_entry = &fragment_table[fragment];
*start_block = fragment_entry->start_block;
*size = fragment_entry->size;
}
static struct inode *read_inode(unsigned int start_block, unsigned int offset)
{
static union squashfs_inode_header_2 header;
long long start = sBlk.s.inode_table_start + start_block;
int bytes = lookup_entry(inode_table_hash, start);
char *block_ptr = inode_table + bytes + offset;
static struct inode i;
TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
if(bytes == -1)
EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
start);
if(swap) {
squashfs_base_inode_header_2 sinode;
memcpy(&sinode, block_ptr, sizeof(header.base));
SQUASHFS_SWAP_BASE_INODE_HEADER_2(&header.base, &sinode,
sizeof(squashfs_base_inode_header_2));
} else
memcpy(&header.base, block_ptr, sizeof(header.base));
i.xattr = SQUASHFS_INVALID_XATTR;
i.uid = (uid_t) uid_table[header.base.uid];
i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid :
(uid_t) guid_table[header.base.guid];
i.mode = lookup_type[header.base.inode_type] | header.base.mode;
i.type = header.base.inode_type;
i.time = sBlk.s.mkfs_time;
i.inode_number = inode_number++;
switch(header.base.inode_type) {
case SQUASHFS_DIR_TYPE: {
squashfs_dir_inode_header_2 *inode = &header.dir;
if(swap) {
squashfs_dir_inode_header_2 sinode;
memcpy(&sinode, block_ptr, sizeof(header.dir));
SQUASHFS_SWAP_DIR_INODE_HEADER_2(&header.dir,
&sinode);
} else
memcpy(&header.dir, block_ptr,
sizeof(header.dir));
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
i.time = inode->mtime;
break;
}
case SQUASHFS_LDIR_TYPE: {
squashfs_ldir_inode_header_2 *inode = &header.ldir;
if(swap) {
squashfs_ldir_inode_header_2 sinode;
memcpy(&sinode, block_ptr, sizeof(header.ldir));
SQUASHFS_SWAP_LDIR_INODE_HEADER_2(&header.ldir,
&sinode);
} else
memcpy(&header.ldir, block_ptr,
sizeof(header.ldir));
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
i.time = inode->mtime;
break;
}
case SQUASHFS_FILE_TYPE: {
squashfs_reg_inode_header_2 *inode = &header.reg;
if(swap) {
squashfs_reg_inode_header_2 sinode;
memcpy(&sinode, block_ptr, sizeof(sinode));
SQUASHFS_SWAP_REG_INODE_HEADER_2(inode,
&sinode);
} else
memcpy(inode, block_ptr, sizeof(*inode));
i.data = inode->file_size;
i.time = inode->mtime;
i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
? 0 : inode->file_size % sBlk.s.block_size;
i.fragment = inode->fragment;
i.offset = inode->offset;
i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
(i.data + sBlk.s.block_size - 1) >>
sBlk.s.block_log : i.data >>
sBlk.s.block_log;
i.start = inode->start_block;
i.sparse = 0;
i.block_ptr = block_ptr + sizeof(*inode);
break;
}
case SQUASHFS_SYMLINK_TYPE: {
squashfs_symlink_inode_header_2 *inodep =
&header.symlink;
if(swap) {
squashfs_symlink_inode_header_2 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.symlink = malloc(inodep->symlink_size + 1);
if(i.symlink == NULL)
EXIT_UNSQUASH("read_inode: failed to malloc "
"symlink data\n");
strncpy(i.symlink, block_ptr +
sizeof(squashfs_symlink_inode_header_2),
inodep->symlink_size);
i.symlink[inodep->symlink_size] = '\0';
i.data = inodep->symlink_size;
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE: {
squashfs_dev_inode_header_2 *inodep = &header.dev;
if(swap) {
squashfs_dev_inode_header_2 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.data = inodep->rdev;
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE:
i.data = 0;
break;
default:
EXIT_UNSQUASH("Unknown inode type %d in "
"read_inode_header_2!\n",
header.base.inode_type);
}
return &i;
}
static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
struct inode **i)
{
squashfs_dir_header_2 dirh;
char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]
__attribute__((aligned));
squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer;
long long start;
int bytes;
int dir_count, size;
struct dir_ent *new_dir;
struct dir *dir;
TRACE("squashfs_opendir: inode start block %d, offset %d\n",
block_start, offset);
*i = read_inode(block_start, offset);
dir = malloc(sizeof(struct dir));
if(dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
dir->dir_count = 0;
dir->cur_entry = 0;
dir->mode = (*i)->mode;
dir->uid = (*i)->uid;
dir->guid = (*i)->gid;
dir->mtime = (*i)->time;
dir->xattr = (*i)->xattr;
dir->dirs = NULL;
if ((*i)->data == 0)
/*
* if the directory is empty, skip the unnecessary
* lookup_entry, this fixes the corner case with
* completely empty filesystems where lookup_entry correctly
* returning -1 is incorrectly treated as an error
*/
return dir;
start = sBlk.s.directory_table_start + (*i)->start;
bytes = lookup_entry(directory_table_hash, start);
if(bytes == -1)
EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
"found!\n", block_start);
bytes += (*i)->offset;
size = (*i)->data + bytes;
while(bytes < size) {
if(swap) {
squashfs_dir_header_2 sdirh;
memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
} else
memcpy(&dirh, directory_table + bytes, sizeof(dirh));
dir_count = dirh.count + 1;
TRACE("squashfs_opendir: Read directory header @ byte position "
"%d, %d directory entries\n", bytes, dir_count);
bytes += sizeof(dirh);
/* dir_count should never be larger than SQUASHFS_DIR_COUNT */
if(dir_count > SQUASHFS_DIR_COUNT) {
ERROR("File system corrupted: too many entries in directory\n");
goto corrupted;
}
while(dir_count--) {
if(swap) {
squashfs_dir_entry_2 sdire;
memcpy(&sdire, directory_table + bytes,
sizeof(sdire));
SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
} else
memcpy(dire, directory_table + bytes,
sizeof(*dire));
bytes += sizeof(*dire);
/* size should never be SQUASHFS_NAME_LEN or larger */
if(dire->size >= SQUASHFS_NAME_LEN) {
ERROR("File system corrupted: filename too long\n");
goto corrupted;
}
memcpy(dire->name, directory_table + bytes,
dire->size + 1);
dire->name[dire->size + 1] = '\0';
TRACE("squashfs_opendir: directory entry %s, inode "
"%d:%d, type %d\n", dire->name,
dirh.start_block, dire->offset, dire->type);
if((dir->dir_count % DIR_ENT_SIZE) == 0) {
new_dir = realloc(dir->dirs, (dir->dir_count +
DIR_ENT_SIZE) * sizeof(struct dir_ent));
if(new_dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: "
"realloc failed!\n");
dir->dirs = new_dir;
}
strcpy(dir->dirs[dir->dir_count].name, dire->name);
dir->dirs[dir->dir_count].start_block =
dirh.start_block;
dir->dirs[dir->dir_count].offset = dire->offset;
dir->dirs[dir->dir_count].type = dire->type;
dir->dir_count ++;
bytes += dire->size + 1;
}
}
return dir;
corrupted:
free(dir->dirs);
free(dir);
return NULL;
}
squashfs_operations *read_filesystem_tables_2()
{
long long table_start;
/* Read uid and gid lookup tables */
/* Sanity check super block contents */
if(sBlk.no_guids) {
if(sBlk.guid_start >= sBlk.s.bytes_used) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
goto corrupted;
table_start = sBlk.guid_start;
} else {
/* no guids, guid_start should be 0 */
if(sBlk.guid_start != 0) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
table_start = sBlk.s.bytes_used;
}
if(sBlk.uid_start >= table_start) {
ERROR("read_filesystem_tables: uid start too large in super block\n");
goto corrupted;
}
/* There should be at least one uid */
if(sBlk.no_uids == 0) {
ERROR("read_filesystem_tables: uid count bad in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
goto corrupted;
table_start = sBlk.uid_start;
/* Read fragment table */
if(sBlk.s.fragments != 0) {
/* Sanity check super block contents */
if(sBlk.s.fragment_table_start >= table_start) {
ERROR("read_filesystem_tables: fragment table start too large in super block\n");
goto corrupted;
}
/* The number of fragments should not exceed the number of inodes */
if(sBlk.s.fragments > sBlk.s.inodes) {
ERROR("read_filesystem_tables: Bad fragment count in super block\n");
goto corrupted;
}
if(read_fragment_table(&table_start) == FALSE)
goto corrupted;
} else {
/*
* Sanity check super block contents - with 0 fragments,
* the fragment table should be empty
*/
if(sBlk.s.fragment_table_start != table_start) {
ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
goto corrupted;
}
}
/* Read directory table */
/* Sanity check super block contents */
if(sBlk.s.directory_table_start > table_start) {
ERROR("read_filesystem_tables: directory table start too large in super block\n");
goto corrupted;
}
directory_table = read_directory_table(sBlk.s.directory_table_start,
table_start);
if(directory_table == NULL)
goto corrupted;
/* Read inode table */
/* Sanity check super block contents */
if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
ERROR("read_filesystem_tables: inode table start too large in super block\n");
goto corrupted;
}
inode_table = read_inode_table(sBlk.s.inode_table_start,
sBlk.s.directory_table_start);
if(inode_table == NULL)
goto corrupted;
return &ops;
corrupted:
ERROR("File system corruption detected\n");
return NULL;
}
static squashfs_operations ops = {
.opendir = squashfs_opendir,
.read_fragment = read_fragment,
.read_block_list = read_block_list,
.read_inode = read_inode
};

View File

@@ -0,0 +1,632 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* unsquash-3.c
*/
#include "unsquashfs.h"
#include "squashfs_compat.h"
static squashfs_fragment_entry_3 *fragment_table;
static unsigned int *uid_table, *guid_table;
static char *inode_table, *directory_table;
static squashfs_operations ops;
static long long *salloc_index_table(int indexes)
{
static long long *alloc_table = NULL;
static int alloc_size = 0;
int length = indexes * sizeof(long long);
if(alloc_size < length || length == 0) {
long long *table = realloc(alloc_table, length);
if(table == NULL && length !=0 )
EXIT_UNSQUASH("alloc_index_table: failed to allocate "
"index table\n");
alloc_table = table;
alloc_size = length;
}
return alloc_table;
}
static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
{
TRACE("read_block_list: blocks %d\n", blocks);
if(swap) {
SQUASHFS_SWAP_INTS_3(block_list, block_ptr, blocks);
} else
memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
}
static int read_fragment_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.fragments is 2^32 (unsigned int)
* Max size of bytes is 2^32*16 or 2^36
* Max indexes is (2^32*16)/8K or 2^23
* Max length is ((2^32*16)/8K)*8 or 2^26 or 64M
*/
int res, i;
long long bytes = SQUASHFS_FRAGMENT_BYTES_3((long long) sBlk.s.fragments);
int indexes = SQUASHFS_FRAGMENT_INDEXES_3((long long) sBlk.s.fragments);
int length = SQUASHFS_FRAGMENT_INDEX_BYTES_3((long long) sBlk.s.fragments);
long long *fragment_table_index;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start - sBlk.s.fragment_table_start)) {
ERROR("read_fragment_table: Bad fragment count in super block\n");
return FALSE;
}
TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
"from 0x%llx\n", sBlk.s.fragments, indexes,
sBlk.s.fragment_table_start);
fragment_table_index = alloc_index_table(indexes);
fragment_table = malloc(bytes);
if(fragment_table == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table\n");
if(swap) {
long long *sfragment_table_index = salloc_index_table(indexes);
res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
length, sfragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table index\n");
return FALSE;
}
SQUASHFS_SWAP_FRAGMENT_INDEXES_3(fragment_table_index,
sfragment_table_index, indexes);
} else {
res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
length, fragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table index\n");
return FALSE;
}
}
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, fragment_table_index[i], NULL,
expected, ((char *) fragment_table) + ((long long) i *
SQUASHFS_METADATA_SIZE));
TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
i, fragment_table_index[i], length);
if(length == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table block\n");
return FALSE;
}
}
if(swap) {
squashfs_fragment_entry_3 sfragment;
for(i = 0; i < sBlk.s.fragments; i++) {
SQUASHFS_SWAP_FRAGMENT_ENTRY_3((&sfragment),
(&fragment_table[i]));
memcpy((char *) &fragment_table[i], (char *) &sfragment,
sizeof(squashfs_fragment_entry_3));
}
}
*table_start = fragment_table_index[0];
return TRUE;
}
static void read_fragment(unsigned int fragment, long long *start_block, int *size)
{
TRACE("read_fragment: reading fragment %d\n", fragment);
squashfs_fragment_entry_3 *fragment_entry = &fragment_table[fragment];
*start_block = fragment_entry->start_block;
*size = fragment_entry->size;
}
static struct inode *read_inode(unsigned int start_block, unsigned int offset)
{
static union squashfs_inode_header_3 header;
long long start = sBlk.s.inode_table_start + start_block;
int bytes = lookup_entry(inode_table_hash, start);
char *block_ptr = inode_table + bytes + offset;
static struct inode i;
TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
if(bytes == -1)
EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
start);
if(swap) {
squashfs_base_inode_header_3 sinode;
memcpy(&sinode, block_ptr, sizeof(header.base));
SQUASHFS_SWAP_BASE_INODE_HEADER_3(&header.base, &sinode,
sizeof(squashfs_base_inode_header_3));
} else
memcpy(&header.base, block_ptr, sizeof(header.base));
i.xattr = SQUASHFS_INVALID_XATTR;
i.uid = (uid_t) uid_table[header.base.uid];
i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid :
(uid_t) guid_table[header.base.guid];
i.mode = lookup_type[header.base.inode_type] | header.base.mode;
i.type = header.base.inode_type;
i.time = header.base.mtime;
i.inode_number = header.base.inode_number;
switch(header.base.inode_type) {
case SQUASHFS_DIR_TYPE: {
squashfs_dir_inode_header_3 *inode = &header.dir;
if(swap) {
squashfs_dir_inode_header_3 sinode;
memcpy(&sinode, block_ptr, sizeof(header.dir));
SQUASHFS_SWAP_DIR_INODE_HEADER_3(&header.dir,
&sinode);
} else
memcpy(&header.dir, block_ptr,
sizeof(header.dir));
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
break;
}
case SQUASHFS_LDIR_TYPE: {
squashfs_ldir_inode_header_3 *inode = &header.ldir;
if(swap) {
squashfs_ldir_inode_header_3 sinode;
memcpy(&sinode, block_ptr, sizeof(header.ldir));
SQUASHFS_SWAP_LDIR_INODE_HEADER_3(&header.ldir,
&sinode);
} else
memcpy(&header.ldir, block_ptr,
sizeof(header.ldir));
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
break;
}
case SQUASHFS_FILE_TYPE: {
squashfs_reg_inode_header_3 *inode = &header.reg;
if(swap) {
squashfs_reg_inode_header_3 sinode;
memcpy(&sinode, block_ptr, sizeof(sinode));
SQUASHFS_SWAP_REG_INODE_HEADER_3(inode,
&sinode);
} else
memcpy(inode, block_ptr, sizeof(*inode));
i.data = inode->file_size;
i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
? 0 : inode->file_size % sBlk.s.block_size;
i.fragment = inode->fragment;
i.offset = inode->offset;
i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
(i.data + sBlk.s.block_size - 1) >>
sBlk.s.block_log :
i.data >> sBlk.s.block_log;
i.start = inode->start_block;
i.sparse = 1;
i.block_ptr = block_ptr + sizeof(*inode);
break;
}
case SQUASHFS_LREG_TYPE: {
squashfs_lreg_inode_header_3 *inode = &header.lreg;
if(swap) {
squashfs_lreg_inode_header_3 sinode;
memcpy(&sinode, block_ptr, sizeof(sinode));
SQUASHFS_SWAP_LREG_INODE_HEADER_3(inode,
&sinode);
} else
memcpy(inode, block_ptr, sizeof(*inode));
i.data = inode->file_size;
i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
? 0 : inode->file_size % sBlk.s.block_size;
i.fragment = inode->fragment;
i.offset = inode->offset;
i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
(inode->file_size + sBlk.s.block_size - 1) >>
sBlk.s.block_log :
inode->file_size >> sBlk.s.block_log;
i.start = inode->start_block;
i.sparse = 1;
i.block_ptr = block_ptr + sizeof(*inode);
break;
}
case SQUASHFS_SYMLINK_TYPE: {
squashfs_symlink_inode_header_3 *inodep =
&header.symlink;
if(swap) {
squashfs_symlink_inode_header_3 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_SYMLINK_INODE_HEADER_3(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.symlink = malloc(inodep->symlink_size + 1);
if(i.symlink == NULL)
EXIT_UNSQUASH("read_inode: failed to malloc "
"symlink data\n");
strncpy(i.symlink, block_ptr +
sizeof(squashfs_symlink_inode_header_3),
inodep->symlink_size);
i.symlink[inodep->symlink_size] = '\0';
i.data = inodep->symlink_size;
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE: {
squashfs_dev_inode_header_3 *inodep = &header.dev;
if(swap) {
squashfs_dev_inode_header_3 sinodep;
memcpy(&sinodep, block_ptr, sizeof(sinodep));
SQUASHFS_SWAP_DEV_INODE_HEADER_3(inodep,
&sinodep);
} else
memcpy(inodep, block_ptr, sizeof(*inodep));
i.data = inodep->rdev;
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE:
i.data = 0;
break;
default:
EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n",
header.base.inode_type);
}
return &i;
}
static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
struct inode **i)
{
squashfs_dir_header_3 dirh;
char buffer[sizeof(squashfs_dir_entry_3) + SQUASHFS_NAME_LEN + 1]
__attribute__((aligned));
squashfs_dir_entry_3 *dire = (squashfs_dir_entry_3 *) buffer;
long long start;
int bytes;
int dir_count, size;
struct dir_ent *new_dir;
struct dir *dir;
TRACE("squashfs_opendir: inode start block %d, offset %d\n",
block_start, offset);
*i = read_inode(block_start, offset);
dir = malloc(sizeof(struct dir));
if(dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
dir->dir_count = 0;
dir->cur_entry = 0;
dir->mode = (*i)->mode;
dir->uid = (*i)->uid;
dir->guid = (*i)->gid;
dir->mtime = (*i)->time;
dir->xattr = (*i)->xattr;
dir->dirs = NULL;
if ((*i)->data == 3)
/*
* if the directory is empty, skip the unnecessary
* lookup_entry, this fixes the corner case with
* completely empty filesystems where lookup_entry correctly
* returning -1 is incorrectly treated as an error
*/
return dir;
start = sBlk.s.directory_table_start + (*i)->start;
bytes = lookup_entry(directory_table_hash, start);
if(bytes == -1)
EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
"found!\n", block_start);
bytes += (*i)->offset;
size = (*i)->data + bytes - 3;
while(bytes < size) {
if(swap) {
squashfs_dir_header_3 sdirh;
memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
SQUASHFS_SWAP_DIR_HEADER_3(&dirh, &sdirh);
} else
memcpy(&dirh, directory_table + bytes, sizeof(dirh));
dir_count = dirh.count + 1;
TRACE("squashfs_opendir: Read directory header @ byte position "
"%d, %d directory entries\n", bytes, dir_count);
bytes += sizeof(dirh);
/* dir_count should never be larger than SQUASHFS_DIR_COUNT */
if(dir_count > SQUASHFS_DIR_COUNT) {
ERROR("File system corrupted: too many entries in directory\n");
goto corrupted;
}
while(dir_count--) {
if(swap) {
squashfs_dir_entry_3 sdire;
memcpy(&sdire, directory_table + bytes,
sizeof(sdire));
SQUASHFS_SWAP_DIR_ENTRY_3(dire, &sdire);
} else
memcpy(dire, directory_table + bytes,
sizeof(*dire));
bytes += sizeof(*dire);
/* size should never be SQUASHFS_NAME_LEN or larger */
if(dire->size >= SQUASHFS_NAME_LEN) {
ERROR("File system corrupted: filename too long\n");
goto corrupted;
}
memcpy(dire->name, directory_table + bytes,
dire->size + 1);
dire->name[dire->size + 1] = '\0';
TRACE("squashfs_opendir: directory entry %s, inode "
"%d:%d, type %d\n", dire->name,
dirh.start_block, dire->offset, dire->type);
if((dir->dir_count % DIR_ENT_SIZE) == 0) {
new_dir = realloc(dir->dirs, (dir->dir_count +
DIR_ENT_SIZE) * sizeof(struct dir_ent));
if(new_dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: "
"realloc failed!\n");
dir->dirs = new_dir;
}
strcpy(dir->dirs[dir->dir_count].name, dire->name);
dir->dirs[dir->dir_count].start_block =
dirh.start_block;
dir->dirs[dir->dir_count].offset = dire->offset;
dir->dirs[dir->dir_count].type = dire->type;
dir->dir_count ++;
bytes += dire->size + 1;
}
}
return dir;
corrupted:
free(dir->dirs);
free(dir);
return NULL;
}
static int parse_exports_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.inodes is 2^32 (unsigned int)
* Max indexes is (2^32*8)/8K or 2^22
* Max length is ((2^32*8)/8K)*8 or 2^25
*/
int res;
int indexes = SQUASHFS_LOOKUP_BLOCKS_3((long long) sBlk.s.inodes);
int length = SQUASHFS_LOOKUP_BLOCK_BYTES_3((long long) sBlk.s.inodes);
long long *export_index_table;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start - sBlk.s.lookup_table_start)) {
ERROR("parse_exports_table: Bad inode count in super block\n");
return FALSE;
}
export_index_table = alloc_index_table(indexes);
if(swap) {
long long *sexport_index_table = salloc_index_table(indexes);
res = read_fs_bytes(fd, sBlk.s.lookup_table_start,
length, sexport_index_table);
if(res == FALSE) {
ERROR("parse_exorts_table: failed to read export "
"index table\n");
return FALSE;
}
SQUASHFS_SWAP_LOOKUP_BLOCKS_3(export_index_table,
sexport_index_table, indexes);
} else {
res = read_fs_bytes(fd, sBlk.s.lookup_table_start, length,
export_index_table);
if(res == FALSE) {
ERROR("parse_exorts_table: failed to read export "
"index table\n");
return FALSE;
}
}
/*
* export_index_table[0] stores the start of the compressed export blocks.
* This by definition is also the end of the previous filesystem
* table - the fragment table.
*/
*table_start = export_index_table[0];
return TRUE;
}
squashfs_operations *read_filesystem_tables_3()
{
long long table_start;
/* Read uid and gid lookup tables */
/* Sanity check super block contents */
if(sBlk.no_guids) {
if(sBlk.guid_start >= sBlk.s.bytes_used) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
goto corrupted;
table_start = sBlk.guid_start;
} else {
/* no guids, guid_start should be 0 */
if(sBlk.guid_start != 0) {
ERROR("read_filesystem_tables: gid start too large in super block\n");
goto corrupted;
}
table_start = sBlk.s.bytes_used;
}
if(sBlk.uid_start >= table_start) {
ERROR("read_filesystem_tables: uid start too large in super block\n");
goto corrupted;
}
/* There should be at least one uid */
if(sBlk.no_uids == 0) {
ERROR("read_filesystem_tables: uid count bad in super block\n");
goto corrupted;
}
if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
goto corrupted;
table_start = sBlk.uid_start;
/* Read exports table */
if(sBlk.s.lookup_table_start != SQUASHFS_INVALID_BLK) {
/* sanity check super block contents */
if(sBlk.s.lookup_table_start >= table_start) {
ERROR("read_filesystem_tables: lookup table start too large in super block\n");
goto corrupted;
}
if(parse_exports_table(&table_start) == FALSE)
goto corrupted;
}
/* Read fragment table */
if(sBlk.s.fragments != 0) {
/* Sanity check super block contents */
if(sBlk.s.fragment_table_start >= table_start) {
ERROR("read_filesystem_tables: fragment table start too large in super block\n");
goto corrupted;
}
/* The number of fragments should not exceed the number of inodes */
if(sBlk.s.fragments > sBlk.s.inodes) {
ERROR("read_filesystem_tables: Bad fragment count in super block\n");
goto corrupted;
}
if(read_fragment_table(&table_start) == FALSE)
goto corrupted;
} else {
/*
* Sanity check super block contents - with 0 fragments,
* the fragment table should be empty
*/
if(sBlk.s.fragment_table_start != table_start) {
ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
goto corrupted;
}
}
/* Read directory table */
/* Sanity check super block contents */
if(sBlk.s.directory_table_start > table_start) {
ERROR("read_filesystem_tables: directory table start too large in super block\n");
goto corrupted;
}
directory_table = read_directory_table(sBlk.s.directory_table_start,
table_start);
if(directory_table == NULL)
goto corrupted;
/* Read inode table */
/* Sanity check super block contents */
if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
ERROR("read_filesystem_tables: inode table start too large in super block\n");
goto corrupted;
}
inode_table = read_inode_table(sBlk.s.inode_table_start,
sBlk.s.directory_table_start);
if(inode_table == NULL)
goto corrupted;
alloc_index_table(0);
salloc_index_table(0);
return &ops;
corrupted:
ERROR("File system corruption detected\n");
alloc_index_table(0);
salloc_index_table(0);
return NULL;
}
static squashfs_operations ops = {
.opendir = squashfs_opendir,
.read_fragment = read_fragment,
.read_block_list = read_block_list,
.read_inode = read_inode
};

View File

@@ -0,0 +1,47 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* unsquash-34.c
*
* Helper functions used by unsquash-3 and unsquash-4.
*/
#include "unsquashfs.h"
long long *alloc_index_table(int indexes)
{
static long long *alloc_table = NULL;
static int alloc_size = 0;
int length = indexes * sizeof(long long);
if(alloc_size < length || length == 0) {
long long *table = realloc(alloc_table, length);
if(table == NULL && length !=0)
EXIT_UNSQUASH("alloc_index_table: failed to allocate "
"index table\n");
alloc_table = table;
alloc_size = length;
}
return alloc_table;
}

View File

@@ -0,0 +1,621 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* unsquash-4.c
*/
#include "unsquashfs.h"
#include "squashfs_swap.h"
#include "xattr.h"
static struct squashfs_fragment_entry *fragment_table;
static unsigned int *id_table;
static char *inode_table, *directory_table;
static squashfs_operations ops;
static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
{
TRACE("read_block_list: blocks %d\n", blocks);
memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
SQUASHFS_INSWAP_INTS(block_list, blocks);
}
static int read_fragment_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.fragments is 2^32 (unsigned int)
* Max size of bytes is 2^32*16 or 2^36
* Max indexes is (2^32*16)/8K or 2^23
* Max length is ((2^32*16)/8K)*8 or 2^26 or 64M
*/
int res, i;
long long bytes = SQUASHFS_FRAGMENT_BYTES((long long) sBlk.s.fragments);
int indexes = SQUASHFS_FRAGMENT_INDEXES((long long) sBlk.s.fragments);
int length = SQUASHFS_FRAGMENT_INDEX_BYTES((long long) sBlk.s.fragments);
long long *fragment_table_index;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start - sBlk.s.fragment_table_start)) {
ERROR("read_fragment_table: Bad fragment count in super block\n");
return FALSE;
}
TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
"from 0x%llx\n", sBlk.s.fragments, indexes,
sBlk.s.fragment_table_start);
fragment_table_index = alloc_index_table(indexes);
fragment_table = malloc(bytes);
if(fragment_table == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table\n");
res = read_fs_bytes(fd, sBlk.s.fragment_table_start, length,
fragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment table "
"index\n");
return FALSE;
}
SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index, indexes);
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, fragment_table_index[i], NULL,
expected, ((char *) fragment_table) + (i *
SQUASHFS_METADATA_SIZE));
TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
i, fragment_table_index[i], length);
if(length == FALSE) {
ERROR("read_fragment_table: failed to read fragment "
"table index\n");
return FALSE;
}
}
for(i = 0; i < sBlk.s.fragments; i++)
SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]);
*table_start = fragment_table_index[0];
return TRUE;
}
static void read_fragment(unsigned int fragment, long long *start_block, int *size)
{
TRACE("read_fragment: reading fragment %d\n", fragment);
struct squashfs_fragment_entry *fragment_entry;
fragment_entry = &fragment_table[fragment];
*start_block = fragment_entry->start_block;
*size = fragment_entry->size;
}
static struct inode *read_inode(unsigned int start_block, unsigned int offset)
{
static union squashfs_inode_header header;
long long start = sBlk.s.inode_table_start + start_block;
long long bytes = lookup_entry(inode_table_hash, start);
char *block_ptr = inode_table + bytes + offset;
static struct inode i;
TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset);
if(bytes == -1)
EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
start);
SQUASHFS_SWAP_BASE_INODE_HEADER(block_ptr, &header.base);
i.uid = (uid_t) id_table[header.base.uid];
i.gid = (uid_t) id_table[header.base.guid];
i.mode = lookup_type[header.base.inode_type] | header.base.mode;
i.type = header.base.inode_type;
i.time = header.base.mtime;
i.inode_number = header.base.inode_number;
switch(header.base.inode_type) {
case SQUASHFS_DIR_TYPE: {
struct squashfs_dir_inode_header *inode = &header.dir;
SQUASHFS_SWAP_DIR_INODE_HEADER(block_ptr, inode);
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
i.xattr = SQUASHFS_INVALID_XATTR;
break;
}
case SQUASHFS_LDIR_TYPE: {
struct squashfs_ldir_inode_header *inode = &header.ldir;
SQUASHFS_SWAP_LDIR_INODE_HEADER(block_ptr, inode);
i.data = inode->file_size;
i.offset = inode->offset;
i.start = inode->start_block;
i.xattr = inode->xattr;
break;
}
case SQUASHFS_FILE_TYPE: {
struct squashfs_reg_inode_header *inode = &header.reg;
SQUASHFS_SWAP_REG_INODE_HEADER(block_ptr, inode);
i.data = inode->file_size;
i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
? 0 : inode->file_size % sBlk.s.block_size;
i.fragment = inode->fragment;
i.offset = inode->offset;
i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
(i.data + sBlk.s.block_size - 1) >>
sBlk.s.block_log :
i.data >> sBlk.s.block_log;
i.start = inode->start_block;
i.sparse = 0;
i.block_ptr = block_ptr + sizeof(*inode);
i.xattr = SQUASHFS_INVALID_XATTR;
break;
}
case SQUASHFS_LREG_TYPE: {
struct squashfs_lreg_inode_header *inode = &header.lreg;
SQUASHFS_SWAP_LREG_INODE_HEADER(block_ptr, inode);
i.data = inode->file_size;
i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
? 0 : inode->file_size % sBlk.s.block_size;
i.fragment = inode->fragment;
i.offset = inode->offset;
i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
(inode->file_size + sBlk.s.block_size - 1) >>
sBlk.s.block_log :
inode->file_size >> sBlk.s.block_log;
i.start = inode->start_block;
i.sparse = inode->sparse != 0;
i.block_ptr = block_ptr + sizeof(*inode);
i.xattr = inode->xattr;
break;
}
case SQUASHFS_SYMLINK_TYPE:
case SQUASHFS_LSYMLINK_TYPE: {
struct squashfs_symlink_inode_header *inode = &header.symlink;
SQUASHFS_SWAP_SYMLINK_INODE_HEADER(block_ptr, inode);
i.symlink = malloc(inode->symlink_size + 1);
if(i.symlink == NULL)
EXIT_UNSQUASH("read_inode: failed to malloc "
"symlink data\n");
strncpy(i.symlink, block_ptr +
sizeof(struct squashfs_symlink_inode_header),
inode->symlink_size);
i.symlink[inode->symlink_size] = '\0';
i.data = inode->symlink_size;
if(header.base.inode_type == SQUASHFS_LSYMLINK_TYPE)
SQUASHFS_SWAP_INTS(block_ptr +
sizeof(struct squashfs_symlink_inode_header) +
inode->symlink_size, &i.xattr, 1);
else
i.xattr = SQUASHFS_INVALID_XATTR;
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE: {
struct squashfs_dev_inode_header *inode = &header.dev;
SQUASHFS_SWAP_DEV_INODE_HEADER(block_ptr, inode);
i.data = inode->rdev;
i.xattr = SQUASHFS_INVALID_XATTR;
break;
}
case SQUASHFS_LBLKDEV_TYPE:
case SQUASHFS_LCHRDEV_TYPE: {
struct squashfs_ldev_inode_header *inode = &header.ldev;
SQUASHFS_SWAP_LDEV_INODE_HEADER(block_ptr, inode);
i.data = inode->rdev;
i.xattr = inode->xattr;
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE:
i.data = 0;
i.xattr = SQUASHFS_INVALID_XATTR;
break;
case SQUASHFS_LFIFO_TYPE:
case SQUASHFS_LSOCKET_TYPE: {
struct squashfs_lipc_inode_header *inode = &header.lipc;
SQUASHFS_SWAP_LIPC_INODE_HEADER(block_ptr, inode);
i.data = 0;
i.xattr = inode->xattr;
break;
}
default:
EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n",
header.base.inode_type);
}
return &i;
}
static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
struct inode **i)
{
struct squashfs_dir_header dirh;
char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]
__attribute__((aligned));
struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
long long start;
long long bytes;
int dir_count, size;
struct dir_ent *new_dir;
struct dir *dir;
TRACE("squashfs_opendir: inode start block %d, offset %d\n",
block_start, offset);
*i = read_inode(block_start, offset);
dir = malloc(sizeof(struct dir));
if(dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
dir->dir_count = 0;
dir->cur_entry = 0;
dir->mode = (*i)->mode;
dir->uid = (*i)->uid;
dir->guid = (*i)->gid;
dir->mtime = (*i)->time;
dir->xattr = (*i)->xattr;
dir->dirs = NULL;
if ((*i)->data == 3)
/*
* if the directory is empty, skip the unnecessary
* lookup_entry, this fixes the corner case with
* completely empty filesystems where lookup_entry correctly
* returning -1 is incorrectly treated as an error
*/
return dir;
start = sBlk.s.directory_table_start + (*i)->start;
bytes = lookup_entry(directory_table_hash, start);
if(bytes == -1)
EXIT_UNSQUASH("squashfs_opendir: directory block %lld not "
"found!\n", start);
bytes += (*i)->offset;
size = (*i)->data + bytes - 3;
while(bytes < size) {
SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh);
dir_count = dirh.count + 1;
TRACE("squashfs_opendir: Read directory header @ byte position "
"%d, %d directory entries\n", bytes, dir_count);
bytes += sizeof(dirh);
/* dir_count should never be larger than SQUASHFS_DIR_COUNT */
if(dir_count > SQUASHFS_DIR_COUNT) {
ERROR("File system corrupted: too many entries in directory\n");
goto corrupted;
}
while(dir_count--) {
SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire);
bytes += sizeof(*dire);
/* size should never be SQUASHFS_NAME_LEN or larger */
if(dire->size >= SQUASHFS_NAME_LEN) {
ERROR("File system corrupted: filename too long\n");
goto corrupted;
}
memcpy(dire->name, directory_table + bytes,
dire->size + 1);
dire->name[dire->size + 1] = '\0';
TRACE("squashfs_opendir: directory entry %s, inode "
"%d:%d, type %d\n", dire->name,
dirh.start_block, dire->offset, dire->type);
if((dir->dir_count % DIR_ENT_SIZE) == 0) {
new_dir = realloc(dir->dirs, (dir->dir_count +
DIR_ENT_SIZE) * sizeof(struct dir_ent));
if(new_dir == NULL)
EXIT_UNSQUASH("squashfs_opendir: "
"realloc failed!\n");
dir->dirs = new_dir;
}
strcpy(dir->dirs[dir->dir_count].name, dire->name);
dir->dirs[dir->dir_count].start_block =
dirh.start_block;
dir->dirs[dir->dir_count].offset = dire->offset;
dir->dirs[dir->dir_count].type = dire->type;
dir->dir_count ++;
bytes += dire->size + 1;
}
}
return dir;
corrupted:
free(dir->dirs);
free(dir);
return NULL;
}
static int read_id_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.no_ids is 2^16 (unsigned short)
* Max size of bytes is 2^16*4 or 256K
* Max indexes is (2^16*4)/8K or 32
* Max length is ((2^16*4)/8K)*8 or 256
*/
int res, i;
int bytes = SQUASHFS_ID_BYTES(sBlk.s.no_ids);
int indexes = SQUASHFS_ID_BLOCKS(sBlk.s.no_ids);
int length = SQUASHFS_ID_BLOCK_BYTES(sBlk.s.no_ids);
long long *id_index_table;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start - sBlk.s.id_table_start)) {
ERROR("read_id_table: Bad id count in super block\n");
return FALSE;
}
TRACE("read_id_table: no_ids %d\n", sBlk.s.no_ids);
id_index_table = alloc_index_table(indexes);
id_table = malloc(bytes);
if(id_table == NULL) {
ERROR("read_id_table: failed to allocate id table\n");
return FALSE;
}
res = read_fs_bytes(fd, sBlk.s.id_table_start, length, id_index_table);
if(res == FALSE) {
ERROR("read_id_table: failed to read id index table\n");
return FALSE;
}
SQUASHFS_INSWAP_ID_BLOCKS(id_index_table, indexes);
/*
* id_index_table[0] stores the start of the compressed id blocks.
* This by definition is also the end of the previous filesystem
* table - this may be the exports table if it is present, or the
* fragments table if it isn't.
*/
*table_start = id_index_table[0];
for(i = 0; i < indexes; i++) {
int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
bytes & (SQUASHFS_METADATA_SIZE - 1);
res = read_block(fd, id_index_table[i], NULL, expected,
((char *) id_table) + i * SQUASHFS_METADATA_SIZE);
if(res == FALSE) {
ERROR("read_id_table: failed to read id table block"
"\n");
return FALSE;
}
}
SQUASHFS_INSWAP_INTS(id_table, sBlk.s.no_ids);
return TRUE;
}
static int parse_exports_table(long long *table_start)
{
/*
* Note on overflow limits:
* Size of SBlk.s.inodes is 2^32 (unsigned int)
* Max indexes is (2^32*8)/8K or 2^22
* Max length is ((2^32*8)/8K)*8 or 2^25
*/
int res;
int indexes = SQUASHFS_LOOKUP_BLOCKS((long long) sBlk.s.inodes);
int length = SQUASHFS_LOOKUP_BLOCK_BYTES((long long) sBlk.s.inodes);
long long *export_index_table;
/*
* The size of the index table (length bytes) should match the
* table start and end points
*/
if(length != (*table_start - sBlk.s.lookup_table_start)) {
ERROR("parse_exports_table: Bad inode count in super block\n");
return FALSE;
}
export_index_table = alloc_index_table(indexes);
res = read_fs_bytes(fd, sBlk.s.lookup_table_start, length,
export_index_table);
if(res == FALSE) {
ERROR("parse_exports_table: failed to read export index table\n");
return FALSE;
}
SQUASHFS_INSWAP_LOOKUP_BLOCKS(export_index_table, indexes);
/*
* export_index_table[0] stores the start of the compressed export blocks.
* This by definition is also the end of the previous filesystem
* table - the fragment table.
*/
*table_start = export_index_table[0];
return TRUE;
}
squashfs_operations *read_filesystem_tables_4()
{
long long table_start;
/* Read xattrs */
if(sBlk.s.xattr_id_table_start != SQUASHFS_INVALID_BLK) {
/* sanity check super block contents */
if(sBlk.s.xattr_id_table_start >= sBlk.s.bytes_used) {
ERROR("read_filesystem_tables: xattr id table start too large in super block\n");
goto corrupted;
}
if(read_xattrs_from_disk(fd, &sBlk.s, no_xattrs, &table_start) == 0)
goto corrupted;
} else
table_start = sBlk.s.bytes_used;
/* Read id lookup table */
/* Sanity check super block contents */
if(sBlk.s.id_table_start >= table_start) {
ERROR("read_filesystem_tables: id table start too large in super block\n");
goto corrupted;
}
/* there should always be at least one id */
if(sBlk.s.no_ids == 0) {
ERROR("read_filesystem_tables: Bad id count in super block\n");
goto corrupted;
}
/*
* the number of ids can never be more than double the number of inodes
* (the maximum is a unique uid and gid for each inode).
*/
if(sBlk.s.no_ids > (sBlk.s.inodes * 2L)) {
ERROR("read_filesystem_tables: Bad id count in super block\n");
goto corrupted;
}
if(read_id_table(&table_start) == FALSE)
goto corrupted;
/* Read exports table */
if(sBlk.s.lookup_table_start != SQUASHFS_INVALID_BLK) {
/* sanity check super block contents */
if(sBlk.s.lookup_table_start >= table_start) {
ERROR("read_filesystem_tables: lookup table start too large in super block\n");
goto corrupted;
}
if(parse_exports_table(&table_start) == FALSE)
goto corrupted;
}
/* Read fragment table */
if(sBlk.s.fragments != 0) {
/* Sanity check super block contents */
if(sBlk.s.fragment_table_start >= table_start) {
ERROR("read_filesystem_tables: fragment table start too large in super block\n");
goto corrupted;
}
/* The number of fragments should not exceed the number of inodes */
if(sBlk.s.fragments > sBlk.s.inodes) {
ERROR("read_filesystem_tables: Bad fragment count in super block\n");
goto corrupted;
}
if(read_fragment_table(&table_start) == FALSE)
goto corrupted;
} else {
/*
* Sanity check super block contents - with 0 fragments,
* the fragment table should be empty
*/
if(sBlk.s.fragment_table_start != table_start) {
ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
goto corrupted;
}
}
/* Read directory table */
/* Sanity check super block contents */
if(sBlk.s.directory_table_start > table_start) {
ERROR("read_filesystem_tables: directory table start too large in super block\n");
goto corrupted;
}
directory_table = read_directory_table(sBlk.s.directory_table_start,
table_start);
if(directory_table == NULL)
goto corrupted;
/* Read inode table */
/* Sanity check super block contents */
if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
ERROR("read_filesystem_tables: inode table start too large in super block\n");
goto corrupted;
}
inode_table = read_inode_table(sBlk.s.inode_table_start,
sBlk.s.directory_table_start);
if(inode_table == NULL)
goto corrupted;
if(no_xattrs)
sBlk.s.xattr_id_table_start = SQUASHFS_INVALID_BLK;
alloc_index_table(0);
return &ops;
corrupted:
ERROR("File system corruption detected\n");
alloc_index_table(0);
return NULL;
}
static squashfs_operations ops = {
.opendir = squashfs_opendir,
.read_fragment = read_fragment,
.read_block_list = read_block_list,
.read_inode = read_inode
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,264 @@
#ifndef UNSQUASHFS_H
#define UNSQUASHFS_H
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2009, 2010, 2013, 2014, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* unsquashfs.h
*/
#define TRUE 1
#define FALSE 0
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <utime.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <regex.h>
#include <signal.h>
#include <pthread.h>
#include <math.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#ifndef linux
#define __BYTE_ORDER BYTE_ORDER
#define __BIG_ENDIAN BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#else
#include <endian.h>
#endif
#include "squashfs_fs.h"
#include "error.h"
#define CALCULATE_HASH(start) (start & 0xffff)
/*
* Unified superblock containing fields for all superblocks
*/
struct super_block {
struct squashfs_super_block s;
/* fields only used by squashfs 3 and earlier layouts */
unsigned int no_uids;
unsigned int no_guids;
long long uid_start;
long long guid_start;
};
struct hash_table_entry {
long long start;
long long bytes;
struct hash_table_entry *next;
};
struct inode {
int blocks;
char *block_ptr;
long long data;
int fragment;
int frag_bytes;
gid_t gid;
int inode_number;
int mode;
int offset;
long long start;
char *symlink;
time_t time;
int type;
uid_t uid;
char sparse;
unsigned int xattr;
};
typedef struct squashfs_operations {
struct dir *(*opendir)(unsigned int block_start,
unsigned int offset, struct inode **i);
void (*read_fragment)(unsigned int fragment, long long *start_block,
int *size);
void (*read_block_list)(unsigned int *block_list, char *block_ptr,
int blocks);
struct inode *(*read_inode)(unsigned int start_block,
unsigned int offset);
} squashfs_operations;
struct test {
int mask;
int value;
int position;
char mode;
};
/* Cache status struct. Caches are used to keep
track of memory buffers passed between different threads */
struct cache {
int max_buffers;
int count;
int used;
int buffer_size;
int wait_free;
int wait_pending;
pthread_mutex_t mutex;
pthread_cond_t wait_for_free;
pthread_cond_t wait_for_pending;
struct cache_entry *free_list;
struct cache_entry *hash_table[65536];
};
/* struct describing a cache entry passed between threads */
struct cache_entry {
struct cache *cache;
long long block;
int size;
int used;
int error;
int pending;
struct cache_entry *hash_next;
struct cache_entry *hash_prev;
struct cache_entry *free_next;
struct cache_entry *free_prev;
char *data;
};
/* struct describing queues used to pass data between threads */
struct queue {
int size;
int readp;
int writep;
pthread_mutex_t mutex;
pthread_cond_t empty;
pthread_cond_t full;
void **data;
};
/* default size of fragment buffer in Mbytes */
#define FRAGMENT_BUFFER_DEFAULT 256
/* default size of data buffer in Mbytes */
#define DATA_BUFFER_DEFAULT 256
#define DIR_ENT_SIZE 16
struct dir_ent {
char name[SQUASHFS_NAME_LEN + 1];
unsigned int start_block;
unsigned int offset;
unsigned int type;
};
struct dir {
int dir_count;
int cur_entry;
unsigned int mode;
uid_t uid;
gid_t guid;
unsigned int mtime;
unsigned int xattr;
struct dir_ent *dirs;
};
struct file_entry {
int offset;
int size;
struct cache_entry *buffer;
};
struct squashfs_file {
int fd;
int blocks;
long long file_size;
int mode;
uid_t uid;
gid_t gid;
time_t time;
char *pathname;
char sparse;
unsigned int xattr;
};
struct path_entry {
char *name;
regex_t *preg;
struct pathname *paths;
};
struct pathname {
int names;
struct path_entry *name;
};
struct pathnames {
int count;
struct pathname *path[0];
};
#define PATHS_ALLOC_SIZE 10
/* globals */
extern struct super_block sBlk;
extern int swap;
extern struct hash_table_entry *inode_table_hash[65536],
*directory_table_hash[65536];
extern pthread_mutex_t screen_mutex;
extern int progress_enabled;
extern int inode_number;
extern int lookup_type[];
extern int fd;
extern int no_xattrs;
extern struct queue *to_reader, *to_inflate, *to_writer;
extern struct cache *fragment_cache, *data_cache;
/* unsquashfs.c */
extern void *read_inode_table(long long, long long);
extern void *read_directory_table(long long, long long);
extern long long lookup_entry(struct hash_table_entry **, long long);
extern int read_fs_bytes(int fd, long long, int, void *);
extern int read_block(int, long long, long long *, int, void *);
extern void enable_progress_bar();
extern void disable_progress_bar();
extern void dump_queue(struct queue *);
extern void dump_cache(struct cache *);
/* unsquash-1.c */
extern squashfs_operations *read_filesystem_tables_1();
/* unsquash-2.c */
extern squashfs_operations *read_filesystem_tables_2();
/* unsquash-3.c */
extern squashfs_operations *read_filesystem_tables_3();
/* unsquash-4.c */
extern squashfs_operations *read_filesystem_tables_4();
/* unsquash-123.c */
extern int read_ids(int, long long, long long, unsigned int **);
/* unsquash-34.c */
extern long long *alloc_index_table(int);
#endif

View File

@@ -0,0 +1,145 @@
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* unsquashfs_info.c
*/
#include <pthread.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include "squashfs_fs.h"
#include "unsquashfs.h"
#include "error.h"
static int silent = 0;
char *pathname = NULL;
pthread_t info_thread;
void disable_info()
{
if(pathname)
free(pathname);
pathname = NULL;
}
void update_info(char *name)
{
if(pathname)
free(pathname);
pathname = name;
}
void dump_state()
{
disable_progress_bar();
printf("Queue and cache status dump\n");
printf("===========================\n");
printf("file buffer read queue (main thread -> reader thread)\n");
dump_queue(to_reader);
printf("file buffer decompress queue (reader thread -> inflate"
" thread(s))\n");
dump_queue(to_inflate);
printf("file buffer write queue (main thread -> writer thread)\n");
dump_queue(to_writer);
printf("\nbuffer cache (uncompressed blocks and compressed blocks "
"'in flight')\n");
dump_cache(data_cache);
printf("fragment buffer cache (uncompressed frags and compressed"
" frags 'in flight')\n");
dump_cache(fragment_cache);
enable_progress_bar();
}
void *info_thrd(void *arg)
{
sigset_t sigmask;
struct timespec timespec = { .tv_sec = 1, .tv_nsec = 0 };
int sig, waiting = 0;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGQUIT);
sigaddset(&sigmask, SIGHUP);
while(1) {
if(waiting)
sig = sigtimedwait(&sigmask, NULL, &timespec);
else
sig = sigwaitinfo(&sigmask, NULL);
if(sig == -1) {
switch(errno) {
case EAGAIN:
/* interval timed out */
waiting = 0;
/* FALLTHROUGH */
case EINTR:
/* if waiting, the wait will be longer, but
that's OK */
continue;
default:
BAD_ERROR("sigtimedwait/sigwaitinfo failed "
"because %s\n", strerror(errno));
}
}
if(sig == SIGQUIT && !waiting) {
if(pathname)
INFO("%s\n", pathname);
/* set one second interval period, if ^\ received
within then, dump queue and cache status */
waiting = 1;
} else
dump_state();
}
}
void init_info()
{
pthread_create(&info_thread, NULL, info_thrd, NULL);
}

View File

@@ -0,0 +1,30 @@
#ifndef UNSQUASHFS_INFO_H
#define UNSQUASHFS_INFO_H
/*
* Create a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2013, 2014
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* unsquashfs_info.h
*/
extern void disable_info();
extern void update_info(char *);
extern void init_info();
#endif

View File

@@ -0,0 +1,144 @@
/*
* Unsquash a squashfs filesystem. This is a highly compressed read only
* filesystem.
*
* Copyright (c) 2010, 2012, 2019
* Phillip Lougher <phillip@squashfs.org.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* unsquashfs_xattr.c
*/
#include "unsquashfs.h"
#include "xattr.h"
#include <sys/xattr.h>
#define NOSPACE_MAX 10
extern int root_process;
extern int user_xattrs;
extern int ignore_errors;
extern int strict_errors;
int write_xattr(char *pathname, unsigned int xattr)
{
unsigned int count;
struct xattr_list *xattr_list;
int i;
static int nonsuper_error = FALSE;
static int ignore_xattrs = FALSE;
static int nospace_error = 0;
int failed;
if(ignore_xattrs || xattr == SQUASHFS_INVALID_XATTR ||
sBlk.s.xattr_id_table_start == SQUASHFS_INVALID_BLK)
return TRUE;
xattr_list = get_xattr(xattr, &count, &failed);
if(failed)
EXIT_UNSQUASH_STRICT("write_xattr: Failed to read one or more xattrs for %s\n", pathname);
for(i = 0; i < count; i++) {
int prefix = xattr_list[i].type & SQUASHFS_XATTR_PREFIX_MASK;
if(ignore_xattrs || (user_xattrs && prefix != SQUASHFS_XATTR_USER))
continue;
if(root_process || prefix == SQUASHFS_XATTR_USER) {
int res = lsetxattr(pathname, xattr_list[i].full_name,
xattr_list[i].value, xattr_list[i].vsize, 0);
if(res == -1) {
if(errno == ENOTSUP) {
/*
* If the destination filesystem cannot
* suppport xattrs, print error, and
* disable xattr output as this error is
* unlikely to go away, and printing
* screenfulls of the same error message
* is rather annoying
*/
ERROR("write_xattr: failed to write "
"xattr %s for file %s because "
"extended attributes are not "
"supported by the destination "
"filesystem\n",
xattr_list[i].full_name,
pathname);
ERROR("Ignoring xattrs in "
"filesystem\n");
EXIT_UNSQUASH_STRICT("To avoid this error message, "
"specify -no-xattrs\n");
ignore_xattrs = TRUE;
} else if((errno == ENOSPC || errno == EDQUOT)
&& nospace_error < NOSPACE_MAX) {
/*
* Many filesystems like ext2/3/4 have
* limits on the amount of xattr
* data that can be stored per file
* (typically one block or 4K), so
* we shouldn't disable xattr ouput,
* as the error may be restriced to one
* file only. If we get a lot of these
* then suppress the error messsage
*/
EXIT_UNSQUASH_IGNORE("write_xattr: failed to write "
"xattr %s for file %s because "
"no extended attribute space "
"remaining (per file or "
"filesystem limit)\n",
xattr_list[i].full_name,
pathname);
if(++ nospace_error == NOSPACE_MAX)
ERROR("%d of these errors "
"printed, further error "
"messages of this type "
"are suppressed!\n",
NOSPACE_MAX);
} else
EXIT_UNSQUASH_IGNORE("write_xattr: failed to write "
"xattr %s for file %s because "
"%s\n", xattr_list[i].full_name,
pathname, strerror(errno));
failed = TRUE;
}
} else if(nonsuper_error == FALSE) {
/*
* if extract user xattrs only then
* error message is suppressed, if not
* print error, and then suppress further error
* messages to avoid possible screenfulls of the
* same error message!
*/
ERROR("write_xattr: could not write xattr %s "
"for file %s because you're not "
"superuser!\n",
xattr_list[i].full_name, pathname);
EXIT_UNSQUASH_STRICT("write_xattr: to avoid this error message, either"
" specify -user-xattrs, -no-xattrs, or run as "
"superuser!\n");
ERROR("Further error messages of this type are "
"suppressed!\n");
nonsuper_error = TRUE;
failed = TRUE;
}
}
free_xattr(xattr_list, count);
return !failed;
}

Some files were not shown because too many files have changed in this diff Show More