Project import
diff --git a/squashfs-tools/ACKNOWLEDGEMENTS b/squashfs-tools/ACKNOWLEDGEMENTS
new file mode 100644
index 0000000..2a80700
--- /dev/null
+++ b/squashfs-tools/ACKNOWLEDGEMENTS
@@ -0,0 +1,165 @@
+ 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.
+
+Joe Blow emailed me that I'd forgotten to put anything in the README about
+mounting the squashfs filesystem.
+
+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!
diff --git a/squashfs-tools/Android.mk b/squashfs-tools/Android.mk
new file mode 100644
index 0000000..d81662e
--- /dev/null
+++ b/squashfs-tools/Android.mk
@@ -0,0 +1,5 @@
+# Copyright (C) 2015 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/squashfs-tools/CHANGES b/squashfs-tools/CHANGES
new file mode 100644
index 0000000..30a06df
--- /dev/null
+++ b/squashfs-tools/CHANGES
@@ -0,0 +1,613 @@
+ SQUASHFS CHANGE LOG
+
+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
diff --git a/squashfs-tools/COPYING b/squashfs-tools/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/squashfs-tools/COPYING
@@ -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.
diff --git a/squashfs-tools/INSTALL b/squashfs-tools/INSTALL
new file mode 100644
index 0000000..5212d54
--- /dev/null
+++ b/squashfs-tools/INSTALL
@@ -0,0 +1,30 @@
+ INSTALLING SQUASHFS
+
+The squashfs4.3.tar.gz file contains the squashfs-tools directory containing
+mksquashfs and unsquashfs.
+
+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. 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 support is not yet in any mainline kernel.
+
+2. Building squashfs tools
+--------------------------
+
+The squashfs-tools directory contains the mksquashfs and unsquashfs programs.
+These can be made by typing make (or make install to install in /usr/local/bin).
+
+By default the tools are built with GZIP compression and extended attribute
+support. Read the Makefile in squashfs-tools/ for instructions on building
+LZO, LZ4 and XZ compression support, and for instructions on disabling GZIP
+and extended attribute support if desired.
diff --git a/squashfs-tools/MODULE_LICENSE_GPL b/squashfs-tools/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/squashfs-tools/MODULE_LICENSE_GPL
diff --git a/squashfs-tools/NOTICE b/squashfs-tools/NOTICE
new file mode 120000
index 0000000..d24842f
--- /dev/null
+++ b/squashfs-tools/NOTICE
@@ -0,0 +1 @@
+COPYING
\ No newline at end of file
diff --git a/squashfs-tools/README b/squashfs-tools/README
new file mode 100644
index 0000000..3ba308d
--- /dev/null
+++ b/squashfs-tools/README
@@ -0,0 +1,21 @@
+Git status:
+
+This is the squashfs.sourceforge.net CVS repository imported into this
+new git repository. All new development will be under git.
+
+Squashfs-tools: The new Squashfs 4.3 release (2014/05/05).
+
+kernel: obsolete, the kernel code is now in mainline at www.kernel.org.
+
+All Squashfs kernel development trees are stored on kernel.org,
+under git.kernel.org:/linux/kernel/git/pkl/...
+
+kernel-2.4: prehistoric, not updated since the 3.1 release. If you still need
+Squashfs support in the 2.4 kernel then use the squashfs 3.1 release. This
+is the last release that supported 2.4 kernels.
+
+The kernel and kernel-2.4 directories (imported from CVS) are not really
+relevant anymore, but are here temporarily while I decide where to put
+them (I don't want to delete them and have all the pre-mainlining
+kernel commit history disappear from public repositories).
+
diff --git a/squashfs-tools/RELEASE-README b/squashfs-tools/RELEASE-README
new file mode 100644
index 0000000..a033a4b
--- /dev/null
+++ b/squashfs-tools/RELEASE-README
@@ -0,0 +1,1010 @@
+ 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 version 4.3. Please read the README-4.3 and CHANGES files
+for details of changes.
+
+Squashfs is a highly compressed read-only filesystem for Linux.
+It uses either gzip/xz/lzo/lz4 compression to compress both 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.
+
+1. SQUASHFS OVERVIEW
+--------------------
+
+1. Data, inodes and directories are compressed.
+
+2. Squashfs stores full uid/gids (32 bits), and file creation time.
+
+3. In theory files up to 2^64 bytes are supported. In theory filesystems can
+ be up to 2^64 bytes.
+
+4. 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).
+
+5. Squashfs can use block sizes up to 1Mbyte (the default size is 128K).
+ Using 128K blocks achieves greater compression ratios than the normal
+ 4K block size.
+
+6. File duplicates are detected and removed.
+
+7. Filesystems can be compressed with gzip, xz (lzma2), lzo or lz4
+ compression algorithms.
+
+1.1 Extended attributes (xattrs)
+--------------------------------
+
+Squashfs filesystems 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. Filesystems
+with extended attributes can be mounted on 2.6.29 and later kernels, the
+extended attributes will be ignored with a warning.
+
+2. USING SQUASHFS
+-----------------
+
+Squashfs filesystems should be mounted with 'mount' with the filesystem type
+'squashfs'. If the filesystem is on a block device, the filesystem can be
+mounted directly, e.g.
+
+%mount -t squashfs /dev/sda1 /mnt
+
+Will mount the squashfs filesystem on "/dev/sda1" under the directory "/mnt".
+
+If the squashfs filesystem has been written to a file, the loopback device
+can be used to mount it (loopback support must be in the kernel), e.g.
+
+%mount -t squashfs image /mnt -o loop
+
+Will mount the squashfs filesystem in the file "image" under
+the directory "/mnt".
+
+3. MKSQUASHFS
+-------------
+
+3.1 Mksquashfs options and overview
+-----------------------------------
+
+As squashfs is a read-only filesystem, the mksquashfs program must be used to
+create populated squashfs filesystems.
+
+SYNTAX:./mksquashfs source1 source2 ... dest [options] [-e list of exclude
+dirs/files]
+
+Filesystem build options:
+-comp <comp> select <comp> compression
+ Compressors available:
+ gzip (default)
+ lzo
+ lz4
+ xz
+-b <block_size> set data block to <block_size>. Default 128 Kbytes
+ Optionally a suffix of K or M can be given to specify
+ Kbytes or Mbytes respectively
+-no-exports don't make the filesystem exportable via NFS
+-no-sparse don't detect sparse files
+-no-xattrs don't store extended attributes
+-xattrs store extended attributes (default)
+-noI do not compress inode table
+-noD do not compress data blocks
+-noF do not compress fragment blocks
+-noX do not compress extended attributes
+-no-fragments do not use fragments
+-always-use-fragments use fragment blocks for files larger than block size
+-no-duplicates do not perform duplicate checking
+-all-root make all files owned by root
+-force-uid uid set all file uids to uid
+-force-gid gid set all file gids to gid
+-nopad do not pad filesystem to a multiple of 4K
+-keep-as-directory if one source directory is specified, create a root
+ directory containing that directory, rather than the
+ contents of the directory
+
+Filesystem filter options:
+-p <pseudo-definition> Add pseudo file definition
+-pf <pseudo-file> Add list of pseudo file definitions
+-sort <sort_file> sort files according to priorities in <sort_file>. One
+ file or dir with priority per line. Priority -32768 to
+ 32767, default priority 0
+-ef <exclude_file> list of exclude dirs/files. One per line
+-wildcards Allow extended shell wildcards (globbing) to be used in
+ exclude dirs/files
+-regex Allow POSIX regular expressions to be used in exclude
+ dirs/files
+
+Filesystem append options:
+-noappend do not append to existing filesystem
+-root-becomes <name> when appending source files/directories, make the
+ original root become a subdirectory in the new root
+ called <name>, rather than adding the new source items
+ to the original root
+
+Mksquashfs runtime options:
+-version print version, licence and copyright message
+-exit-on-error treat normally ignored errors as fatal
+-recover <name> recover filesystem data using recovery file <name>
+-no-recovery don't generate a recovery file
+-info print files written to filesystem
+-no-progress don't display the progress bar
+-progress display progress bar when using the -info option
+-processors <number> Use <number> processors. By default will use number of
+ processors available
+-mem <size> Use <size> physical memory. Currently set to 1922M
+ Optionally a suffix of K, M or G can be given to specify
+ Kbytes, Mbytes or Gbytes respectively
+
+Miscellaneous options:
+-root-owned alternative name for -all-root
+-noInodeCompression alternative name for -noI
+-noDataCompression alternative name for -noD
+-noFragmentCompression alternative name for -noF
+-noXattrCompression alternative name for -noX
+
+-Xhelp print compressor options for selected compressor
+
+Compressors available and compressor specific options:
+ gzip (default)
+ -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
+ 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
+ lz4
+ -Xhc
+ Compress using LZ4 High Compression
+ 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.
+
+Source1 source2 ... are the source directories/files containing the
+files/directories that will form the squashfs filesystem. If a single
+directory is specified (i.e. mksquashfs source output_fs) the squashfs
+filesystem will consist of that directory, with the top-level root
+directory corresponding to the source directory.
+
+If multiple source directories or files are specified, mksquashfs will merge
+the specified sources into a single filesystem, with the root directory
+containing each of the source files/directories. The name of each directory
+entry will be the basename of the source path. If more than one source
+entry maps to the same name, the conflicts are named xxx_1, xxx_2, etc. where
+xxx is the original name.
+
+To make this clear, take two example directories. Source directory
+"/home/phillip/test" contains "file1", "file2" and "dir1".
+Source directory "goodies" contains "goodies1", "goodies2" and "goodies3".
+
+usage example 1:
+
+%mksquashfs /home/phillip/test output_fs
+
+This will generate a squashfs filesystem with root entries
+"file1", "file2" and "dir1".
+
+example 2:
+
+%mksquashfs /home/phillip/test goodies output_fs
+
+This will create a squashfs filesystem with the root containing
+entries "test" and "goodies" corresponding to the source
+directories "/home/phillip/test" and "goodies".
+
+example 3:
+
+%mksquashfs /home/phillip/test goodies test output_fs
+
+This is the same as the previous example, except a third
+source directory "test" has been specified. This conflicts
+with the first directory named "test" and will be renamed "test_1".
+
+Multiple sources allow filesystems to be generated without needing to
+copy all source files into a common directory. This simplifies creating
+filesystems.
+
+The -keep-as-directory option can be used when only one source directory
+is specified, and you wish the root to contain that directory, rather than
+the contents of the directory. For example:
+
+example 4:
+
+%mksquashfs /home/phillip/test output_fs -keep-as-directory
+
+This is the same as example 1, except for -keep-as-directory.
+This will generate a root directory containing directory "test",
+rather than the "test" directory contents "file1", "file2" and "dir1".
+
+The Dest argument is the destination where the squashfs filesystem will be
+written. This can either be a conventional file or a block device. If the file
+doesn't exist it will be created, if it does exist and a squashfs
+filesystem exists on it, mksquashfs will append. The -noappend option will
+write a new filesystem irrespective of whether an existing filesystem is
+present.
+
+3.2 Changing compression algorithm and compression specific options
+-------------------------------------------------------------------
+
+By default Mksquashfs will compress using the gzip compression
+algorithm. This algorithm offers a good trade-off between compression
+ratio, and memory and time taken to decompress.
+
+Squashfs also supports LZ4, LZO and XZ (LZMA2) compression. LZO offers worse
+compression ratio than gzip, but is faster to decompress. XZ offers better
+compression ratio than gzip, but at the expense of greater memory and time
+to decompress (and significantly more time to compress). LZ4 is similar
+to LZO, but, support for it is not yet in the mainline kernel, and so
+its usefulness is currently limited to using Squashfs with Mksquashfs/Unsquashfs
+as an archival system like tar.
+
+If you're not building the squashfs-tools and kernel from source, then
+the tools and kernel may or may not have been built with support for LZ4, LZO or
+XZ 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 and compressor specific options:
+ gzip (default)
+ -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
+ 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
+ lz4
+ -Xhc
+ Compress using LZ4 High Compression
+ 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.
+
+If the compressor offers compression specific options (all the compressors now
+have compression specific options except the deprecated lzma1 compressor)
+then these options are also displayed (.i.e. in the above XZ is shown with two
+compression specific options). The compression specific options are, obviously,
+specific to the compressor in question, and the compressor documentation and
+web sites should be consulted to understand their behaviour. In general
+the Mksquashfs compression defaults 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,
+for LZO/LZ4 best means a tradeoff between compression and (de)-compression
+overhead (LZO/LZ4 by definition are intended for weaker processors).
+
+3.3 Changing global compression defaults used in mksquashfs
+-----------------------------------------------------------
+
+There are a large number of options that can be used to control the
+compression in mksquashfs. By and large the defaults are the most
+optimum settings and should only be changed in exceptional circumstances!
+Note, this does not apply to the block size, increasing the block size
+from the default of 128Kbytes will increase compression (especially
+for the xz compressor) and should increase I/O performance too. However,
+a block size of greater than 128Kbytes may increase latency in certain
+cases (where the filesystem contains lots of fragments, and no locality
+of reference is observed). For this reason the block size default is
+configured to the less optimal 128Kbytes. Users should experiment
+with 256Kbyte sizes or above.
+
+The -noI, -noD and -noF options (also -noInodeCompression, -noDataCompression
+and -noFragmentCompression) can be used to force mksquashfs to not compress
+inodes/directories, data and fragments respectively. Giving all options
+generates an uncompressed filesystem.
+
+The -no-fragments tells mksquashfs to not generate fragment blocks, and rather
+generate a filesystem similar to a Squashfs 1.x filesystem. It will of course
+still be a Squashfs 4.0 filesystem but without fragments, and so it won't be
+mountable on a Squashfs 1.x system.
+
+The -always-use-fragments option tells mksquashfs to always generate
+fragments for files irrespective of the file length. 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.
+
+The -no-duplicates option tells mksquashfs to not check the files being
+added to the filesystem for duplicates. This can result in quicker filesystem
+generation and appending although obviously compression will suffer badly if
+there is a lot of duplicate files.
+
+The -b option allows the block size to be selected, both "K" and "M" postfixes
+are supported, this can be either 4K, 8K, 16K, 32K, 64K, 128K, 256K, 512K or
+1M bytes.
+
+3.4 Specifying the UIDs/GIDs used in the filesystem
+---------------------------------------------------
+
+By default files in the generated filesystem inherit the UID and GID ownership
+of the original file. However, mksquashfs provides a number of options which
+can be used to override the ownership.
+
+The options -all-root and -root-owned (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.
+
+The "-force-uid uid" 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.
+
+The "-force-gid gid" 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.
+
+3.5 Excluding files from the filesystem
+---------------------------------------
+
+The -e and -ef options allow files/directories to be specified which are
+excluded from the output filesystem. The -e option takes the exclude
+files/directories from the command line, the -ef option takes the
+exlude files/directories from the specified exclude file, one file/directory
+per line.
+
+Two styles of exclude file matching are supported: basic exclude matching, and
+extended wildcard matching. Basic exclude matching is a legacy feature
+retained for backwards compatibility with earlier versions of Mksquashfs.
+Extended wildcard matching should be used in preference.
+
+3.5.1 Basic exclude matching
+----------------------------
+
+Each exclude file is treated as an exact match of a file/directory in
+the source directories. If an exclude file/directory is absolute (i.e.
+prefixed with /, ../, or ./) the entry is treated as absolute, however, if an
+exclude file/directory is relative, it is treated as being relative to each of
+the sources in turn, i.e.
+
+%mksquashfs /tmp/source1 source2 output_fs -e ex1 /tmp/source1/ex2 out/ex3
+
+Will generate exclude files /tmp/source1/ex2, /tmp/source1/ex1, source2/ex1,
+/tmp/source1/out/ex3 and source2/out/ex3.
+
+3.5.2 Extended exclude file handling
+------------------------------------
+
+Extended exclude file matching treats each exclude file as a wildcard or
+regex expression. To enable wildcard matching specify the -wildcards
+option, and to enable regex matching specify the -regex option. In most
+cases the -wildcards option should be used rather than -regex because wildcard
+matching behaviour is significantly easier to understand!
+
+In addition to wildcards/regex expressions, exclude files can be "anchored" or
+"non-anchored". An anchored exclude is one which matches from the root of the
+directory and nowhere else, a non-anchored exclude matches anywhere. For
+example given the directory hierarchy "a/b/c/a/b", the anchored exclude
+"a/b" will match "a/b" at the root of the directory hierarchy, but
+it will not match the "/a/b" sub-directory within directory "c", whereas a
+non-anchored exclude would.
+
+A couple of examples should make this clearer.
+
+Anchored excludes
+
+ 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.
+
+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.
+
+3.5.3 Exclude files summary
+---------------------------
+
+The -e and -ef exclude options are usefully used in archiving the entire
+filesystem, where it is wished to avoid archiving /proc, and the filesystem
+being generated, i.e.
+
+%mksquashfs / /tmp/root.sqsh -e proc /tmp/root.sqsh
+
+Multiple -ef options can be specified on the command line, and the -ef
+option can be used in conjuction with the -e option.
+
+3.6 Appending to squashfs filesystems
+-------------------------------------
+
+Running squashfs with the destination directory containing an existing
+filesystem will add the source items to the existing filesystem. By default,
+the source items are added to the existing root directory.
+
+To make this clear... An existing filesystem "image" contains root entries
+"old1", and "old2". Source directory "/home/phillip/test" contains "file1",
+"file2" and "dir1".
+
+example 1:
+
+%mksquashfs /home/phillip/test image
+
+Will create a new "image" with root entries "old1", "old2", "file1", "file2" and
+"dir1"
+
+example 2:
+
+%mksquashfs /home/phillip/test image -keep-as-directory
+
+Will create a new "image" with root entries "old1", "old2", and "test".
+As shown in the previous section, for single source directories
+'-keep-as-directory' adds the source directory rather than the
+contents of the directory.
+
+example 3:
+
+%mksquashfs /home/phillip/test image -keep-as-directory -root-becomes
+original-root
+
+Will create a new "image" with root entries "original-root", and "test". The
+'-root-becomes' option specifies that the original root becomes a subdirectory
+in the new root, with the specified name.
+
+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.
+
+3.7 Appending recovery file feature
+-----------------------------------
+
+Recovery files are 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.8 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 added 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.
+
+3.8.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.
+
+3.8.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.8.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.
+
+3.8.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.
+
+3.9 Miscellaneous options
+-------------------------
+
+The -info option displays the files/directories as they are compressed and
+added to the filesystem. The original uncompressed size of each file
+is printed, along with DUPLICATE if the file is a duplicate of a
+file in the filesystem.
+
+The -nopad option informs mksquashfs to not pad the filesystem to a 4K multiple.
+This is performed by default to enable the output filesystem file to be mounted
+by loopback, which requires files to be a 4K multiple. If the filesystem is
+being written to a block device, or is to be stored in a bootimage, the extra
+pad bytes are not needed.
+
+4. UNSQUASHFS
+-------------
+
+Unsquashfs allows you to decompress and extract a Squashfs filesystem without
+mounting it. It can extract the entire filesystem, or a specific
+file or directory.
+
+The Unsquashfs usage info is:
+
+SYNTAX: ./unsquashfs [options] filesystem [directories or files to extract]
+ -v[ersion] print version, licence and copyright information
+ -d[est] <pathname> unsquash to <pathname>, default "squashfs-root"
+ -n[o-progress] don't display the progress bar
+ -no[-xattrs] don't extract xattrs in file system
+ -x[attrs] extract xattrs in file system (default)
+ -u[ser-xattrs] only extract user xattrs in file system.
+ Enables extracting xattrs
+ -p[rocessors] <number> use <number> processors. By default will use
+ number of processors available
+ -i[nfo] print files as they are unsquashed
+ -li[nfo] print files as they are unsquashed with file
+ attributes (like ls -l output)
+ -l[s] list filesystem, but don't unsquash
+ -ll[s] list filesystem with file attributes (like
+ ls -l output), but don't unsquash
+ -f[orce] if file already exists then overwrite
+ -s[tat] display filesystem superblock information
+ -e[f] <extract file> list of directories or files to extract.
+ One per line
+ -da[ta-queue] <size> Set data queue to <size> Mbytes. Default 256
+ Mbytes
+ -fr[ag-queue] <size> Set fragment queue to <size> Mbytes. Default
+ 256 Mbytes
+ -r[egex] treat extract names as POSIX regular expressions
+ rather than use the default shell wildcard
+ expansion (globbing)
+
+Decompressors available:
+ gzip
+ lzo
+ lz4
+ xz
+
+To extract a subset of the filesystem, the filenames or directory
+trees that are to be extracted can be specified on the command line. The
+files/directories should be specified using the full path to the
+files/directories as they appear within the Squashfs filesystem. The
+files/directories will also be extracted to those positions within the specified
+destination directory.
+
+The extract files can also be given in a file using the "-e[f]" option.
+
+Similarly to Mksquashfs, wildcard matching is performed on the extract
+files. Wildcard matching is enabled by default.
+
+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.
+
+
+4.1 Unsquashfs options
+----------------------
+
+The "-ls" option can be used to list the contents of a filesystem without
+decompressing the filesystem data itself. The "-lls" option is similar
+but it also displays file attributes (ls -l style output).
+
+The "-info" option forces Unsquashfs to print each file as it is decompressed.
+The -"linfo" is similar but it also displays file attributes.
+
+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.
+
+The "-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.
+
+The "-stat" option displays filesystem superblock information. This is
+useful to discover the filesystem version, byte ordering, whether it has a NFS
+export table, and what options were used to compress the filesystem, etc.
+
+Unsquashfs can decompress all Squashfs filesystem versions, 1.x, 2.x, 3.x and
+4.0 filesystems.
+
+5. FILESYSTEM LAYOUT
+--------------------
+
+A squashfs filesystem consists of a maximum of nine parts, packed together on a
+byte alignment:
+
+ ---------------
+ | superblock |
+ |---------------|
+ | compression |
+ | options |
+ |---------------|
+ | datablocks |
+ | & fragments |
+ |---------------|
+ | inode table |
+ |---------------|
+ | directory |
+ | table |
+ |---------------|
+ | fragment |
+ | table |
+ |---------------|
+ | export |
+ | table |
+ |---------------|
+ | uid/gid |
+ | lookup table |
+ |---------------|
+ | xattr |
+ | 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 super-block, compression options, inode, directory,
+fragment, export, uid/gid lookup and xattr tables are written.
+
+5.1 Compression options
+-----------------------
+
+Compressors can optionally support compression specific options (e.g.
+dictionary size). If non-default compression options have been used, then
+these are stored here.
+
+5.2 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.
+
+5.3 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.
+
+5.4 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.
+
+5.5 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.
+
+5.6 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.
+
+5.7 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.
+
+5.8 Xattr table
+---------------
+
+The xattr table contains extended attributes for each inode. The xattrs
+for each inode are stored in a list, each list entry containing a type,
+name and value field. The type field encodes the xattr prefix
+("user.", "trusted." etc) and it also encodes how the name/value fields
+should be interpreted. Currently the type indicates whether the value
+is stored inline (in which case the value field contains the xattr value),
+or if it is stored out of line (in which case the value field stores a
+reference to where the actual value is stored). This allows large values
+to be stored out of line improving scanning and lookup performance and it
+also allows values to be de-duplicated, the value being stored once, and
+all other occurences holding an out of line reference to that value.
+
+The xattr lists are packed into compressed 8K metadata blocks.
+To reduce overhead in inodes, rather than storing the on-disk
+location of the xattr list inside each inode, a 32-bit xattr id
+is stored. This xattr id is mapped into the location of the xattr
+list using a second xattr id lookup table.
+
+6. AUTHOR INFO
+--------------
+
+Squashfs was written by Phillip Lougher, email phillip@lougher.demon.co.uk,
+in Chepstow, Wales, UK. If you like the program, or have any problems,
+then please email me, as it's nice to get feedback!
diff --git a/squashfs-tools/RELEASE-READMEs/DONATIONS b/squashfs-tools/RELEASE-READMEs/DONATIONS
new file mode 100644
index 0000000..b4653bb
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/DONATIONS
@@ -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.
+
diff --git a/squashfs-tools/RELEASE-READMEs/PERFORMANCE.README b/squashfs-tools/RELEASE-READMEs/PERFORMANCE.README
new file mode 100644
index 0000000..efb98f2
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/PERFORMANCE.README
@@ -0,0 +1,171 @@
+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)
diff --git a/squashfs-tools/RELEASE-READMEs/README-2.0 b/squashfs-tools/RELEASE-READMEs/README-2.0
new file mode 100644
index 0000000..41931d8
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-2.0
@@ -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.
diff --git a/squashfs-tools/RELEASE-READMEs/README-2.0-AMD64 b/squashfs-tools/RELEASE-READMEs/README-2.0-AMD64
new file mode 100644
index 0000000..5569617
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-2.0-AMD64
@@ -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.
+
diff --git a/squashfs-tools/RELEASE-READMEs/README-2.1 b/squashfs-tools/RELEASE-READMEs/README-2.1
new file mode 100644
index 0000000..e70167e
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-2.1
@@ -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.
diff --git a/squashfs-tools/RELEASE-READMEs/README-3.0 b/squashfs-tools/RELEASE-READMEs/README-3.0
new file mode 100644
index 0000000..fd16dd3
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-3.0
@@ -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.
diff --git a/squashfs-tools/RELEASE-READMEs/README-3.1 b/squashfs-tools/RELEASE-READMEs/README-3.1
new file mode 100755
index 0000000..0e1ee79
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-3.1
@@ -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.
diff --git a/squashfs-tools/RELEASE-READMEs/README-3.2 b/squashfs-tools/RELEASE-READMEs/README-3.2
new file mode 100755
index 0000000..e38286f
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-3.2
@@ -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.
diff --git a/squashfs-tools/RELEASE-READMEs/README-3.3 b/squashfs-tools/RELEASE-READMEs/README-3.3
new file mode 100644
index 0000000..a38a39e
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-3.3
@@ -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.
diff --git a/squashfs-tools/RELEASE-READMEs/README-4.0 b/squashfs-tools/RELEASE-READMEs/README-4.0
new file mode 100644
index 0000000..8cc9514
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-4.0
@@ -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.
diff --git a/squashfs-tools/RELEASE-READMEs/README-4.1 b/squashfs-tools/RELEASE-READMEs/README-4.1
new file mode 100644
index 0000000..d2712f9
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-4.1
@@ -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.
diff --git a/squashfs-tools/RELEASE-READMEs/README-4.2 b/squashfs-tools/RELEASE-READMEs/README-4.2
new file mode 100644
index 0000000..db28f53
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-4.2
@@ -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.
diff --git a/squashfs-tools/RELEASE-READMEs/README-4.3 b/squashfs-tools/RELEASE-READMEs/README-4.3
new file mode 100644
index 0000000..d2370a0
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/README-4.3
@@ -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.
diff --git a/squashfs-tools/RELEASE-READMEs/pseudo-file.example b/squashfs-tools/RELEASE-READMEs/pseudo-file.example
new file mode 100644
index 0000000..f866d90
--- /dev/null
+++ b/squashfs-tools/RELEASE-READMEs/pseudo-file.example
@@ -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
+
diff --git a/squashfs-tools/kernel-2.4/fs/squashfs/Makefile b/squashfs-tools/kernel-2.4/fs/squashfs/Makefile
new file mode 100644
index 0000000..0965287
--- /dev/null
+++ b/squashfs-tools/kernel-2.4/fs/squashfs/Makefile
@@ -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
diff --git a/squashfs-tools/kernel-2.4/fs/squashfs/inode.c b/squashfs-tools/kernel-2.4/fs/squashfs/inode.c
new file mode 100755
index 0000000..54061f6
--- /dev/null
+++ b/squashfs-tools/kernel-2.4/fs/squashfs/inode.c
@@ -0,0 +1,2029 @@
+/*
+ * 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.
+ *
+ * inode.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/blkdev.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+#include "squashfs.h"
+
+static struct super_block *squashfs_read_super(struct super_block *, void *, int);
+static void squashfs_put_super(struct super_block *);
+static int squashfs_statfs(struct super_block *, struct statfs *);
+static int squashfs_symlink_readpage(struct file *file, struct page *page);
+static int squashfs_readpage(struct file *file, struct page *page);
+static int squashfs_readpage4K(struct file *file, struct page *page);
+static int squashfs_readdir(struct file *, void *, filldir_t);
+static struct dentry *squashfs_lookup(struct inode *, struct dentry *);
+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode);
+static long long read_blocklist(struct inode *inode, int index,
+ int readahead_blks, char *block_list,
+ unsigned short **block_p, unsigned int *bsize);
+
+static DECLARE_FSTYPE_DEV(squashfs_fs_type, "squashfs", squashfs_read_super);
+
+static unsigned char squashfs_filetype_table[] = {
+ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
+};
+
+static struct super_operations squashfs_ops = {
+ .statfs = squashfs_statfs,
+ .put_super = squashfs_put_super,
+};
+
+SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = {
+ .readpage = squashfs_symlink_readpage
+};
+
+SQSH_EXTERN struct address_space_operations squashfs_aops = {
+ .readpage = squashfs_readpage
+};
+
+SQSH_EXTERN struct address_space_operations squashfs_aops_4K = {
+ .readpage = squashfs_readpage4K
+};
+
+static struct file_operations squashfs_dir_ops = {
+ .read = generic_read_dir,
+ .readdir = squashfs_readdir
+};
+
+static struct inode_operations squashfs_dir_inode_ops = {
+ .lookup = squashfs_lookup
+};
+
+static struct buffer_head *get_block_length(struct super_block *s,
+ int *cur_index, int *offset, int *c_byte)
+{
+ struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+ unsigned short temp;
+ struct buffer_head *bh;
+
+ if (!(bh = sb_bread(s, *cur_index)))
+ goto out;
+
+ if (msblk->devblksize - *offset == 1) {
+ if (msblk->swap)
+ ((unsigned char *) &temp)[1] = *((unsigned char *)
+ (bh->b_data + *offset));
+ else
+ ((unsigned char *) &temp)[0] = *((unsigned char *)
+ (bh->b_data + *offset));
+ brelse(bh);
+ if (!(bh = sb_bread(s, ++(*cur_index))))
+ goto out;
+ if (msblk->swap)
+ ((unsigned char *) &temp)[0] = *((unsigned char *)
+ bh->b_data);
+ else
+ ((unsigned char *) &temp)[1] = *((unsigned char *)
+ bh->b_data);
+ *c_byte = temp;
+ *offset = 1;
+ } else {
+ if (msblk->swap) {
+ ((unsigned char *) &temp)[1] = *((unsigned char *)
+ (bh->b_data + *offset));
+ ((unsigned char *) &temp)[0] = *((unsigned char *)
+ (bh->b_data + *offset + 1));
+ } else {
+ ((unsigned char *) &temp)[0] = *((unsigned char *)
+ (bh->b_data + *offset));
+ ((unsigned char *) &temp)[1] = *((unsigned char *)
+ (bh->b_data + *offset + 1));
+ }
+ *c_byte = temp;
+ *offset += 2;
+ }
+
+ if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) {
+ if (*offset == msblk->devblksize) {
+ brelse(bh);
+ if (!(bh = sb_bread(s, ++(*cur_index))))
+ goto out;
+ *offset = 0;
+ }
+ if (*((unsigned char *) (bh->b_data + *offset)) !=
+ SQUASHFS_MARKER_BYTE) {
+ ERROR("Metadata block marker corrupt @ %x\n",
+ *cur_index);
+ brelse(bh);
+ goto out;
+ }
+ (*offset)++;
+ }
+ return bh;
+
+out:
+ return NULL;
+}
+
+
+SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
+ long long index, unsigned int length,
+ long long *next_index)
+{
+ struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+ struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >>
+ msblk->devblksize_log2) + 2];
+ unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1);
+ unsigned int cur_index = index >> msblk->devblksize_log2;
+ int bytes, avail_bytes, b = 0, k;
+ char *c_buffer;
+ unsigned int compressed;
+ unsigned int c_byte = length;
+
+ if (c_byte) {
+ bytes = msblk->devblksize - offset;
+ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte);
+ c_buffer = compressed ? msblk->read_data : buffer;
+ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
+
+ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
+ ? "" : "un", (unsigned int) c_byte);
+
+ if (!(bh[0] = sb_getblk(s, cur_index)))
+ goto block_release;
+
+ for (b = 1; bytes < c_byte; b++) {
+ if (!(bh[b] = sb_getblk(s, ++cur_index)))
+ goto block_release;
+ bytes += msblk->devblksize;
+ }
+ ll_rw_block(READ, b, bh);
+ } else {
+ if (!(bh[0] = get_block_length(s, &cur_index, &offset,
+ &c_byte)))
+ goto read_failure;
+
+ bytes = msblk->devblksize - offset;
+ compressed = SQUASHFS_COMPRESSED(c_byte);
+ c_buffer = compressed ? msblk->read_data : buffer;
+ c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
+
+ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
+ ? "" : "un", (unsigned int) c_byte);
+
+ for (b = 1; bytes < c_byte; b++) {
+ if (!(bh[b] = sb_getblk(s, ++cur_index)))
+ goto block_release;
+ bytes += msblk->devblksize;
+ }
+ ll_rw_block(READ, b - 1, bh + 1);
+ }
+
+ if (compressed)
+ down(&msblk->read_data_mutex);
+
+ for (bytes = 0, k = 0; k < b; k++) {
+ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
+ msblk->devblksize - offset :
+ c_byte - bytes;
+ wait_on_buffer(bh[k]);
+ if (!buffer_uptodate(bh[k]))
+ goto block_release;
+ memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes);
+ bytes += avail_bytes;
+ offset = 0;
+ brelse(bh[k]);
+ }
+
+ /*
+ * uncompress block
+ */
+ if (compressed) {
+ int zlib_err;
+
+ msblk->stream.next_in = c_buffer;
+ msblk->stream.avail_in = c_byte;
+ msblk->stream.next_out = buffer;
+ msblk->stream.avail_out = msblk->read_size;
+
+ if (((zlib_err = zlib_inflateInit(&msblk->stream)) != Z_OK) ||
+ ((zlib_err = zlib_inflate(&msblk->stream, Z_FINISH))
+ != Z_STREAM_END) || ((zlib_err =
+ zlib_inflateEnd(&msblk->stream)) != Z_OK)) {
+ ERROR("zlib_fs returned unexpected result 0x%x\n",
+ zlib_err);
+ bytes = 0;
+ } else
+ bytes = msblk->stream.total_out;
+
+ up(&msblk->read_data_mutex);
+ }
+
+ if (next_index)
+ *next_index = index + c_byte + (length ? 0 :
+ (SQUASHFS_CHECK_DATA(msblk->sblk.flags)
+ ? 3 : 2));
+ return bytes;
+
+block_release:
+ while (--b >= 0)
+ brelse(bh[b]);
+
+read_failure:
+ ERROR("sb_bread failed reading block 0x%x\n", cur_index);
+ return 0;
+}
+
+
+SQSH_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)
+{
+ struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+ int n, i, bytes, return_length = length;
+ long long next_index;
+
+ TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset);
+
+ while ( 1 ) {
+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
+ if (msblk->block_cache[i].block == block)
+ break;
+
+ down(&msblk->block_cache_mutex);
+
+ if (i == SQUASHFS_CACHED_BLKS) {
+ /* read inode header block */
+ for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS;
+ n ; n --, i = (i + 1) %
+ SQUASHFS_CACHED_BLKS)
+ if (msblk->block_cache[i].block !=
+ SQUASHFS_USED_BLK)
+ break;
+
+ if (n == 0) {
+ wait_queue_t wait;
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&msblk->waitq, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ up(&msblk->block_cache_mutex);
+ schedule();
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&msblk->waitq, &wait);
+ continue;
+ }
+ msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS;
+
+ if (msblk->block_cache[i].block ==
+ SQUASHFS_INVALID_BLK) {
+ if (!(msblk->block_cache[i].data =
+ kmalloc(SQUASHFS_METADATA_SIZE,
+ GFP_KERNEL))) {
+ ERROR("Failed to allocate cache"
+ "block\n");
+ up(&msblk->block_cache_mutex);
+ goto out;
+ }
+ }
+
+ msblk->block_cache[i].block = SQUASHFS_USED_BLK;
+ up(&msblk->block_cache_mutex);
+
+ if (!(msblk->block_cache[i].length =
+ squashfs_read_data(s,
+ msblk->block_cache[i].data,
+ block, 0, &next_index))) {
+ ERROR("Unable to read cache block [%llx:%x]\n",
+ block, offset);
+ goto out;
+ }
+
+ down(&msblk->block_cache_mutex);
+ wake_up(&msblk->waitq);
+ msblk->block_cache[i].block = block;
+ msblk->block_cache[i].next_index = next_index;
+ TRACE("Read cache block [%llx:%x]\n", block, offset);
+ }
+
+ if (msblk->block_cache[i].block != block) {
+ up(&msblk->block_cache_mutex);
+ continue;
+ }
+
+ if ((bytes = msblk->block_cache[i].length - offset) >= length) {
+ if (buffer)
+ memcpy(buffer, msblk->block_cache[i].data +
+ offset, length);
+ if (msblk->block_cache[i].length - offset == length) {
+ *next_block = msblk->block_cache[i].next_index;
+ *next_offset = 0;
+ } else {
+ *next_block = block;
+ *next_offset = offset + length;
+ }
+ up(&msblk->block_cache_mutex);
+ goto finish;
+ } else {
+ if (buffer) {
+ memcpy(buffer, msblk->block_cache[i].data +
+ offset, bytes);
+ buffer += bytes;
+ }
+ block = msblk->block_cache[i].next_index;
+ up(&msblk->block_cache_mutex);
+ length -= bytes;
+ offset = 0;
+ }
+ }
+
+finish:
+ return return_length;
+out:
+ return 0;
+}
+
+
+static int get_fragment_location(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[SQUASHFS_FRAGMENT_INDEX(fragment)];
+ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
+ struct squashfs_fragment_entry fragment_entry;
+
+ if (msblk->swap) {
+ struct squashfs_fragment_entry 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(&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;
+}
+
+
+SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct
+ squashfs_fragment_cache *fragment)
+{
+ down(&msblk->fragment_mutex);
+ fragment->locked --;
+ wake_up(&msblk->fragment_wait_queue);
+ up(&msblk->fragment_mutex);
+}
+
+
+SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block
+ *s, long long start_block,
+ int length)
+{
+ int i, n;
+ struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+
+ while ( 1 ) {
+ down(&msblk->fragment_mutex);
+
+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS &&
+ msblk->fragment[i].block != start_block; i++);
+
+ if (i == SQUASHFS_CACHED_FRAGMENTS) {
+ for (i = msblk->next_fragment, n =
+ SQUASHFS_CACHED_FRAGMENTS; n &&
+ msblk->fragment[i].locked; n--, i = (i + 1) %
+ SQUASHFS_CACHED_FRAGMENTS);
+
+ if (n == 0) {
+ wait_queue_t wait;
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&msblk->fragment_wait_queue,
+ &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ up(&msblk->fragment_mutex);
+ schedule();
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&msblk->fragment_wait_queue,
+ &wait);
+ continue;
+ }
+ msblk->next_fragment = (msblk->next_fragment + 1) %
+ SQUASHFS_CACHED_FRAGMENTS;
+
+ if (msblk->fragment[i].data == NULL)
+ if (!(msblk->fragment[i].data = SQUASHFS_ALLOC
+ (SQUASHFS_FILE_MAX_SIZE))) {
+ ERROR("Failed to allocate fragment "
+ "cache block\n");
+ up(&msblk->fragment_mutex);
+ goto out;
+ }
+
+ msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
+ msblk->fragment[i].locked = 1;
+ up(&msblk->fragment_mutex);
+
+ if (!(msblk->fragment[i].length = squashfs_read_data(s,
+ msblk->fragment[i].data,
+ start_block, length, NULL))) {
+ ERROR("Unable to read fragment cache block "
+ "[%llx]\n", start_block);
+ msblk->fragment[i].locked = 0;
+ goto out;
+ }
+
+ msblk->fragment[i].block = start_block;
+ TRACE("New fragment %d, start block %lld, locked %d\n",
+ i, msblk->fragment[i].block,
+ msblk->fragment[i].locked);
+ break;
+ }
+
+ msblk->fragment[i].locked++;
+ up(&msblk->fragment_mutex);
+ TRACE("Got fragment %d, start block %lld, locked %d\n", i,
+ msblk->fragment[i].block,
+ msblk->fragment[i].locked);
+ break;
+ }
+
+ return &msblk->fragment[i];
+
+out:
+ return NULL;
+}
+
+
+static struct inode *squashfs_new_inode(struct super_block *s,
+ struct squashfs_base_inode_header *inodeb)
+{
+ struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+ struct inode *i = new_inode(s);
+
+ if (i) {
+ i->i_ino = inodeb->inode_number;
+ i->i_mtime = inodeb->mtime;
+ i->i_atime = inodeb->mtime;
+ i->i_ctime = inodeb->mtime;
+ i->i_uid = msblk->uid[inodeb->uid];
+ i->i_mode = inodeb->mode;
+ 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(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;
+ long long block = SQUASHFS_INODE_BLK(inode) +
+ sblk->inode_table_start;
+ unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
+ long long next_block;
+ unsigned int next_offset;
+ union squashfs_inode_header id, sid;
+ struct squashfs_base_inode_header *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(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: {
+ unsigned int frag_size;
+ long long frag_blk;
+ struct squashfs_reg_inode_header *inodep = &id.reg;
+ struct squashfs_reg_inode_header *sinodep = &sid.reg;
+
+ 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(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(s,
+ inodep->fragment, &frag_blk, &frag_size))
+ goto failed_read;
+
+ if((i = squashfs_new_inode(s, inodeb)) == NULL)
+ goto failed_read1;
+
+ i->i_nlink = 1;
+ i->i_size = inodep->file_size;
+ i->i_fop = &generic_ro_fops;
+ i->i_mode |= S_IFREG;
+ 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 %llx, "
+ "block_list_start %llx, offset %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ inodep->start_block, next_block,
+ next_offset);
+ break;
+ }
+ case SQUASHFS_LREG_TYPE: {
+ unsigned int frag_size;
+ long long frag_blk;
+ struct squashfs_lreg_inode_header *inodep = &id.lreg;
+ struct squashfs_lreg_inode_header *sinodep = &sid.lreg;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, (char *)
+ sinodep, block, offset,
+ sizeof(*sinodep), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_LREG_INODE_HEADER(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(s,
+ inodep->fragment, &frag_blk, &frag_size))
+ goto failed_read;
+
+ if((i = squashfs_new_inode(s, inodeb)) == NULL)
+ goto failed_read1;
+
+ i->i_nlink = inodep->nlink;
+ i->i_size = inodep->file_size;
+ i->i_fop = &generic_ro_fops;
+ i->i_mode |= S_IFREG;
+ 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 %llx, "
+ "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 *inodep = &id.dir;
+ struct squashfs_dir_inode_header *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(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)) == NULL)
+ goto failed_read1;
+
+ i->i_nlink = inodep->nlink;
+ i->i_size = inodep->file_size;
+ i->i_op = &squashfs_dir_inode_ops;
+ i->i_fop = &squashfs_dir_ops;
+ i->i_mode |= S_IFDIR;
+ 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 = inodep->parent_inode;
+
+ 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 *inodep = &id.ldir;
+ struct squashfs_ldir_inode_header *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(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)) == NULL)
+ goto failed_read1;
+
+ i->i_nlink = inodep->nlink;
+ i->i_size = inodep->file_size;
+ i->i_op = &squashfs_dir_inode_ops;
+ i->i_fop = &squashfs_dir_ops;
+ i->i_mode |= S_IFDIR;
+ 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 = inodep->parent_inode;
+
+ 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 *inodep =
+ &id.symlink;
+ struct squashfs_symlink_inode_header *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(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)) == NULL)
+ goto failed_read1;
+
+ i->i_nlink = inodep->nlink;
+ 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 *inodep = &id.dev;
+ struct squashfs_dev_inode_header *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(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)) == NULL)
+ goto failed_read1;
+
+ i->i_nlink = inodep->nlink;
+ 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: {
+ struct squashfs_ipc_inode_header *inodep = &id.ipc;
+ struct squashfs_ipc_inode_header *sinodep = &sid.ipc;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, (char *)
+ sinodep, block, offset,
+ sizeof(*sinodep), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_IPC_INODE_HEADER(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)) == NULL)
+ goto failed_read1;
+
+ i->i_nlink = inodep->nlink;
+ 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 [%llx:%x]\n", block, offset);
+
+failed_read1:
+ return NULL;
+}
+
+
+int read_fragment_index_table(struct super_block *s)
+{
+ struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+
+ if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES
+ (sblk->fragments), GFP_KERNEL))) {
+ ERROR("Failed to allocate uid/gid table\n");
+ return 0;
+ }
+
+ if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) &&
+ !squashfs_read_data(s, (char *)
+ msblk->fragment_index,
+ sblk->fragment_table_start,
+ SQUASHFS_FRAGMENT_INDEX_BYTES
+ (sblk->fragments) |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+ ERROR("unable to read fragment index table\n");
+ return 0;
+ }
+
+ if (msblk->swap) {
+ int i;
+ long long fragment;
+
+ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments);
+ i++) {
+ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment),
+ &msblk->fragment_index[i], 1);
+ msblk->fragment_index[i] = fragment;
+ }
+ }
+
+ return 1;
+}
+
+
+static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent)
+{
+ struct squashfs_super_block *sblk = &msblk->sblk;
+
+ msblk->iget = squashfs_iget;
+ msblk->read_blocklist = read_blocklist;
+ msblk->read_fragment_index_table = read_fragment_index_table;
+
+ if (sblk->s_major == 1) {
+ if (!squashfs_1_0_supported(msblk)) {
+ SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems "
+ "are unsupported\n");
+ SERROR("Please recompile with "
+ "Squashfs 1.0 support enabled\n");
+ return 0;
+ }
+ } else if (sblk->s_major == 2) {
+ if (!squashfs_2_0_supported(msblk)) {
+ SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems "
+ "are unsupported\n");
+ SERROR("Please recompile with "
+ "Squashfs 2.0 support enabled\n");
+ return 0;
+ }
+ } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor >
+ SQUASHFS_MINOR) {
+ SERROR("Major/Minor mismatch, trying to mount newer %d.%d "
+ "filesystem\n", sblk->s_major, sblk->s_minor);
+ SERROR("Please update your kernel\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static struct super_block *squashfs_read_super(struct super_block *s,
+ void *data, int silent)
+{
+ kdev_t dev = s->s_dev;
+ struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ int i;
+ struct inode *root;
+
+ if (!(msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
+ ERROR("Failed to allocate zlib workspace\n");
+ goto failed_mount;
+ }
+
+ msblk->devblksize = get_hardsect_size(dev);
+ if(msblk->devblksize < BLOCK_SIZE)
+ msblk->devblksize = BLOCK_SIZE;
+ msblk->devblksize_log2 = ffz(~msblk->devblksize);
+ set_blocksize(dev, msblk->devblksize);
+ s->s_blocksize = msblk->devblksize;
+ s->s_blocksize_bits = msblk->devblksize_log2;
+
+ init_MUTEX(&msblk->read_data_mutex);
+ init_MUTEX(&msblk->read_page_mutex);
+ init_MUTEX(&msblk->block_cache_mutex);
+ init_MUTEX(&msblk->fragment_mutex);
+
+ init_waitqueue_head(&msblk->waitq);
+ init_waitqueue_head(&msblk->fragment_wait_queue);
+
+ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
+ sizeof(struct squashfs_super_block) |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+ SERROR("unable to read superblock\n");
+ goto failed_mount;
+ }
+
+ /* Check it is a SQUASHFS superblock */
+ msblk->swap = 0;
+ if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) {
+ if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) {
+ struct squashfs_super_block ssblk;
+
+ WARNING("Mounting a different endian SQUASHFS "
+ "filesystem on %s\n", bdevname(dev));
+
+ SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk);
+ memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block));
+ msblk->swap = 1;
+ } else {
+ SERROR("Can't find a SQUASHFS superblock on %s\n",
+ bdevname(dev));
+ goto failed_mount;
+ }
+ }
+
+ /* Check the MAJOR & MINOR versions */
+ if(!supported_squashfs_filesystem(msblk, silent))
+ goto failed_mount;
+
+ TRACE("Found valid superblock on %s\n", bdevname(dev));
+ TRACE("Inodes are %scompressed\n",
+ SQUASHFS_UNCOMPRESSED_INODES
+ (sblk->flags) ? "un" : "");
+ TRACE("Data is %scompressed\n",
+ SQUASHFS_UNCOMPRESSED_DATA(sblk->flags)
+ ? "un" : "");
+ TRACE("Check data is %s present in the filesystem\n",
+ SQUASHFS_CHECK_DATA(sblk->flags) ?
+ "" : "not");
+ TRACE("Filesystem size %lld bytes\n", sblk->bytes_used);
+ TRACE("Block size %d\n", sblk->block_size);
+ TRACE("Number of inodes %d\n", sblk->inodes);
+ if (sblk->s_major > 1)
+ TRACE("Number of fragments %d\n", sblk->fragments);
+ TRACE("Number of uids %d\n", sblk->no_uids);
+ TRACE("Number of gids %d\n", sblk->no_guids);
+ TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start);
+ TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start);
+ if (sblk->s_major > 1)
+ TRACE("sblk->fragment_table_start %llx\n",
+ sblk->fragment_table_start);
+ TRACE("sblk->uid_start %llx\n", sblk->uid_start);
+
+ s->s_flags |= MS_RDONLY;
+ s->s_op = &squashfs_ops;
+
+ /* Init inode_table block pointer array */
+ if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) *
+ SQUASHFS_CACHED_BLKS, GFP_KERNEL))) {
+ ERROR("Failed to allocate block cache\n");
+ goto failed_mount;
+ }
+
+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
+ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
+
+ msblk->next_cache = 0;
+
+ /* Allocate read_data block */
+ msblk->read_size = (sblk->block_size < SQUASHFS_METADATA_SIZE) ?
+ SQUASHFS_METADATA_SIZE :
+ sblk->block_size;
+
+ if (!(msblk->read_data = kmalloc(msblk->read_size, GFP_KERNEL))) {
+ ERROR("Failed to allocate read_data block\n");
+ goto failed_mount;
+ }
+
+ /* Allocate read_page block */
+ if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) {
+ ERROR("Failed to allocate read_page block\n");
+ goto failed_mount;
+ }
+
+ /* Allocate uid and gid tables */
+ if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) *
+ sizeof(unsigned int), GFP_KERNEL))) {
+ ERROR("Failed to allocate uid/gid table\n");
+ goto failed_mount;
+ }
+ msblk->guid = msblk->uid + sblk->no_uids;
+
+ if (msblk->swap) {
+ unsigned int suid[sblk->no_uids + sblk->no_guids];
+
+ if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
+ ((sblk->no_uids + sblk->no_guids) *
+ sizeof(unsigned int)) |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+ ERROR("unable to read uid/gid table\n");
+ goto failed_mount;
+ }
+
+ SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids +
+ sblk->no_guids), (sizeof(unsigned int) * 8));
+ } else
+ if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start,
+ ((sblk->no_uids + sblk->no_guids) *
+ sizeof(unsigned int)) |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+ ERROR("unable to read uid/gid table\n");
+ goto failed_mount;
+ }
+
+
+ if (sblk->s_major == 1 && squashfs_1_0_supported(msblk))
+ goto allocate_root;
+
+ if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) *
+ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) {
+ ERROR("Failed to allocate fragment block cache\n");
+ goto failed_mount;
+ }
+
+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) {
+ msblk->fragment[i].locked = 0;
+ msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
+ msblk->fragment[i].data = NULL;
+ }
+
+ msblk->next_fragment = 0;
+
+ /* Allocate fragment index table */
+ if(msblk->read_fragment_index_table(s) == 0)
+ goto failed_mount;
+
+allocate_root:
+ if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL)
+ goto failed_mount;
+
+ if ((s->s_root = d_alloc_root(root)) == NULL) {
+ ERROR("Root inode create failed\n");
+ iput(root);
+ goto failed_mount;
+ }
+
+ TRACE("Leaving squashfs_read_super\n");
+ return s;
+
+failed_mount:
+ kfree(msblk->fragment_index);
+ kfree(msblk->fragment);
+ kfree(msblk->uid);
+ kfree(msblk->read_page);
+ kfree(msblk->read_data);
+ kfree(msblk->block_cache);
+ kfree(msblk->fragment_index_2);
+ vfree(msblk->stream.workspace);
+ return NULL;
+}
+
+
+static int squashfs_statfs(struct super_block *s, struct statfs *buf)
+{
+ struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+
+ TRACE("Entered squashfs_statfs\n");
+
+ buf->f_type = SQUASHFS_MAGIC;
+ buf->f_bsize = sblk->block_size;
+ buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1;
+ buf->f_bfree = buf->f_bavail = 0;
+ buf->f_files = sblk->inodes;
+ buf->f_ffree = 0;
+ buf->f_namelen = SQUASHFS_NAME_LEN;
+
+ return 0;
+}
+
+
+static int squashfs_symlink_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ int index = page->index << PAGE_CACHE_SHIFT, length, bytes;
+ long long block = SQUASHFS_I(inode)->start_block;
+ int offset = SQUASHFS_I(inode)->offset;
+ void *pageaddr = kmap(page);
+
+ TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
+ "%llx, offset %x\n", page->index,
+ SQUASHFS_I(inode)->start_block,
+ SQUASHFS_I(inode)->offset);
+
+ for (length = 0; length < index; length += bytes) {
+ if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL,
+ block, offset, PAGE_CACHE_SIZE, &block,
+ &offset))) {
+ ERROR("Unable to read symbolic link [%llx:%x]\n", block,
+ offset);
+ goto skip_read;
+ }
+ }
+
+ if (length != index) {
+ ERROR("(squashfs_symlink_readpage) length != index\n");
+ bytes = 0;
+ goto skip_read;
+ }
+
+ bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE :
+ i_size_read(inode) - length;
+
+ if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block,
+ offset, bytes, &block, &offset)))
+ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset);
+
+skip_read:
+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+ kunmap(page);
+ SetPageUptodate(page);
+ UnlockPage(page);
+
+ return 0;
+}
+
+
+struct meta_index *locate_meta_index(struct inode *inode, int index, int offset)
+{
+ struct meta_index *meta = NULL;
+ struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb;
+ int i;
+
+ down(&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_NUMBER; 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:
+ up(&msblk->meta_index_mutex);
+
+ return meta;
+}
+
+
+struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip)
+{
+ struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb;
+ struct meta_index *meta = NULL;
+ int i;
+
+ down(&msblk->meta_index_mutex);
+
+ TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
+
+ if(msblk->meta_index == NULL) {
+ if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) *
+ SQUASHFS_META_NUMBER, GFP_KERNEL))) {
+ ERROR("Failed to allocate meta_index\n");
+ goto failed;
+ }
+ for(i = 0; i < SQUASHFS_META_NUMBER; i++) {
+ msblk->meta_index[i].inode_number = 0;
+ msblk->meta_index[i].locked = 0;
+ }
+ msblk->next_meta_index = 0;
+ }
+
+ for(i = SQUASHFS_META_NUMBER; i &&
+ msblk->meta_index[msblk->next_meta_index].locked; i --)
+ msblk->next_meta_index = (msblk->next_meta_index + 1) %
+ SQUASHFS_META_NUMBER;
+
+ 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_NUMBER;
+
+ meta->inode_number = inode->i_ino;
+ meta->offset = offset;
+ meta->skip = skip;
+ meta->entries = 0;
+ meta->locked = 1;
+
+failed:
+ up(&msblk->meta_index_mutex);
+ return meta;
+}
+
+
+void release_meta_index(struct inode *inode, struct meta_index *meta)
+{
+ meta->locked = 0;
+}
+
+
+static int read_block_index(struct super_block *s, int blocks, char *block_list,
+ long long *start_block, int *offset)
+{
+ struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+ unsigned int *block_listp;
+ int block = 0;
+
+ if (msblk->swap) {
+ char sblock_list[blocks << 2];
+
+ if (!squashfs_get_cached_block(s, sblock_list, *start_block,
+ *offset, blocks << 2, start_block, offset)) {
+ ERROR("Unable to read block list [%llx:%x]\n",
+ *start_block, *offset);
+ goto failure;
+ }
+ SQUASHFS_SWAP_INTS(((unsigned int *)block_list),
+ ((unsigned int *)sblock_list), blocks);
+ } else
+ if (!squashfs_get_cached_block(s, block_list, *start_block,
+ *offset, blocks << 2, start_block, offset)) {
+ ERROR("Unable to read block list [%llx:%x]\n",
+ *start_block, *offset);
+ goto failure;
+ }
+
+ for (block_listp = (unsigned int *) block_list; blocks;
+ block_listp++, blocks --)
+ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp);
+
+ return block;
+
+failure:
+ return -1;
+}
+
+
+#define SIZE 256
+
+static inline int calculate_skip(int blocks) {
+ int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES);
+ return skip >= 7 ? 7 : skip + 1;
+}
+
+
+static int get_meta_index(struct inode *inode, int index,
+ long long *index_block, int *index_offset,
+ long long *data_block, char *block_list)
+{
+ struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ int skip = calculate_skip(i_size_read(inode) >> sblk->block_log);
+ int offset = 0;
+ struct meta_index *meta;
+ struct meta_entry *meta_entry;
+ long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start;
+ int cur_offset = SQUASHFS_I(inode)->offset;
+ long long cur_data_block = SQUASHFS_I(inode)->start_block;
+ int i;
+
+ index /= SQUASHFS_META_INDEXES * skip;
+
+ while ( offset < index ) {
+ meta = locate_meta_index(inode, index, offset + 1);
+
+ if (meta == NULL) {
+ if ((meta = empty_meta_index(inode, offset + 1,
+ skip)) == 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 + sblk->inode_table_start;
+ 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);
+ }
+
+ for (i = meta->offset + meta->entries; i <= index &&
+ i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
+ int blocks = skip * SQUASHFS_META_INDEXES;
+
+ while (blocks) {
+ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) :
+ blocks;
+ int res = read_block_index(inode->i_sb, block,
+ block_list, &cur_index_block,
+ &cur_offset);
+
+ if (res == -1)
+ goto failed;
+
+ cur_data_block += res;
+ blocks -= block;
+ }
+
+ meta_entry = &meta->meta_entry[i - meta->offset];
+ meta_entry->index_block = cur_index_block - sblk->inode_table_start;
+ 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;
+
+ return offset * SQUASHFS_META_INDEXES * skip;
+
+failed:
+ release_meta_index(inode, meta);
+ return -1;
+}
+
+
+static long long read_blocklist(struct inode *inode, int index,
+ int readahead_blks, char *block_list,
+ unsigned short **block_p, unsigned int *bsize)
+{
+ long long block_ptr;
+ int offset;
+ long long block;
+ int res = get_meta_index(inode, index, &block_ptr, &offset, &block,
+ block_list);
+
+ TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset"
+ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset,
+ block);
+
+ if(res == -1)
+ goto failure;
+
+ index -= res;
+
+ while ( index ) {
+ int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index;
+ int res = read_block_index(inode->i_sb, blocks, block_list,
+ &block_ptr, &offset);
+ if (res == -1)
+ goto failure;
+ block += res;
+ index -= blocks;
+ }
+
+ if (read_block_index(inode->i_sb, 1, block_list,
+ &block_ptr, &offset) == -1)
+ goto failure;
+ *bsize = *((unsigned int *) block_list);
+
+ return block;
+
+failure:
+ return 0;
+}
+
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ unsigned char block_list[SIZE];
+ long long block;
+ unsigned int bsize, i = 0, bytes = 0, byte_offset = 0;
+ int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT);
+ void *pageaddr;
+ struct squashfs_fragment_cache *fragment = NULL;
+ char *data_ptr = msblk->read_page;
+
+ int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1;
+ int start_index = page->index & ~mask;
+ int end_index = start_index | mask;
+
+ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
+ page->index,
+ SQUASHFS_I(inode)->start_block);
+
+ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT))
+ goto skip_read;
+
+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+ || index < (i_size_read(inode) >>
+ sblk->block_log)) {
+ if ((block = (msblk->read_blocklist)(inode, index, 1,
+ block_list, NULL, &bsize)) == 0)
+ goto skip_read;
+
+ down(&msblk->read_page_mutex);
+
+ if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page,
+ block, bsize, NULL))) {
+ ERROR("Unable to read page, block %llx, size %x\n", block,
+ bsize);
+ up(&msblk->read_page_mutex);
+ goto skip_read;
+ }
+ } else {
+ if ((fragment = get_cached_fragment(inode->i_sb,
+ SQUASHFS_I(inode)->
+ u.s1.fragment_start_block,
+ SQUASHFS_I(inode)->u.s1.fragment_size))
+ == NULL) {
+ ERROR("Unable to read page, block %llx, size %x\n",
+ SQUASHFS_I(inode)->
+ u.s1.fragment_start_block,
+ (int) SQUASHFS_I(inode)->
+ u.s1.fragment_size);
+ goto skip_read;
+ }
+ bytes = SQUASHFS_I(inode)->u.s1.fragment_offset +
+ (i_size_read(inode) & (sblk->block_size
+ - 1));
+ byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset;
+ data_ptr = fragment->data;
+ }
+
+ for (i = start_index; i <= end_index && byte_offset < bytes;
+ i++, byte_offset += PAGE_CACHE_SIZE) {
+ struct page *push_page;
+ int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ?
+ PAGE_CACHE_SIZE : bytes - byte_offset;
+
+ TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n",
+ bytes, i, byte_offset, available_bytes);
+
+ if (i == page->index) {
+ pageaddr = kmap_atomic(page, KM_USER0);
+ memcpy(pageaddr, data_ptr + byte_offset,
+ available_bytes);
+ memset(pageaddr + available_bytes, 0,
+ PAGE_CACHE_SIZE - available_bytes);
+ kunmap_atomic(pageaddr, KM_USER0);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ UnlockPage(page);
+ } else if ((push_page =
+ grab_cache_page_nowait(page->mapping, i))) {
+ pageaddr = kmap_atomic(push_page, KM_USER0);
+
+ memcpy(pageaddr, data_ptr + byte_offset,
+ available_bytes);
+ memset(pageaddr + available_bytes, 0,
+ PAGE_CACHE_SIZE - available_bytes);
+ kunmap_atomic(pageaddr, KM_USER0);
+ flush_dcache_page(push_page);
+ SetPageUptodate(push_page);
+ UnlockPage(push_page);
+ page_cache_release(push_page);
+ }
+ }
+
+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+ || index < (i_size_read(inode) >>
+ sblk->block_log))
+ up(&msblk->read_page_mutex);
+ else
+ release_cached_fragment(msblk, fragment);
+
+ return 0;
+
+skip_read:
+ pageaddr = kmap_atomic(page, KM_USER0);
+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+ kunmap_atomic(pageaddr, KM_USER0);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ UnlockPage(page);
+
+ return 0;
+}
+
+
+static int squashfs_readpage4K(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ unsigned char block_list[SIZE];
+ long long block;
+ unsigned int bsize, bytes = 0;
+ void *pageaddr;
+
+ TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n",
+ page->index,
+ SQUASHFS_I(inode)->start_block);
+
+ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT)) {
+ pageaddr = kmap_atomic(page, KM_USER0);
+ goto skip_read;
+ }
+
+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+ || page->index < (i_size_read(inode) >>
+ sblk->block_log)) {
+ block = (msblk->read_blocklist)(inode, page->index, 1,
+ block_list, NULL, &bsize);
+
+ down(&msblk->read_page_mutex);
+ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
+ bsize, NULL);
+ pageaddr = kmap_atomic(page, KM_USER0);
+ if (bytes)
+ memcpy(pageaddr, msblk->read_page, bytes);
+ else
+ ERROR("Unable to read page, block %llx, size %x\n",
+ block, bsize);
+ up(&msblk->read_page_mutex);
+ } else {
+ struct squashfs_fragment_cache *fragment =
+ get_cached_fragment(inode->i_sb,
+ SQUASHFS_I(inode)->
+ u.s1.fragment_start_block,
+ SQUASHFS_I(inode)-> u.s1.fragment_size);
+ pageaddr = kmap_atomic(page, KM_USER0);
+ if (fragment) {
+ bytes = i_size_read(inode) & (sblk->block_size - 1);
+ memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->
+ u.s1.fragment_offset, bytes);
+ release_cached_fragment(msblk, fragment);
+ } else
+ ERROR("Unable to read page, block %llx, size %x\n",
+ SQUASHFS_I(inode)->
+ u.s1.fragment_start_block, (int)
+ SQUASHFS_I(inode)-> u.s1.fragment_size);
+ }
+
+skip_read:
+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+ kunmap_atomic(pageaddr, KM_USER0);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ UnlockPage(page);
+
+ return 0;
+}
+
+
+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 index;
+
+ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
+ i_count, (unsigned int) f_pos);
+
+ f_pos -= 3;
+ if (f_pos == 0)
+ goto finish;
+
+ for (i = 0; i < i_count; i++) {
+ if (msblk->swap) {
+ struct squashfs_dir_index sindex;
+ squashfs_get_cached_block(s, (char *) &sindex,
+ index_start, index_offset,
+ sizeof(sindex), &index_start,
+ &index_offset);
+ SQUASHFS_SWAP_DIR_INDEX(&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 + 3;
+}
+
+
+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) + SQUASHFS_NAME_LEN + 1];
+ struct squashfs_dir_index *index = (struct squashfs_dir_index *) 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 sindex;
+ squashfs_get_cached_block(s, (char *) &sindex,
+ index_start, index_offset,
+ sizeof(sindex), &index_start,
+ &index_offset);
+ SQUASHFS_SWAP_DIR_INDEX(index, &sindex);
+ } else
+ squashfs_get_cached_block(s, (char *) index,
+ index_start, index_offset,
+ sizeof(struct squashfs_dir_index),
+ &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 + 3;
+}
+
+
+static int squashfs_readdir(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 dirh;
+ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1];
+ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
+
+ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset);
+
+ while(file->f_pos < 3) {
+ char *name;
+ int size, i_ino;
+
+ if(file->f_pos == 0) {
+ name = ".";
+ size = 1;
+ i_ino = i->i_ino;
+ } else {
+ name = "..";
+ size = 2;
+ i_ino = SQUASHFS_I(i)->u.s2.parent_inode;
+ }
+ TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n",
+ (unsigned int) dirent, name, size, (int)
+ 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(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 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(&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 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(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, %d)\n",
+ (unsigned int) dirent, dire->name,
+ dire->size + 1, (int) file->f_pos,
+ dirh.start_block, dire->offset,
+ dirh.inode_number + dire->inode_number,
+ squashfs_filetype_table[dire->type]);
+
+ if (filldir(dirent, dire->name, dire->size + 1,
+ file->f_pos,
+ dirh.inode_number + dire->inode_number,
+ 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(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 dirh;
+ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN];
+ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
+
+ 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 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(&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 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(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 (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,
+ dirh.inode_number + dire->inode_number);
+
+ 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;
+}
+
+
+static void squashfs_put_super(struct super_block *s)
+{
+ int i;
+
+ struct squashfs_sb_info *sbi = &s->u.squashfs_sb;
+ if (sbi->block_cache)
+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
+ if (sbi->block_cache[i].block !=
+ SQUASHFS_INVALID_BLK)
+ kfree(sbi->block_cache[i].data);
+ if (sbi->fragment)
+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++)
+ SQUASHFS_FREE(sbi->fragment[i].data);
+ kfree(sbi->fragment);
+ kfree(sbi->block_cache);
+ kfree(sbi->read_data);
+ kfree(sbi->read_page);
+ kfree(sbi->uid);
+ kfree(sbi->fragment_index);
+ kfree(sbi->fragment_index_2);
+ kfree(sbi->meta_index);
+ vfree(sbi->stream.workspace);
+ sbi->block_cache = NULL;
+ sbi->uid = NULL;
+ sbi->read_data = NULL;
+ sbi->read_page = NULL;
+ sbi->fragment = NULL;
+ sbi->fragment_index = NULL;
+ sbi->fragment_index_2 = NULL;
+ sbi->meta_index = NULL;
+ sbi->stream.workspace = NULL;
+}
+
+
+static int __init init_squashfs_fs(void)
+{
+
+ printk(KERN_INFO "squashfs: version 3.1 (2006/08/15) "
+ "Phillip Lougher\n");
+
+ return register_filesystem(&squashfs_fs_type);
+}
+
+
+static void __exit exit_squashfs_fs(void)
+{
+ unregister_filesystem(&squashfs_fs_type);
+}
+
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_squashfs_fs);
+module_exit(exit_squashfs_fs);
+MODULE_DESCRIPTION("squashfs 3.1, a compressed read-only filesystem");
+MODULE_AUTHOR("Phillip Lougher <phillip@lougher.demon.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/squashfs-tools/kernel-2.4/fs/squashfs/squashfs.h b/squashfs-tools/kernel-2.4/fs/squashfs/squashfs.h
new file mode 100755
index 0000000..b5f93fb
--- /dev/null
+++ b/squashfs-tools/kernel-2.4/fs/squashfs/squashfs.h
@@ -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
diff --git a/squashfs-tools/kernel-2.4/fs/squashfs/squashfs2_0.c b/squashfs-tools/kernel-2.4/fs/squashfs/squashfs2_0.c
new file mode 100755
index 0000000..6411a04
--- /dev/null
+++ b/squashfs-tools/kernel-2.4/fs/squashfs/squashfs2_0.c
@@ -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;
+}
diff --git a/squashfs-tools/kernel-2.4/include/linux/squashfs_fs.h b/squashfs-tools/kernel-2.4/include/linux/squashfs_fs.h
new file mode 100755
index 0000000..eed48c3
--- /dev/null
+++ b/squashfs-tools/kernel-2.4/include/linux/squashfs_fs.h
@@ -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
diff --git a/squashfs-tools/kernel-2.4/include/linux/squashfs_fs_i.h b/squashfs-tools/kernel-2.4/include/linux/squashfs_fs_i.h
new file mode 100755
index 0000000..1d7720c
--- /dev/null
+++ b/squashfs-tools/kernel-2.4/include/linux/squashfs_fs_i.h
@@ -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
diff --git a/squashfs-tools/kernel-2.4/include/linux/squashfs_fs_sb.h b/squashfs-tools/kernel-2.4/include/linux/squashfs_fs_sb.h
new file mode 100755
index 0000000..ba08d7f
--- /dev/null
+++ b/squashfs-tools/kernel-2.4/include/linux/squashfs_fs_sb.h
@@ -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
diff --git a/squashfs-tools/kernel/Documentation/filesystems/squashfs.txt b/squashfs-tools/kernel/Documentation/filesystems/squashfs.txt
new file mode 100644
index 0000000..3e79e4a
--- /dev/null
+++ b/squashfs-tools/kernel/Documentation/filesystems/squashfs.txt
@@ -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.
diff --git a/squashfs-tools/kernel/README b/squashfs-tools/kernel/README
new file mode 100644
index 0000000..54ab958
--- /dev/null
+++ b/squashfs-tools/kernel/README
@@ -0,0 +1,3 @@
+Squashfs is now in mainline at www.kernel.org.
+
+These files are obsolete and not updated.
diff --git a/squashfs-tools/kernel/fs/squashfs/Makefile b/squashfs-tools/kernel/fs/squashfs/Makefile
new file mode 100644
index 0000000..8258cf9
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/Makefile
@@ -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
diff --git a/squashfs-tools/kernel/fs/squashfs/block.c b/squashfs-tools/kernel/fs/squashfs/block.c
new file mode 100644
index 0000000..c837dfc
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/block.c
@@ -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;
+}
diff --git a/squashfs-tools/kernel/fs/squashfs/cache.c b/squashfs-tools/kernel/fs/squashfs/cache.c
new file mode 100644
index 0000000..f29eda1
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/cache.c
@@ -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;
+}
diff --git a/squashfs-tools/kernel/fs/squashfs/dir.c b/squashfs-tools/kernel/fs/squashfs/dir.c
new file mode 100644
index 0000000..566b0ea
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/dir.c
@@ -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
+};
diff --git a/squashfs-tools/kernel/fs/squashfs/export.c b/squashfs-tools/kernel/fs/squashfs/export.c
new file mode 100644
index 0000000..69e971d
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/export.c
@@ -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
+};
diff --git a/squashfs-tools/kernel/fs/squashfs/file.c b/squashfs-tools/kernel/fs/squashfs/file.c
new file mode 100644
index 0000000..717767d
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/file.c
@@ -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
+};
diff --git a/squashfs-tools/kernel/fs/squashfs/fragment.c b/squashfs-tools/kernel/fs/squashfs/fragment.c
new file mode 100644
index 0000000..b5a2c15
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/fragment.c
@@ -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;
+}
diff --git a/squashfs-tools/kernel/fs/squashfs/id.c b/squashfs-tools/kernel/fs/squashfs/id.c
new file mode 100644
index 0000000..3795b83
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/id.c
@@ -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;
+}
diff --git a/squashfs-tools/kernel/fs/squashfs/inode.c b/squashfs-tools/kernel/fs/squashfs/inode.c
new file mode 100644
index 0000000..7a63398
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/inode.c
@@ -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;
+}
diff --git a/squashfs-tools/kernel/fs/squashfs/namei.c b/squashfs-tools/kernel/fs/squashfs/namei.c
new file mode 100644
index 0000000..9e39865
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/namei.c
@@ -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
+};
diff --git a/squashfs-tools/kernel/fs/squashfs/squashfs.h b/squashfs-tools/kernel/fs/squashfs/squashfs.h
new file mode 100644
index 0000000..6b2515d
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/squashfs.h
@@ -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;
diff --git a/squashfs-tools/kernel/fs/squashfs/squashfs_fs.h b/squashfs-tools/kernel/fs/squashfs/squashfs_fs.h
new file mode 100644
index 0000000..6840da1
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/squashfs_fs.h
@@ -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
diff --git a/squashfs-tools/kernel/fs/squashfs/squashfs_fs_i.h b/squashfs-tools/kernel/fs/squashfs/squashfs_fs_i.h
new file mode 100644
index 0000000..fbfca30
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/squashfs_fs_i.h
@@ -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
diff --git a/squashfs-tools/kernel/fs/squashfs/squashfs_fs_sb.h b/squashfs-tools/kernel/fs/squashfs/squashfs_fs_sb.h
new file mode 100644
index 0000000..c8c6561
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/squashfs_fs_sb.h
@@ -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
diff --git a/squashfs-tools/kernel/fs/squashfs/super.c b/squashfs-tools/kernel/fs/squashfs/super.c
new file mode 100644
index 0000000..a0466d7
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/super.c
@@ -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");
diff --git a/squashfs-tools/kernel/fs/squashfs/symlink.c b/squashfs-tools/kernel/fs/squashfs/symlink.c
new file mode 100644
index 0000000..83d8788
--- /dev/null
+++ b/squashfs-tools/kernel/fs/squashfs/symlink.c
@@ -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
+};
diff --git a/squashfs-tools/kernel/include/linux/squashfs_fs.h b/squashfs-tools/kernel/include/linux/squashfs_fs.h
new file mode 100644
index 0000000..eef85b1
--- /dev/null
+++ b/squashfs-tools/kernel/include/linux/squashfs_fs.h
@@ -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
diff --git a/squashfs-tools/kernel/include/linux/squashfs_fs_i.h b/squashfs-tools/kernel/include/linux/squashfs_fs_i.h
new file mode 100644
index 0000000..f78e6f8
--- /dev/null
+++ b/squashfs-tools/kernel/include/linux/squashfs_fs_i.h
@@ -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
diff --git a/squashfs-tools/kernel/include/linux/squashfs_fs_sb.h b/squashfs-tools/kernel/include/linux/squashfs_fs_sb.h
new file mode 100644
index 0000000..cc9fd5d
--- /dev/null
+++ b/squashfs-tools/kernel/include/linux/squashfs_fs_sb.h
@@ -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
diff --git a/squashfs-tools/squashfs-tools/Android.mk b/squashfs-tools/squashfs-tools/Android.mk
new file mode 100644
index 0000000..79b411b
--- /dev/null
+++ b/squashfs-tools/squashfs-tools/Android.mk
@@ -0,0 +1,42 @@
+# Copyright (C) 2015 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# squashfs-tools depends on Linux Kernel specific headers (e.g. sysinfo.h).
+LOCAL_MODULE_HOST_OS := linux darwin
+
+# The LOCAL_MODULE name is referenced by the code. Don't change it.
+LOCAL_MODULE := mksquashfs
+
+LOCAL_SRC_FILES := \
+ mksquashfs.c \
+ read_fs.c \
+ action.c \
+ swap.c \
+ pseudo.c \
+ compressor.c \
+ sort.c \
+ progressbar.c \
+ read_file.c \
+ info.c \
+ restore.c \
+ process_fragments.c \
+ caches-queues-lists.c \
+ xattr.c \
+ read_xattrs.c \
+ gzip_wrapper.c \
+ android.c \
+ lz4_wrapper.c
+
+LOCAL_CFLAGS := -I -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_GNU_SOURCE -Wall \
+ -DCOMP_DEFAULT="\"lz4\"" -DGZIP_SUPPORT -DLZ4_SUPPORT -DXATTR_SUPPORT -DXATTR_DEFAULT \
+ -Wno-unused-parameter
+
+LOCAL_LDLIBS := -lpthread -lm -lz
+
+LOCAL_SHARED_LIBRARIES := libcutils libselinux
+LOCAL_STATIC_LIBRARIES := liblz4
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/squashfs-tools/squashfs-tools/Makefile b/squashfs-tools/squashfs-tools/Makefile
new file mode 100644
index 0000000..52d2582
--- /dev/null
+++ b/squashfs-tools/squashfs-tools/Makefile
@@ -0,0 +1,305 @@
+###############################################
+# 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
+#
+# To build using XZ Utils liblzma - 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.
+#
+# To build using the LZO library - install the library and uncomment the
+# LZO_SUPPORT line below. If needed, uncomment and set LZO_DIR to the
+# installation prefix.
+#
+#LZO_SUPPORT = 1
+#LZO_DIR = /usr/local
+
+
+########### 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
+#
+# To build configure the tools using cmake to build shared libraries,
+# install and uncomment
+# the LZ4_SUPPORT line below.
+#
+#LZ4_SUPPORT = 1
+
+
+########### Building LZMA support #############
+#
+# LZMA1 compression.
+#
+# LZMA1 compression is deprecated, 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
+
+######## 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
+
+
+###############################################
+# 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 swap.o compressor.o unsquashfs_info.o
+
+CFLAGS ?= -O2
+CFLAGS += $(EXTRA_CFLAGS) $(INCLUDEDIR) -D_FILE_OFFSET_BITS=64 \
+ -D_LARGEFILE_SOURCE -D_GNU_SOURCE -DCOMP_DEFAULT=\"$(COMP_DEFAULT)\" \
+ -Wall
+
+LIBS = -lpthread -lm
+ifeq ($(GZIP_SUPPORT),1)
+CFLAGS += -DGZIP_SUPPORT
+MKSQUASHFS_OBJS += gzip_wrapper.o
+UNSQUASHFS_OBJS += gzip_wrapper.o
+LIBS += -lz
+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 += -llzma
+COMPRESSORS += lzma
+endif
+
+ifeq ($(XZ_SUPPORT),1)
+CFLAGS += -DXZ_SUPPORT
+MKSQUASHFS_OBJS += xz_wrapper.o
+UNSQUASHFS_OBJS += xz_wrapper.o
+LIBS += -llzma
+COMPRESSORS += xz
+endif
+
+ifeq ($(LZO_SUPPORT),1)
+CFLAGS += -DLZO_SUPPORT
+ifdef LZO_DIR
+INCLUDEDIR += -I$(LZO_DIR)/include
+LZO_LIBDIR = -L$(LZO_DIR)/lib
+endif
+MKSQUASHFS_OBJS += lzo_wrapper.o
+UNSQUASHFS_OBJS += lzo_wrapper.o
+LIBS += $(LZO_LIBDIR) -llzo2
+COMPRESSORS += lzo
+endif
+
+ifeq ($(LZ4_SUPPORT),1)
+CFLAGS += -DLZ4_SUPPORT
+MKSQUASHFS_OBJS += lz4_wrapper.o
+UNSQUASHFS_OBJS += lz4_wrapper.o
+LIBS += -llz4
+COMPRESSORS += lz4
+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
+
+#
+# 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 or \
+ LZ4!")
+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
+
+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)
diff --git a/squashfs-tools/squashfs-tools/action.c b/squashfs-tools/squashfs-tools/action.c
new file mode 100644
index 0000000..7e43f17
--- /dev/null
+++ b/squashfs-tools/squashfs-tools/action.c
@@ -0,0 +1,3273 @@
+/*
+ * 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.c
+ */
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fnmatch.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/wait.h>
+#include <regex.h>
+#include <limits.h>
+#include <errno.h>
+
+#ifndef FNM_EXTMATCH /* glibc extension */
+ #define FNM_EXTMATCH 0
+#endif
+
+#include "squashfs_fs.h"
+#include "mksquashfs.h"
+#include "action.h"
+#include "error.h"
+
+/*
+ * code to parse actions
+ */
+
+static char *cur_ptr, *source;
+static struct action *fragment_spec = NULL;
+static struct action *exclude_spec = NULL;
+static struct action *empty_spec = NULL;
+static struct action *move_spec = NULL;
+static struct action *prune_spec = NULL;
+static struct action *other_spec = NULL;
+static int fragment_count = 0;
+static int exclude_count = 0;
+static int empty_count = 0;
+static int move_count = 0;
+static int prune_count = 0;
+static int other_count = 0;
+static struct action_entry *parsing_action;
+
+static struct file_buffer *def_fragment = NULL;
+
+static struct token_entry token_table[] = {
+ { "(", TOK_OPEN_BRACKET, 1, },
+ { ")", TOK_CLOSE_BRACKET, 1 },
+ { "&&", TOK_AND, 2 },
+ { "||", TOK_OR, 2 },
+ { "!", TOK_NOT, 1 },
+ { ",", TOK_COMMA, 1 },
+ { "@", TOK_AT, 1},
+ { " ", TOK_WHITE_SPACE, 1 },
+ { "\t ", TOK_WHITE_SPACE, 1 },
+ { "", -1, 0 }
+};
+
+
+static struct test_entry test_table[];
+
+static struct action_entry action_table[];
+
+static struct expr *parse_expr(int subexp);
+
+extern char *pathname(struct dir_ent *);
+
+extern char *subpathname(struct dir_ent *);
+
+extern int read_file(char *filename, char *type, int (parse_line)(char *));
+
+/*
+ * Lexical analyser
+ */
+#define STR_SIZE 256
+
+static int get_token(char **string)
+{
+ /* string buffer */
+ static char *str = NULL;
+ static int size = 0;
+
+ char *str_ptr;
+ int cur_size, i, quoted;
+
+ while (1) {
+ if (*cur_ptr == '\0')
+ return TOK_EOF;
+ for (i = 0; token_table[i].token != -1; i++)
+ if (strncmp(cur_ptr, token_table[i].string,
+ token_table[i].size) == 0)
+ break;
+ if (token_table[i].token != TOK_WHITE_SPACE)
+ break;
+ cur_ptr ++;
+ }
+
+ if (token_table[i].token != -1) {
+ cur_ptr += token_table[i].size;
+ return token_table[i].token;
+ }
+
+ /* string */
+ if(str == NULL) {
+ str = malloc(STR_SIZE);
+ if(str == NULL)
+ MEM_ERROR();
+ size = STR_SIZE;
+ }
+
+ /* Initialise string being read */
+ str_ptr = str;
+ cur_size = 0;
+ quoted = 0;
+
+ while(1) {
+ while(*cur_ptr == '"') {
+ cur_ptr ++;
+ quoted = !quoted;
+ }
+
+ if(*cur_ptr == '\0') {
+ /* inside quoted string EOF, otherwise end of string */
+ if(quoted)
+ return TOK_EOF;
+ else
+ break;
+ }
+
+ if(!quoted) {
+ for(i = 0; token_table[i].token != -1; i++)
+ if (strncmp(cur_ptr, token_table[i].string,
+ token_table[i].size) == 0)
+ break;
+ if (token_table[i].token != -1)
+ break;
+ }
+
+ if(*cur_ptr == '\\') {
+ cur_ptr ++;
+ if(*cur_ptr == '\0')
+ return TOK_EOF;
+ }
+
+ if(cur_size + 2 > size) {
+ char *tmp;
+
+ size = (cur_size + 1 + STR_SIZE) & ~(STR_SIZE - 1);
+
+ tmp = realloc(str, size);
+ if(tmp == NULL)
+ MEM_ERROR();
+
+ str_ptr = str_ptr - str + tmp;
+ str = tmp;
+ }
+
+ *str_ptr ++ = *cur_ptr ++;
+ cur_size ++;
+ }
+
+ *str_ptr = '\0';
+ *string = str;
+ return TOK_STRING;
+}
+
+
+static int peek_token(char **string)
+{
+ char *saved = cur_ptr;
+ int token = get_token(string);
+
+ cur_ptr = saved;
+
+ return token;
+}
+
+
+/*
+ * Expression parser
+ */
+static void free_parse_tree(struct expr *expr)
+{
+ if(expr->type == ATOM_TYPE) {
+ int i;
+
+ for(i = 0; i < expr->atom.test->args; i++)
+ free(expr->atom.argv[i]);
+
+ free(expr->atom.argv);
+ } else if (expr->type == UNARY_TYPE)
+ free_parse_tree(expr->unary_op.expr);
+ else {
+ free_parse_tree(expr->expr_op.lhs);
+ free_parse_tree(expr->expr_op.rhs);
+ }
+
+ free(expr);
+}
+
+
+static struct expr *create_expr(struct expr *lhs, int op, struct expr *rhs)
+{
+ struct expr *expr;
+
+ if (rhs == NULL) {
+ free_parse_tree(lhs);
+ return NULL;
+ }
+
+ expr = malloc(sizeof(*expr));
+ if (expr == NULL)
+ MEM_ERROR();
+
+ expr->type = OP_TYPE;
+ expr->expr_op.lhs = lhs;
+ expr->expr_op.rhs = rhs;
+ expr->expr_op.op = op;
+
+ return expr;
+}
+
+
+static struct expr *create_unary_op(struct expr *lhs, int op)
+{
+ struct expr *expr;
+
+ if (lhs == NULL)
+ return NULL;
+
+ expr = malloc(sizeof(*expr));
+ if (expr == NULL)
+ MEM_ERROR();
+
+ expr->type = UNARY_TYPE;
+ expr->unary_op.expr = lhs;
+ expr->unary_op.op = op;
+
+ return expr;
+}
+
+
+static struct expr *parse_test(char *name)
+{
+ char *string, **argv = NULL;
+ int token, args = 0;
+ int i;
+ struct test_entry *test;
+ struct expr *expr;
+
+ for (i = 0; test_table[i].args != -1; i++)
+ if (strcmp(name, test_table[i].name) == 0)
+ break;
+
+ test = &test_table[i];
+
+ if (test->args == -1) {
+ SYNTAX_ERROR("Non-existent test \"%s\"\n", name);
+ return NULL;
+ }
+
+ if(parsing_action->type == EXCLUDE_ACTION && !test->exclude_ok) {
+ fprintf(stderr, "Failed to parse action \"%s\"\n", source);
+ fprintf(stderr, "Test \"%s\" cannot be used in exclude "
+ "actions\n", name);
+ fprintf(stderr, "Use prune action instead ...\n");
+ return NULL;
+ }
+
+ expr = malloc(sizeof(*expr));
+ if (expr == NULL)
+ MEM_ERROR();
+
+ expr->type = ATOM_TYPE;
+
+ expr->atom.test = test;
+ expr->atom.data = NULL;
+
+ /*
+ * If the test has no arguments, then go straight to checking if there's
+ * enough arguments
+ */
+ token = peek_token(&string);
+
+ if (token != TOK_OPEN_BRACKET)
+ goto skip_args;
+
+ get_token(&string);
+
+ /*
+ * speculatively read all the arguments, and then see if the
+ * number of arguments read is the number expected, this handles
+ * tests with a variable number of arguments
+ */
+ token = get_token(&string);
+ if (token == TOK_CLOSE_BRACKET)
+ goto skip_args;
+
+ while(1) {
+ if (token != TOK_STRING) {
+ SYNTAX_ERROR("Unexpected token \"%s\", expected "
+ "argument\n", TOK_TO_STR(token, string));
+ goto failed;
+ }
+
+ argv = realloc(argv, (args + 1) * sizeof(char *));
+ if (argv == NULL)
+ MEM_ERROR();
+
+ argv[args ++ ] = strdup(string);
+
+ token = get_token(&string);
+
+ if (token == TOK_CLOSE_BRACKET)
+ break;
+
+ if (token != TOK_COMMA) {
+ SYNTAX_ERROR("Unexpected token \"%s\", expected "
+ "\",\" or \")\"\n", TOK_TO_STR(token, string));
+ goto failed;
+ }
+ token = get_token(&string);
+ }
+
+skip_args:
+ /*
+ * expected number of arguments?
+ */
+ if(test->args != -2 && args != test->args) {
+ SYNTAX_ERROR("Unexpected number of arguments, expected %d, "
+ "got %d\n", test->args, args);
+ goto failed;
+ }
+
+ expr->atom.args = args;
+ expr->atom.argv = argv;
+
+ if (test->parse_args) {
+ int res = test->parse_args(test, &expr->atom);
+
+ if (res == 0)
+ goto failed;
+ }
+
+ return expr;
+
+failed:
+ free(argv);
+ free(expr);
+ return NULL;
+}
+
+
+static struct expr *get_atom()
+{
+ char *string;
+ int token = get_token(&string);
+
+ switch(token) {
+ case TOK_NOT:
+ return create_unary_op(get_atom(), token);
+ case TOK_OPEN_BRACKET:
+ return parse_expr(1);
+ case TOK_STRING:
+ return parse_test(string);
+ default:
+ SYNTAX_ERROR("Unexpected token \"%s\", expected test "
+ "operation, \"!\", or \"(\"\n",
+ TOK_TO_STR(token, string));
+ return NULL;
+ }
+}
+
+
+static struct expr *parse_expr(int subexp)
+{
+ struct expr *expr = get_atom();
+
+ while (expr) {
+ char *string;
+ int op = get_token(&string);
+
+ if (op == TOK_EOF) {
+ if (subexp) {
+ free_parse_tree(expr);
+ SYNTAX_ERROR("Expected \"&&\", \"||\" or "
+ "\")\", got EOF\n");
+ return NULL;
+ }
+ break;
+ }
+
+ if (op == TOK_CLOSE_BRACKET) {
+ if (!subexp) {
+ free_parse_tree(expr);
+ SYNTAX_ERROR("Unexpected \")\", expected "
+ "\"&&\", \"!!\" or EOF\n");
+ return NULL;
+ }
+ break;
+ }
+
+ if (op != TOK_AND && op != TOK_OR) {
+ free_parse_tree(expr);
+ SYNTAX_ERROR("Unexpected token \"%s\", expected "
+ "\"&&\" or \"||\"\n", TOK_TO_STR(op, string));
+ return NULL;
+ }
+
+ expr = create_expr(expr, op, get_atom());
+ }
+
+ return expr;
+}
+
+
+/*
+ * Action parser
+ */
+int parse_action(char *s, int verbose)
+{
+ char *string, **argv = NULL;
+ int i, token, args = 0;
+ struct expr *expr;
+ struct action_entry *action;
+ void *data = NULL;
+ struct action **spec_list;
+ int spec_count;
+
+ cur_ptr = source = s;
+ token = get_token(&string);
+
+ if (token != TOK_STRING) {
+ SYNTAX_ERROR("Unexpected token \"%s\", expected name\n",
+ TOK_TO_STR(token, string));
+ return 0;
+ }
+
+ for (i = 0; action_table[i].args != -1; i++)
+ if (strcmp(string, action_table[i].name) == 0)
+ break;
+
+ if (action_table[i].args == -1) {
+ SYNTAX_ERROR("Non-existent action \"%s\"\n", string);
+ return 0;
+ }
+
+ action = &action_table[i];
+
+ token = get_token(&string);
+
+ if (token == TOK_AT)
+ goto skip_args;
+
+ if (token != TOK_OPEN_BRACKET) {
+ SYNTAX_ERROR("Unexpected token \"%s\", expected \"(\"\n",
+ TOK_TO_STR(token, string));
+ goto failed;
+ }
+
+ /*
+ * speculatively read all the arguments, and then see if the
+ * number of arguments read is the number expected, this handles
+ * actions with a variable number of arguments
+ */
+ token = get_token(&string);
+ if (token == TOK_CLOSE_BRACKET)
+ goto skip_args;
+
+ while (1) {
+ if (token != TOK_STRING) {
+ SYNTAX_ERROR("Unexpected token \"%s\", expected "
+ "argument\n", TOK_TO_STR(token, string));
+ goto failed;
+ }
+
+ argv = realloc(argv, (args + 1) * sizeof(char *));
+ if (argv == NULL)
+ MEM_ERROR();
+
+ argv[args ++] = strdup(string);
+
+ token = get_token(&string);
+
+ if (token == TOK_CLOSE_BRACKET)
+ break;
+
+ if (token != TOK_COMMA) {
+ SYNTAX_ERROR("Unexpected token \"%s\", expected "
+ "\",\" or \")\"\n", TOK_TO_STR(token, string));
+ goto failed;
+ }
+ token = get_token(&string);
+ }
+
+skip_args:
+ /*
+ * expected number of arguments?
+ */
+ if(action->args != -2 && args != action->args) {
+ SYNTAX_ERROR("Unexpected number of arguments, expected %d, "
+ "got %d\n", action->args, args);
+ goto failed;
+ }
+
+ if (action->parse_args) {
+ int res = action->parse_args(action, args, argv, &data);
+
+ if (res == 0)
+ goto failed;
+ }
+
+ if (token == TOK_CLOSE_BRACKET)
+ token = get_token(&string);
+
+ if (token != TOK_AT) {
+ SYNTAX_ERROR("Unexpected token \"%s\", expected \"@\"\n",
+ TOK_TO_STR(token, string));
+ goto failed;
+ }
+
+ parsing_action = action;
+ expr = parse_expr(0);
+
+ if (expr == NULL)
+ goto failed;
+
+ /*
+ * choose action list and increment action counter
+ */
+ switch(action->type) {
+ case FRAGMENT_ACTION:
+ spec_count = fragment_count ++;
+ spec_list = &fragment_spec;
+ break;
+ case EXCLUDE_ACTION:
+ spec_count = exclude_count ++;
+ spec_list = &exclude_spec;
+ break;
+ case EMPTY_ACTION:
+ spec_count = empty_count ++;
+ spec_list = &empty_spec;
+ break;
+ case MOVE_ACTION:
+ spec_count = move_count ++;
+ spec_list = &move_spec;
+ break;
+ case PRUNE_ACTION:
+ spec_count = prune_count ++;
+ spec_list = &prune_spec;
+ break;
+ default:
+ spec_count = other_count ++;
+ spec_list = &other_spec;
+ }
+
+ *spec_list = realloc(*spec_list, (spec_count + 1) *
+ sizeof(struct action));
+ if (*spec_list == NULL)
+ MEM_ERROR();
+
+ (*spec_list)[spec_count].type = action->type;
+ (*spec_list)[spec_count].action = action;
+ (*spec_list)[spec_count].args = args;
+ (*spec_list)[spec_count].argv = argv;
+ (*spec_list)[spec_count].expr = expr;
+ (*spec_list)[spec_count].data = data;
+ (*spec_list)[spec_count].verbose = verbose;
+
+ return 1;
+
+failed:
+ free(argv);
+ return 0;
+}
+
+
+/*
+ * Evaluate expressions
+ */
+
+#define ALLOC_SZ 128
+
+#define LOG_ENABLE 0
+#define LOG_DISABLE 1
+#define LOG_PRINT 2
+#define LOG_ENABLED 3
+
+char *_expr_log(char *string, int cmnd)
+{
+ static char *expr_msg = NULL;
+ static int cur_size = 0, alloc_size = 0;
+ int size;
+
+ switch(cmnd) {
+ case LOG_ENABLE:
+ expr_msg = malloc(ALLOC_SZ);
+ alloc_size = ALLOC_SZ;
+ cur_size = 0;
+ return expr_msg;
+ case LOG_DISABLE:
+ free(expr_msg);
+ alloc_size = cur_size = 0;
+ return expr_msg = NULL;
+ case LOG_ENABLED:
+ return expr_msg;
+ default:
+ if(expr_msg == NULL)
+ return NULL;
+ break;
+ }
+
+ /* if string is empty append '\0' */
+ size = strlen(string) ? : 1;
+
+ if(alloc_size - cur_size < size) {
+ /* buffer too small, expand */
+ alloc_size = (cur_size + size + ALLOC_SZ - 1) & ~(ALLOC_SZ - 1);
+
+ expr_msg = realloc(expr_msg, alloc_size);
+ if(expr_msg == NULL)
+ MEM_ERROR();
+ }
+
+ memcpy(expr_msg + cur_size, string, size);
+ cur_size += size;
+
+ return expr_msg;
+}
+
+
+char *expr_log_cmnd(int cmnd)
+{
+ return _expr_log(NULL, cmnd);
+}
+
+
+char *expr_log(char *string)
+{
+ return _expr_log(string, LOG_PRINT);
+}
+
+
+void expr_log_atom(struct atom *atom)
+{
+ int i;
+
+ if(atom->test->handle_logging)
+ return;
+
+ expr_log(atom->test->name);
+
+ if(atom->args) {
+ expr_log("(");
+ for(i = 0; i < atom->args; i++) {
+ expr_log(atom->argv[i]);
+ if (i + 1 < atom->args)
+ expr_log(",");
+ }
+ expr_log(")");
+ }
+}
+
+
+void expr_log_match(int match)
+{
+ if(match)
+ expr_log("=True");
+ else
+ expr_log("=False");
+}
+
+
+static int eval_expr_log(struct expr *expr, struct action_data *action_data)
+{
+ int match;
+
+ switch (expr->type) {
+ case ATOM_TYPE:
+ expr_log_atom(&expr->atom);
+ match = expr->atom.test->fn(&expr->atom, action_data);
+ expr_log_match(match);
+ break;
+ case UNARY_TYPE:
+ expr_log("!");
+ match = !eval_expr_log(expr->unary_op.expr, action_data);
+ break;
+ default:
+ expr_log("(");
+ match = eval_expr_log(expr->expr_op.lhs, action_data);
+
+ if ((expr->expr_op.op == TOK_AND && match) ||
+ (expr->expr_op.op == TOK_OR && !match)) {
+ expr_log(token_table[expr->expr_op.op].string);
+ match = eval_expr_log(expr->expr_op.rhs, action_data);
+ }
+ expr_log(")");
+ break;
+ }
+
+ return match;
+}
+
+
+static int eval_expr(struct expr *expr, struct action_data *action_data)
+{
+ int match;
+
+ switch (expr->type) {
+ case ATOM_TYPE:
+ match = expr->atom.test->fn(&expr->atom, action_data);
+ break;
+ case UNARY_TYPE:
+ match = !eval_expr(expr->unary_op.expr, action_data);
+ break;
+ default:
+ match = eval_expr(expr->expr_op.lhs, action_data);
+
+ if ((expr->expr_op.op == TOK_AND && match) ||
+ (expr->expr_op.op == TOK_OR && !match))
+ match = eval_expr(expr->expr_op.rhs, action_data);
+ break;
+ }
+
+ return match;
+}
+
+
+static int eval_expr_top(struct action *action, struct action_data *action_data)
+{
+ if(action->verbose) {
+ int match, n;
+
+ expr_log_cmnd(LOG_ENABLE);
+
+ if(action_data->subpath)
+ expr_log(action_data->subpath);
+
+ expr_log("=");
+ expr_log(action->action->name);
+
+ if(action->args) {
+ expr_log("(");
+ for (n = 0; n < action->args; n++) {
+ expr_log(action->argv[n]);
+ if(n + 1 < action->args)
+ expr_log(",");
+ }
+ expr_log(")");
+ }
+
+ expr_log("@");
+
+ match = eval_expr_log(action->expr, action_data);
+
+ /*
+ * Print the evaluated expression log, if the
+ * result matches the logging specified
+ */
+ if((match && (action->verbose & ACTION_LOG_TRUE)) || (!match
+ && (action->verbose & ACTION_LOG_FALSE)))
+ progressbar_info("%s\n", expr_log(""));
+
+ expr_log_cmnd(LOG_DISABLE);
+
+ return match;
+ } else
+ return eval_expr(action->expr, action_data);
+}
+
+
+/*
+ * Read action file, passing each line to parse_action() for
+ * parsing.
+ *
+ * One action per line, of the form
+ * action(arg1,arg2)@expr(arg1,arg2)....
+ *
+ * Actions can be split across multiple lines using "\".
+ *
+ * Blank lines and comment lines indicated by # are supported.
+ */
+int parse_action_true(char *s)
+{
+ return parse_action(s, ACTION_LOG_TRUE);
+}
+
+
+int parse_action_false(char *s)
+{
+ return parse_action(s, ACTION_LOG_FALSE);
+}
+
+
+int parse_action_verbose(char *s)
+{
+ return parse_action(s, ACTION_LOG_VERBOSE);
+}
+
+
+int parse_action_nonverbose(char *s)
+{
+ return parse_action(s, ACTION_LOG_NONE);
+}
+
+
+int read_action_file(char *filename, int verbose)
+{
+ switch(verbose) {
+ case ACTION_LOG_TRUE:
+ return read_file(filename, "action", parse_action_true);
+ case ACTION_LOG_FALSE:
+ return read_file(filename, "action", parse_action_false);
+ case ACTION_LOG_VERBOSE:
+ return read_file(filename, "action", parse_action_verbose);
+ default:
+ return read_file(filename, "action", parse_action_nonverbose);
+ }
+}
+
+
+/*
+ * helper to evaluate whether action/test acts on this file type
+ */
+static int file_type_match(int st_mode, int type)
+{
+ switch(type) {
+ case ACTION_DIR:
+ return S_ISDIR(st_mode);
+ case ACTION_REG:
+ return S_ISREG(st_mode);
+ case ACTION_ALL:
+ return S_ISREG(st_mode) || S_ISDIR(st_mode) ||
+ S_ISCHR(st_mode) || S_ISBLK(st_mode) ||
+ S_ISFIFO(st_mode) || S_ISSOCK(st_mode);
+ case ACTION_LNK:
+ return S_ISLNK(st_mode);
+ case ACTION_ALL_LNK:
+ default:
+ return 1;
+ }
+}
+
+
+/*
+ * General action evaluation code
+ */
+int actions()
+{
+ return other_count;
+}
+
+
+void eval_actions(struct dir_info *root, struct dir_ent *dir_ent)
+{
+ int i, match;
+ struct action_data action_data;
+ int st_mode = dir_ent->inode->buf.st_mode;
+
+ action_data.name = dir_ent->name;
+ action_data.pathname = strdup(pathname(dir_ent));
+ action_data.subpath = strdup(subpathname(dir_ent));
+ action_data.buf = &dir_ent->inode->buf;
+ action_data.depth = dir_ent->our_dir->depth;
+ action_data.dir_ent = dir_ent;
+ action_data.root = root;
+
+ for (i = 0; i < other_count; i++) {
+ struct action *action = &other_spec[i];
+
+ if (!file_type_match(st_mode, action->action->file_types))
+ /* action does not operate on this file type */
+ continue;
+
+ match = eval_expr_top(action, &action_data);
+
+ if (match)
+ action->action->run_action(action, dir_ent);
+ }
+
+ free(action_data.pathname);
+ free(action_data.subpath);
+}
+
+
+/*
+ * Fragment specific action code
+ */
+void *eval_frag_actions(struct dir_info *root, struct dir_ent *dir_ent)
+{
+ int i, match;
+ struct action_data action_data;
+
+ action_data.name = dir_ent->name;
+ action_data.pathname = strdup(pathname(dir_ent));
+ action_data.subpath = strdup(subpathname(dir_ent));
+ action_data.buf = &dir_ent->inode->buf;
+ action_data.depth = dir_ent->our_dir->depth;
+ action_data.dir_ent = dir_ent;
+ action_data.root = root;
+
+ for (i = 0; i < fragment_count; i++) {
+ match = eval_expr_top(&fragment_spec[i], &action_data);
+ if (match) {
+ free(action_data.pathname);
+ free(action_data.subpath);
+ return &fragment_spec[i].data;
+ }
+ }
+
+ free(action_data.pathname);
+ free(action_data.subpath);
+ return &def_fragment;
+}
+
+
+void *get_frag_action(void *fragment)
+{
+ struct action *spec_list_end = &fragment_spec[fragment_count];
+ struct action *action;
+
+ if (fragment == NULL)
+ return &def_fragment;
+
+ if (fragment_count == 0)
+ return NULL;
+
+ if (fragment == &def_fragment)
+ action = &fragment_spec[0] - 1;
+ else
+ action = fragment - offsetof(struct action, data);
+
+ if (++action == spec_list_end)
+ return NULL;
+
+ return &action->data;
+}
+
+
+/*
+ * Exclude specific action code
+ */
+int exclude_actions()
+{
+ return exclude_count;
+}
+
+
+int eval_exclude_actions(char *name, char *pathname, char *subpath,
+ struct stat *buf, int depth, struct dir_ent *dir_ent)
+{
+ int i, match = 0;
+ struct action_data action_data;
+
+ action_data.name = name;
+ action_data.pathname = pathname;
+ action_data.subpath = subpath;
+ action_data.buf = buf;
+ action_data.depth = depth;
+ action_data.dir_ent = dir_ent;
+
+ for (i = 0; i < exclude_count && !match; i++)
+ match = eval_expr_top(&exclude_spec[i], &action_data);
+
+ return match;
+}
+
+
+/*
+ * Fragment specific action code
+ */
+static void frag_action(struct action *action, struct dir_ent *dir_ent)
+{
+ struct inode_info *inode = dir_ent->inode;
+
+ inode->no_fragments = 0;
+}
+
+static void no_frag_action(struct action *action, struct dir_ent *dir_ent)
+{
+ struct inode_info *inode = dir_ent->inode;
+
+ inode->no_fragments = 1;
+}
+
+static void always_frag_action(struct action *action, struct dir_ent *dir_ent)
+{
+ struct inode_info *inode = dir_ent->inode;
+
+ inode->always_use_fragments = 1;
+}
+
+static void no_always_frag_action(struct action *action, struct dir_ent *dir_ent)
+{
+ struct inode_info *inode = dir_ent->inode;
+
+ inode->always_use_fragments = 0;
+}
+
+
+/*
+ * Compression specific action code
+ */
+static void comp_action(struct action *action, struct dir_ent *dir_ent)
+{
+ struct inode_info *inode = dir_ent->inode;
+
+ inode->noD = inode->noF = 0;
+}
+
+static void uncomp_action(struct action *action, struct dir_ent *dir_ent)
+{
+ struct inode_info *inode = dir_ent->inode;
+
+ inode->noD = inode->noF = 1;
+}
+
+
+/*
+ * Uid/gid specific action code
+ */
+static long long parse_uid(char *arg) {
+ char *b;
+ long long uid = strtoll(arg, &b, 10);
+
+ if (*b == '\0') {
+ if (uid < 0 || uid >= (1LL << 32)) {
+ SYNTAX_ERROR("Uid out of range\n");
+ return -1;
+ }
+ } else {
+ struct passwd *passwd = getpwnam(arg);
+
+ if (passwd)
+ uid = passwd->pw_uid;
+ else {
+ SYNTAX_ERROR("Invalid uid or unknown user\n");
+ return -1;
+ }
+ }
+
+ return uid;
+}
+
+
+static long long parse_gid(char *arg) {
+ char *b;
+ long long gid = strtoll(arg, &b, 10);
+
+ if (*b == '\0') {
+ if (gid < 0 || gid >= (1LL << 32)) {
+ SYNTAX_ERROR("Gid out of range\n");
+ return -1;
+ }
+ } else {
+ struct group *group = getgrnam(arg);
+
+ if (group)
+ gid = group->gr_gid;
+ else {
+ SYNTAX_ERROR("Invalid gid or unknown group\n");
+ return -1;
+ }
+ }
+
+ return gid;
+}
+
+
+static int parse_uid_args(struct action_entry *action, int args, char **argv,
+ void **data)
+{
+ long long uid;
+ struct uid_info *uid_info;
+
+ uid = parse_uid(argv[0]);
+ if (uid == -1)
+ return 0;
+
+ uid_info = malloc(sizeof(struct uid_info));
+ if (uid_info == NULL)
+ MEM_ERROR();
+
+ uid_info->uid = uid;
+ *data = uid_info;
+
+ return 1;
+}
+
+
+static int parse_gid_args(struct action_entry *action, int args, char **argv,
+ void **data)
+{
+ long long gid;
+ struct gid_info *gid_info;
+
+ gid = parse_gid(argv[0]);
+ if (gid == -1)
+ return 0;
+
+ gid_info = malloc(sizeof(struct gid_info));
+ if (gid_info == NULL)
+ MEM_ERROR();
+
+ gid_info->gid = gid;
+ *data = gid_info;
+
+ return 1;
+}
+
+
+static int parse_guid_args(struct action_entry *action, int args, char **argv,
+ void **data)
+{
+ long long uid, gid;
+ struct guid_info *guid_info;
+
+ uid = parse_uid(argv[0]);
+ if (uid == -1)
+ return 0;
+
+ gid = parse_gid(argv[1]);
+ if (gid == -1)
+ return 0;
+
+ guid_info = malloc(sizeof(struct guid_info));
+ if (guid_info == NULL)
+ MEM_ERROR();
+
+ guid_info->uid = uid;
+ guid_info->gid = gid;
+ *data = guid_info;
+
+ return 1;
+}
+
+
+static void uid_action(struct action *action, struct dir_ent *dir_ent)
+{
+ struct inode_info *inode = dir_ent->inode;
+ struct uid_info *uid_info = action->data;
+
+ inode->buf.st_uid = uid_info->uid;
+}
+
+static void gid_action(struct action *action, struct dir_ent *dir_ent)
+{
+ struct inode_info *inode = dir_ent->inode;
+ struct gid_info *gid_info = action->data;
+
+ inode->buf.st_gid = gid_info->gid;
+}
+
+static void guid_action(struct action *action, struct dir_ent *dir_ent)
+{
+ struct inode_info *inode = dir_ent->inode;
+ struct guid_info *guid_info = action->data;
+
+ inode->buf.st_uid = guid_info->uid;
+ inode->buf.st_gid = guid_info->gid;
+
+}
+
+
+/*
+ * Mode specific action code
+ */
+static int parse_octal_mode_args(int args, char **argv,
+ void **data)
+{
+ int n, bytes;
+ unsigned int mode;
+ struct mode_data *mode_data;
+
+ /* octal mode number? */
+ n = sscanf(argv[0], "%o%n", &mode, &bytes);
+ if (n == 0)
+ return -1; /* not an octal number arg */
+
+
+ /* check there's no trailing junk */
+ if (argv[0][bytes] != '\0') {
+ SYNTAX_ERROR("Unexpected trailing bytes after octal "
+ "mode number\n");
+ return 0; /* bad octal number arg */
+ }
+
+ /* check there's only one argument */
+ if (args > 1) {
+ SYNTAX_ERROR("Octal mode number is first argument, "
+ "expected one argument, got %d\n", args);
+ return 0; /* bad octal number arg */
+ }
+
+ /* check mode is within range */
+ if (mode > 07777) {
+ SYNTAX_ERROR("Octal mode %o is out of range\n", mode);
+ return 0; /* bad octal number arg */
+ }
+
+ mode_data = malloc(sizeof(struct mode_data));
+ if (mode_data == NULL)
+ MEM_ERROR();
+
+ mode_data->operation = ACTION_MODE_OCT;
+ mode_data->mode = mode;
+ mode_data->next = NULL;
+ *data = mode_data;
+
+ return 1;
+}
+
+
+/*
+ * Parse symbolic mode of format [ugoa]*[[+-=]PERMS]+
+ * PERMS = [rwxXst]+ or [ugo]
+ */
+static int parse_sym_mode_arg(char *arg, struct mode_data **head,
+ struct mode_data **cur)
+{
+ struct mode_data *mode_data;
+ int mode;
+ int mask = 0;
+ int op;
+ char X;
+
+ if (arg[0] != 'u' && arg[0] != 'g' && arg[0] != 'o' && arg[0] != 'a') {
+ /* no ownership specifiers, default to a */
+ mask = 0777;
+ goto parse_operation;
+ }
+
+ /* parse ownership specifiers */
+ while(1) {
+ switch(*arg) {
+ case 'u':
+ mask |= 04700;
+ break;
+ case 'g':
+ mask |= 02070;
+ break;
+ case 'o':
+ mask |= 01007;
+ break;
+ case 'a':
+ mask = 07777;
+ break;
+ default:
+ goto parse_operation;
+ }
+ arg ++;
+ }
+
+parse_operation:
+ /* trap a symbolic mode with just an ownership specification */
+ if(*arg == '\0') {
+ SYNTAX_ERROR("Expected one of '+', '-' or '=', got EOF\n");
+ goto failed;
+ }
+
+ while(*arg != '\0') {
+ mode = 0;
+ X = 0;
+
+ switch(*arg) {
+ case '+':
+ op = ACTION_MODE_ADD;
+ break;
+ case '-':
+ op = ACTION_MODE_REM;
+ break;
+ case '=':
+ op = ACTION_MODE_SET;
+ break;
+ default:
+ SYNTAX_ERROR("Expected one of '+', '-' or '=', got "
+ "'%c'\n", *arg);
+ goto failed;
+ }
+
+ arg ++;
+
+ /* Parse PERMS */
+ if (*arg == 'u' || *arg == 'g' || *arg == 'o') {
+ /* PERMS = [ugo] */
+ mode = - *arg;
+ arg ++;
+ } else {
+ /* PERMS = [rwxXst]* */
+ while(1) {
+ switch(*arg) {
+ case 'r':
+ mode |= 0444;
+ break;
+ case 'w':
+ mode |= 0222;
+ break;
+ case 'x':
+ mode |= 0111;
+ break;
+ case 's':
+ mode |= 06000;
+ break;
+ case 't':
+ mode |= 01000;
+ break;
+ case 'X':
+ X = 1;
+ break;
+ case '+':
+ case '-':
+ case '=':
+ case '\0':
+ mode &= mask;
+ goto perms_parsed;
+ default:
+ SYNTAX_ERROR("Unrecognised permission "
+ "'%c'\n", *arg);
+ goto failed;
+ }
+
+ arg ++;
+ }
+ }
+
+perms_parsed:
+ mode_data = malloc(sizeof(*mode_data));
+ if (mode_data == NULL)
+ MEM_ERROR();
+
+ mode_data->operation = op;
+ mode_data->mode = mode;
+ mode_data->mask = mask;
+ mode_data->X = X;
+ mode_data->next = NULL;
+
+ if (*cur) {
+ (*cur)->next = mode_data;
+ *cur = mode_data;
+ } else
+ *head = *cur = mode_data;
+ }
+
+ return 1;
+
+failed:
+ return 0;
+}
+
+
+static int parse_sym_mode_args(struct action_entry *action, int args,
+ char **argv, void **data)
+{
+ int i, res = 1;
+ struct mode_data *head = NULL, *cur = NULL;
+
+ for (i = 0; i < args && res; i++)
+ res = parse_sym_mode_arg(argv[i], &head, &cur);
+
+ *data = head;
+
+ return res;
+}
+
+
+static int parse_mode_args(struct action_entry *action, int args,
+ char **argv, void **data)
+{
+ int res;
+
+ if (args == 0) {
+ SYNTAX_ERROR("Mode action expects one or more arguments\n");
+ return 0;
+ }
+
+ res = parse_octal_mode_args(args, argv, data);
+ if(res >= 0)
+ /* Got an octal mode argument */
+ return res;
+ else /* not an octal mode argument */
+ return parse_sym_mode_args(action, args, argv, data);
+}
+
+
+static int mode_execute(struct mode_data *mode_data, int st_mode)
+{
+ int mode = 0;
+
+ for (;mode_data; mode_data = mode_data->next) {
+ if (mode_data->mode < 0) {
+ /* 'u', 'g' or 'o' */
+ switch(-mode_data->mode) {
+ case 'u':
+ mode = (st_mode >> 6) & 07;
+ break;
+ case 'g':
+ mode = (st_mode >> 3) & 07;
+ break;
+ case 'o':
+ mode = st_mode & 07;
+ break;
+ }
+ mode = ((mode << 6) | (mode << 3) | mode) &
+ mode_data->mask;
+ } else if (mode_data->X &&
+ ((st_mode & S_IFMT) == S_IFDIR ||
+ (st_mode & 0111)))
+ /* X permission, only takes effect if inode is a
+ * directory or x is set for some owner */
+ mode = mode_data->mode | (0111 & mode_data->mask);
+ else
+ mode = mode_data->mode;
+
+ switch(mode_data->operation) {
+ case ACTION_MODE_OCT:
+ st_mode = (st_mode & S_IFMT) | mode;
+ break;
+ case ACTION_MODE_SET:
+ st_mode = (st_mode & ~mode_data->mask) | mode;
+ break;
+ case ACTION_MODE_ADD:
+ st_mode |= mode;
+ break;
+ case ACTION_MODE_REM:
+ st_mode &= ~mode;
+ }
+ }
+
+ return st_mode;
+}
+
+
+static void mode_action(struct action *action, struct dir_ent *dir_ent)
+{
+ dir_ent->inode->buf.st_mode = mode_execute(action->data,
+ dir_ent->inode->buf.st_mode);
+}
+
+
+/*
+ * Empty specific action code
+ */
+int empty_actions()
+{
+ return empty_count;
+}
+
+
+static int parse_empty_args(struct action_entry *action, int args,
+ char **argv, void **data)
+{
+ struct empty_data *empty_data;
+ int val;
+
+ if (args >= 2) {
+ SYNTAX_ERROR("Empty action expects zero or one argument\n");
+ return 0;
+ }
+
+ if (args == 0 || strcmp(argv[0], "all") == 0)
+ val = EMPTY_ALL;
+ else if (strcmp(argv[0], "source") == 0)
+ val = EMPTY_SOURCE;
+ else if (strcmp(argv[0], "excluded") == 0)
+ val = EMPTY_EXCLUDED;
+ else {
+ SYNTAX_ERROR("Empty action expects zero arguments, or one"
+ "argument containing \"all\", \"source\", or \"excluded\""
+ "\n");
+ return 0;
+ }
+
+ empty_data = malloc(sizeof(*empty_data));
+ if (empty_data == NULL)
+ MEM_ERROR();
+
+ empty_data->val = val;
+ *data = empty_data;
+
+ return 1;
+}
+
+
+int eval_empty_actions(struct dir_info *root, struct dir_ent *dir_ent)
+{
+ int i, match = 0;
+ struct action_data action_data;
+ struct empty_data *data;
+ struct dir_info *dir = dir_ent->dir;
+
+ /*
+ * Empty action only works on empty directories
+ */
+ if (dir->count != 0)
+ return 0;
+
+ action_data.name = dir_ent->name;
+ action_data.pathname = strdup(pathname(dir_ent));
+ action_data.subpath = strdup(subpathname(dir_ent));
+ action_data.buf = &dir_ent->inode->buf;
+ action_data.depth = dir_ent->our_dir->depth;
+ action_data.dir_ent = dir_ent;
+ action_data.root = root;
+
+ for (i = 0; i < empty_count && !match; i++) {
+ data = empty_spec[i].data;
+
+ /*
+ * determine the cause of the empty directory and evaluate
+ * the empty action specified. Three empty actions:
+ * - EMPTY_SOURCE: empty action triggers only if the directory
+ * was originally empty, i.e directories that are empty
+ * only due to excluding are ignored.
+ * - EMPTY_EXCLUDED: empty action triggers only if the directory
+ * is empty because of excluding, i.e. directories that
+ * were originally empty are ignored.
+ * - EMPTY_ALL (the default): empty action triggers if the
+ * directory is empty, irrespective of the reason, i.e.
+ * the directory could have been originally empty or could
+ * be empty due to excluding.
+ */
+ if ((data->val == EMPTY_EXCLUDED && !dir->excluded) ||
+ (data->val == EMPTY_SOURCE && dir->excluded))
+ continue;
+
+ match = eval_expr_top(&empty_spec[i], &action_data);
+ }
+
+ free(action_data.pathname);
+ free(action_data.subpath);
+
+ return match;
+}
+
+
+/*
+ * Move specific action code
+ */
+static struct move_ent *move_list = NULL;
+
+
+int move_actions()
+{
+ return move_count;
+}
+
+
+static char *move_pathname(struct move_ent *move)
+{
+ struct dir_info *dest;
+ char *name, *pathname;
+ int res;
+
+ dest = (move->ops & ACTION_MOVE_MOVE) ?
+ move->dest : move->dir_ent->our_dir;
+ name = (move->ops & ACTION_MOVE_RENAME) ?
+ move->name : move->dir_ent->name;
+
+ if(dest->subpath[0] != '\0')
+ res = asprintf(&pathname, "%s/%s", dest->subpath, name);
+ else
+ res = asprintf(&pathname, "/%s", name);
+
+ if(res == -1)
+ BAD_ERROR("asprintf failed in move_pathname\n");
+
+ return pathname;
+}
+
+
+static char *get_comp(char **pathname)
+{
+ char *path = *pathname, *start;
+
+ while(*path == '/')
+ path ++;
+
+ if(*path == '\0')
+ return NULL;
+
+ start = path;
+ while(*path != '/' && *path != '\0')
+ path ++;
+
+ *pathname = path;
+ return strndup(start, path - start);
+}
+
+
+static struct dir_ent *lookup_comp(char *comp, struct dir_info *dest)
+{
+ struct dir_ent *dir_ent;
+
+ for(dir_ent = dest->list; dir_ent; dir_ent = dir_ent->next)
+ if(strcmp(comp, dir_ent->name) == 0)
+ break;
+
+ return dir_ent;
+}
+
+
+void eval_move(struct action_data *action_data, struct move_ent *move,
+ struct dir_info *root, struct dir_ent *dir_ent, char *pathname)
+{
+ struct dir_info *dest, *source = dir_ent->our_dir;
+ struct dir_ent *comp_ent;
+ char *comp, *path = pathname;
+
+ /*
+ * Walk pathname to get the destination directory
+ *
+ * Like the mv command, if the last component exists and it
+ * is a directory, then move the file into that directory,
+ * otherwise, move the file into parent directory of the last
+ * component and rename to the last component.
+ */
+ if (pathname[0] == '/')
+ /* absolute pathname, walk from root directory */
+ dest = root;
+ else
+ /* relative pathname, walk from current directory */
+ dest = source;
+
+ for(comp = get_comp(&pathname); comp; free(comp),
+ comp = get_comp(&pathname)) {
+
+ if (strcmp(comp, ".") == 0)
+ continue;
+
+ if (strcmp(comp, "..") == 0) {
+ /* if we're in the root directory then ignore */
+ if(dest->depth > 1)
+ dest = dest->dir_ent->our_dir;
+ continue;
+ }
+
+ /*
+ * Look up comp in current directory, if it exists and it is a
+ * directory continue walking the pathname, otherwise exit,
+ * we've walked as far as we can go, normally this is because
+ * we've arrived at the leaf component which we are going to
+ * rename source to
+ */
+ comp_ent = lookup_comp(comp, dest);
+ if (comp_ent == NULL || (comp_ent->inode->buf.st_mode & S_IFMT)
+ != S_IFDIR)
+ break;
+
+ dest = comp_ent->dir;
+ }
+
+ if(comp) {
+ /* Leaf component? If so we're renaming to this */
+ char *remainder = get_comp(&pathname);
+ free(remainder);
+
+ if(remainder) {
+ /*
+ * trying to move source to a subdirectory of
+ * comp, but comp either doesn't exist, or it isn't
+ * a directory, which is impossible
+ */
+ if (comp_ent == NULL)
+ ERROR("Move action: cannot move %s to %s, no "
+ "such directory %s\n",
+ action_data->subpath, path, comp);
+ else
+ ERROR("Move action: cannot move %s to %s, %s "
+ "is not a directory\n",
+ action_data->subpath, path, comp);
+ free(comp);
+ return;
+ }
+
+ /*
+ * Multiple move actions triggering on one file can be merged
+ * if one is a RENAME and the other is a MOVE. Multiple RENAMEs
+ * can only merge if they're doing the same thing
+ */
+ if(move->ops & ACTION_MOVE_RENAME) {
+ if(strcmp(comp, move->name) != 0) {
+ char *conf_path = move_pathname(move);
+ ERROR("Move action: Cannot move %s to %s, "
+ "conflicting move, already moving "
+ "to %s via another move action!\n",
+ action_data->subpath, path, conf_path);
+ free(conf_path);
+ free(comp);
+ return;
+ }
+ free(comp);
+ } else {
+ move->name = comp;
+ move->ops |= ACTION_MOVE_RENAME;
+ }
+ }
+
+ if(dest != source) {
+ /*
+ * Multiple move actions triggering on one file can be merged
+ * if one is a RENAME and the other is a MOVE. Multiple MOVEs
+ * can only merge if they're doing the same thing
+ */
+ if(move->ops & ACTION_MOVE_MOVE) {
+ if(dest != move->dest) {
+ char *conf_path = move_pathname(move);
+ ERROR("Move action: Cannot move %s to %s, "
+ "conflicting move, already moving "
+ "to %s via another move action!\n",
+ action_data->subpath, path, conf_path);
+ free(conf_path);
+ return;
+ }
+ } else {
+ move->dest = dest;
+ move->ops |= ACTION_MOVE_MOVE;
+ }
+ }
+}
+
+
+static int subdirectory(struct dir_info *source, struct dir_info *dest)
+{
+ if(source == NULL)
+ return 0;
+
+ return strlen(source->subpath) <= strlen(dest->subpath) &&
+ (dest->subpath[strlen(source->subpath)] == '/' ||
+ dest->subpath[strlen(source->subpath)] == '\0') &&
+ strncmp(source->subpath, dest->subpath,
+ strlen(source->subpath)) == 0;
+}
+
+
+void eval_move_actions(struct dir_info *root, struct dir_ent *dir_ent)
+{
+ int i;
+ struct action_data action_data;
+ struct move_ent *move = NULL;
+
+ action_data.name = dir_ent->name;
+ action_data.pathname = strdup(pathname(dir_ent));
+ action_data.subpath = strdup(subpathname(dir_ent));
+ action_data.buf = &dir_ent->inode->buf;
+ action_data.depth = dir_ent->our_dir->depth;
+ action_data.dir_ent = dir_ent;
+ action_data.root = root;
+
+ /*
+ * Evaluate each move action against the current file. For any
+ * move actions that match don't actually perform the move now, but,
+ * store it, and execute all the stored move actions together once the
+ * directory scan is complete. This is done to ensure each separate
+ * move action does not nondeterministically interfere with other move
+ * actions. Each move action is considered to act independently, and
+ * each move action sees the directory tree in the same state.
+ */
+ for (i = 0; i < move_count; i++) {
+ struct action *action = &move_spec[i];
+ int match = eval_expr_top(action, &action_data);
+
+ if(match) {
+ if(move == NULL) {
+ move = malloc(sizeof(*move));
+ if(move == NULL)
+ MEM_ERROR();
+
+ move->ops = 0;
+ move->dir_ent = dir_ent;
+ }
+ eval_move(&action_data, move, root, dir_ent,
+ action->argv[0]);
+ }
+ }
+
+ if(move) {
+ struct dir_ent *comp_ent;
+ struct dir_info *dest;
+ char *name;
+
+ /*
+ * Move contains the result of all triggered move actions.
+ * Check the destination doesn't already exist
+ */
+ if(move->ops == 0) {
+ free(move);
+ goto finish;
+ }
+
+ dest = (move->ops & ACTION_MOVE_MOVE) ?
+ move->dest : dir_ent->our_dir;
+ name = (move->ops & ACTION_MOVE_RENAME) ?
+ move->name : dir_ent->name;
+ comp_ent = lookup_comp(name, dest);
+ if(comp_ent) {
+ char *conf_path = move_pathname(move);
+ ERROR("Move action: Cannot move %s to %s, "
+ "destination already exists\n",
+ action_data.subpath, conf_path);
+ free(conf_path);
+ free(move);
+ goto finish;
+ }
+
+ /*
+ * If we're moving a directory, check we're not moving it to a
+ * subdirectory of itself
+ */
+ if(subdirectory(dir_ent->dir, dest)) {
+ char *conf_path = move_pathname(move);
+ ERROR("Move action: Cannot move %s to %s, this is a "
+ "subdirectory of itself\n",
+ action_data.subpath, conf_path);
+ free(conf_path);
+ free(move);
+ goto finish;
+ }
+ move->next = move_list;
+ move_list = move;
+ }
+
+finish:
+ free(action_data.pathname);
+ free(action_data.subpath);
+}
+
+
+static void move_dir(struct dir_ent *dir_ent)
+{
+ struct dir_info *dir = dir_ent->dir;
+ struct dir_ent *comp_ent;
+
+ /* update our directory's subpath name */
+ free(dir->subpath);
+ dir->subpath = strdup(subpathname(dir_ent));
+
+ /* recursively update the subpaths of any sub-directories */
+ for(comp_ent = dir->list; comp_ent; comp_ent = comp_ent->next)
+ if(comp_ent->dir)
+ move_dir(comp_ent);
+}
+
+
+static void move_file(struct move_ent *move_ent)
+{
+ struct dir_ent *dir_ent = move_ent->dir_ent;
+
+ if(move_ent->ops & ACTION_MOVE_MOVE) {
+ struct dir_ent *comp_ent, *prev = NULL;
+ struct dir_info *source = dir_ent->our_dir,
+ *dest = move_ent->dest;
+ char *filename = pathname(dir_ent);
+
+ /*
+ * If we're moving a directory, check we're not moving it to a
+ * subdirectory of itself
+ */
+ if(subdirectory(dir_ent->dir, dest)) {
+ char *conf_path = move_pathname(move_ent);
+ ERROR("Move action: Cannot move %s to %s, this is a "
+ "subdirectory of itself\n",
+ subpathname(dir_ent), conf_path);
+ free(conf_path);
+ return;
+ }
+
+ /* Remove the file from source directory */
+ for(comp_ent = source->list; comp_ent != dir_ent;
+ prev = comp_ent, comp_ent = comp_ent->next);
+
+ if(prev)
+ prev->next = comp_ent->next;
+ else
+ source->list = comp_ent->next;
+
+ source->count --;
+ if((comp_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
+ source->directory_count --;
+
+ /* Add the file to dest directory */
+ comp_ent->next = dest->list;
+ dest->list = comp_ent;
+ comp_ent->our_dir = dest;
+
+ dest->count ++;
+ if((comp_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
+ dest->directory_count ++;
+
+ /*
+ * We've moved the file, and so we can't now use the
+ * parent directory's pathname to calculate the pathname
+ */
+ if(dir_ent->nonstandard_pathname == NULL) {
+ dir_ent->nonstandard_pathname = strdup(filename);
+ if(dir_ent->source_name) {
+ free(dir_ent->source_name);
+ dir_ent->source_name = NULL;
+ }
+ }
+ }
+
+ if(move_ent->ops & ACTION_MOVE_RENAME) {
+ /*
+ * If we're using name in conjunction with the parent
+ * directory's pathname to calculate the pathname, we need
+ * to use source_name to override. Otherwise it's already being
+ * over-ridden
+ */
+ if(dir_ent->nonstandard_pathname == NULL &&
+ dir_ent->source_name == NULL)
+ dir_ent->source_name = dir_ent->name;
+ else
+ free(dir_ent->name);
+
+ dir_ent->name = move_ent->name;
+ }
+
+ if(dir_ent->dir)
+ /*
+ * dir_ent is a directory, and we have to recursively fix-up
+ * its subpath, and the subpaths of all of its sub-directories
+ */
+ move_dir(dir_ent);
+}
+
+
+void do_move_actions()
+{
+ while(move_list) {
+ struct move_ent *temp = move_list;
+ struct dir_info *dest = (move_list->ops & ACTION_MOVE_MOVE) ?
+ move_list->dest : move_list->dir_ent->our_dir;
+ char *name = (move_list->ops & ACTION_MOVE_RENAME) ?
+ move_list->name : move_list->dir_ent->name;
+ struct dir_ent *comp_ent = lookup_comp(name, dest);
+ if(comp_ent) {
+ char *conf_path = move_pathname(move_list);
+ ERROR("Move action: Cannot move %s to %s, "
+ "destination already exists\n",
+ subpathname(move_list->dir_ent), conf_path);
+ free(conf_path);
+ } else
+ move_file(move_list);
+
+ move_list = move_list->next;
+ free(temp);
+ }
+}
+
+
+/*
+ * Prune specific action code
+ */
+int prune_actions()
+{
+ return prune_count;
+}
+
+
+int eval_prune_actions(struct dir_info *root, struct dir_ent *dir_ent)
+{
+ int i, match = 0;
+ struct action_data action_data;
+
+ action_data.name = dir_ent->name;
+ action_data.pathname = strdup(pathname(dir_ent));
+ action_data.subpath = strdup(subpathname(dir_ent));
+ action_data.buf = &dir_ent->inode->buf;
+ action_data.depth = dir_ent->our_dir->depth;
+ action_data.dir_ent = dir_ent;
+ action_data.root = root;
+
+ for (i = 0; i < prune_count && !match; i++)
+ match = eval_expr_top(&prune_spec[i], &action_data);
+
+ free(action_data.pathname);
+ free(action_data.subpath);
+
+ return match;
+}
+
+
+/*
+ * Noop specific action code
+ */
+static void noop_action(struct action *action, struct dir_ent *dir_ent)
+{
+}
+
+
+/*
+ * General test evaluation code
+ */
+
+/*
+ * A number can be of the form [range]number[size]
+ * [range] is either:
+ * '<' or '-', match on less than number
+ * '>' or '+', match on greater than number
+ * '' (nothing), match on exactly number
+ * [size] is either:
+ * '' (nothing), number
+ * 'k' or 'K', number * 2^10
+ * 'm' or 'M', number * 2^20
+ * 'g' or 'G', number * 2^30
+ */
+static int parse_number(char *start, long long *size, int *range, char **error)
+{
+ char *end;
+ long long number;
+
+ if (*start == '>' || *start == '+') {
+ *range = NUM_GREATER;
+ start ++;
+ } else if (*start == '<' || *start == '-') {
+ *range = NUM_LESS;
+ start ++;
+ } else
+ *range = NUM_EQ;
+
+ errno = 0; /* To enable failure after call to be determined */
+ number = strtoll(start, &end, 10);
+
+ if((errno == ERANGE && (number == LLONG_MAX || number == LLONG_MIN))
+ || (errno != 0 && number == 0)) {
+ /* long long underflow or overflow in conversion, or other
+ * conversion error.
+ * Note: we don't check for LLONG_MIN and LLONG_MAX only
+ * because strtoll can validly return that if the
+ * user used these values
+ */
+ *error = "Long long underflow, overflow or other conversion "
+ "error";
+ return 0;
+ }
+
+ if (end == start) {
+ /* Couldn't read any number */
+ *error = "Number expected";
+ return 0;
+ }
+
+ switch (end[0]) {
+ case 'g':
+ case 'G':
+ number *= 1024;
+ case 'm':
+ case 'M':
+ number *= 1024;
+ case 'k':
+ case 'K':
+ number *= 1024;
+
+ if (end[1] != '\0') {
+ *error = "Trailing junk after size specifier";
+ return 0;
+ }
+
+ break;
+ case '\0':
+ break;
+ default:
+ *error = "Trailing junk after number";
+ return 0;
+ }
+
+ *size = number;
+
+ return 1;
+}
+
+
+static int parse_number_arg(struct test_entry *test, struct atom *atom)
+{
+ struct test_number_arg *number;
+ long long size;
+ int range;
+ char *error;
+ int res = parse_number(atom->argv[0], &size, &range, &error);
+
+ if (res == 0) {
+ TEST_SYNTAX_ERROR(test, 0, "%s\n", error);
+ return 0;
+ }
+
+ number = malloc(sizeof(*number));
+ if (number == NULL)
+ MEM_ERROR();
+
+ number->range = range;
+ number->size = size;
+
+ atom->data = number;
+
+ return 1;
+}
+
+
+static int parse_range_args(struct test_entry *test, struct atom *atom)
+{
+ struct test_range_args *range;
+ long long start, end;
+ int type;
+ int res;
+ char *error;
+
+ res = parse_number(atom->argv[0], &start, &type, &error);
+ if (res == 0) {
+ TEST_SYNTAX_ERROR(test, 0, "%s\n", error);
+ return 0;
+ }
+
+ if (type != NUM_EQ) {
+ TEST_SYNTAX_ERROR(test, 0, "Range specifier (<, >, -, +) not "
+ "expected\n");
+ return 0;
+ }
+
+ res = parse_number(atom->argv[1], &end, &type, &error);
+ if (res == 0) {
+ TEST_SYNTAX_ERROR(test, 1, "%s\n", error);
+ return 0;
+ }
+
+ if (type != NUM_EQ) {
+ TEST_SYNTAX_ERROR(test, 1, "Range specifier (<, >, -, +) not "
+ "expected\n");
+ return 0;
+ }
+
+ range = malloc(sizeof(*range));
+ if (range == NULL)
+ MEM_ERROR();
+
+ range->start = start;
+ range->end = end;
+
+ atom->data = range;
+
+ return 1;
+}
+
+
+/*
+ * Generic test code macro
+ */
+#define TEST_FN(NAME, MATCH, CODE) \
+static int NAME##_fn(struct atom *atom, struct action_data *action_data) \
+{ \
+ /* test operates on MATCH file types only */ \
+ if (!file_type_match(action_data->buf->st_mode, MATCH)) \
+ return 0; \
+ \
+ CODE \
+}
+
+/*
+ * Generic test code macro testing VAR for size (eq, less than, greater than)
+ */
+#define TEST_VAR_FN(NAME, MATCH, VAR) TEST_FN(NAME, MATCH, \
+ { \
+ int match = 0; \
+ struct test_number_arg *number = atom->data; \
+ \
+ switch (number->range) { \
+ case NUM_EQ: \
+ match = VAR == number->size; \
+ break; \
+ case NUM_LESS: \
+ match = VAR < number->size; \
+ break; \
+ case NUM_GREATER: \
+ match = VAR > number->size; \
+ break; \
+ } \
+ \
+ return match; \
+ })
+
+
+/*
+ * Generic test code macro testing VAR for range [x, y] (value between x and y
+ * inclusive).
+ */
+#define TEST_VAR_RANGE_FN(NAME, MATCH, VAR) TEST_FN(NAME##_range, MATCH, \
+ { \
+ struct test_range_args *range = atom->data; \
+ \
+ return range->start <= VAR && VAR <= range->end; \
+ })
+
+
+/*
+ * Name, Pathname and Subpathname test specific code
+ */
+
+/*
+ * Add a leading "/" if subpathname and pathname lacks it
+ */
+static int check_pathname(struct test_entry *test, struct atom *atom)
+{
+ int res;
+ char *name;
+
+ if(atom->argv[0][0] != '/') {
+ res = asprintf(&name, "/%s", atom->argv[0]);
+ if(res == -1)
+ BAD_ERROR("asprintf failed in check_pathname\n");
+
+ free(atom->argv[0]);
+ atom->argv[0] = name;
+ }
+
+ return 1;
+}
+
+
+TEST_FN(name, ACTION_ALL_LNK, \
+ return fnmatch(atom->argv[0], action_data->name,
+ FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;)
+
+TEST_FN(pathname, ACTION_ALL_LNK, \
+ return fnmatch(atom->argv[0], action_data->subpath,
+ FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;)
+
+
+static int count_components(char *path)
+{
+ int count;
+
+ for (count = 0; *path != '\0'; count ++) {
+ while (*path == '/')
+ path ++;
+
+ while (*path != '\0' && *path != '/')
+ path ++;
+ }
+
+ return count;
+}
+
+
+static char *get_start(char *s, int n)
+{
+ int count;
+ char *path = s;
+
+ for (count = 0; *path != '\0' && count < n; count ++) {
+ while (*path == '/')
+ path ++;
+
+ while (*path != '\0' && *path != '/')
+ path ++;
+ }
+
+ if (count == n)
+ *path = '\0';
+
+ return s;
+}
+
+
+static int subpathname_fn(struct atom *atom, struct action_data *action_data)
+{
+ char *path = strdup(action_data->subpath);
+ int is_match = fnmatch(atom->argv[0], get_start(path,
+ count_components(atom->argv[0])),
+ FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;
+ free(path);
+ return is_match;
+}
+
+/*
+ * Inode attribute test operations using generic
+ * TEST_VAR_FN(test name, file scope, attribute name) macro.
+ * This is for tests that do not need to be specially handled in any way.
+ * They just take a variable and compare it against a number.
+ */
+TEST_VAR_FN(filesize, ACTION_REG, action_data->buf->st_size)
+
+TEST_VAR_FN(dirsize, ACTION_DIR, action_data->buf->st_size)
+
+TEST_VAR_FN(size, ACTION_ALL_LNK, action_data->buf->st_size)
+
+TEST_VAR_FN(inode, ACTION_ALL_LNK, action_data->buf->st_ino)
+
+TEST_VAR_FN(nlink, ACTION_ALL_LNK, action_data->buf->st_nlink)
+
+TEST_VAR_FN(fileblocks, ACTION_REG, action_data->buf->st_blocks)
+
+TEST_VAR_FN(dirblocks, ACTION_DIR, action_data->buf->st_blocks)
+
+TEST_VAR_FN(blocks, ACTION_ALL_LNK, action_data->buf->st_blocks)
+
+TEST_VAR_FN(dircount, ACTION_DIR, action_data->dir_ent->dir->count)
+
+TEST_VAR_FN(depth, ACTION_ALL_LNK, action_data->depth)
+
+TEST_VAR_RANGE_FN(filesize, ACTION_REG, action_data->buf->st_size)
+
+TEST_VAR_RANGE_FN(dirsize, ACTION_DIR, action_data->buf->st_size)
+
+TEST_VAR_RANGE_FN(size, ACTION_ALL_LNK, action_data->buf->st_size)
+
+TEST_VAR_RANGE_FN(inode, ACTION_ALL_LNK, action_data->buf->st_ino)
+
+TEST_VAR_RANGE_FN(nlink, ACTION_ALL_LNK, action_data->buf->st_nlink)
+
+TEST_VAR_RANGE_FN(fileblocks, ACTION_REG, action_data->buf->st_blocks)
+
+TEST_VAR_RANGE_FN(dirblocks, ACTION_DIR, action_data->buf->st_blocks)
+
+TEST_VAR_RANGE_FN(blocks, ACTION_ALL_LNK, action_data->buf->st_blocks)
+
+TEST_VAR_RANGE_FN(gid, ACTION_ALL_LNK, action_data->buf->st_gid)
+
+TEST_VAR_RANGE_FN(uid, ACTION_ALL_LNK, action_data->buf->st_uid)
+
+TEST_VAR_RANGE_FN(depth, ACTION_ALL_LNK, action_data->depth)
+
+TEST_VAR_RANGE_FN(dircount, ACTION_DIR, action_data->dir_ent->dir->count)
+
+/*
+ * uid specific test code
+ */
+TEST_VAR_FN(uid, ACTION_ALL_LNK, action_data->buf->st_uid)
+
+static int parse_uid_arg(struct test_entry *test, struct atom *atom)
+{
+ struct test_number_arg *number;
+ long long size;
+ int range;
+ char *error;
+
+ if(parse_number(atom->argv[0], &size, &range, &error)) {
+ /* managed to fully parse argument as a number */
+ if(size < 0 || size > (((long long) 1 << 32) - 1)) {
+ TEST_SYNTAX_ERROR(test, 1, "Numeric uid out of "
+ "range\n");
+ return 0;
+ }
+ } else {
+ /* couldn't parse (fully) as a number, is it a user name? */
+ struct passwd *uid = getpwnam(atom->argv[0]);
+ if(uid) {
+ size = uid->pw_uid;
+ range = NUM_EQ;
+ } else {
+ TEST_SYNTAX_ERROR(test, 1, "Invalid uid or unknown "
+ "user\n");
+ return 0;
+ }
+ }
+
+ number = malloc(sizeof(*number));
+ if(number == NULL)
+ MEM_ERROR();
+
+ number->range = range;
+ number->size= size;
+
+ atom->data = number;
+
+ return 1;
+}
+
+
+/*
+ * gid specific test code
+ */
+TEST_VAR_FN(gid, ACTION_ALL_LNK, action_data->buf->st_gid)
+
+static int parse_gid_arg(struct test_entry *test, struct atom *atom)
+{
+ struct test_number_arg *number;
+ long long size;
+ int range;
+ char *error;
+
+ if(parse_number(atom->argv[0], &size, &range, &error)) {
+ /* managed to fully parse argument as a number */
+ if(size < 0 || size > (((long long) 1 << 32) - 1)) {
+ TEST_SYNTAX_ERROR(test, 1, "Numeric gid out of "
+ "range\n");
+ return 0;
+ }
+ } else {
+ /* couldn't parse (fully) as a number, is it a group name? */
+ struct group *gid = getgrnam(atom->argv[0]);
+ if(gid) {
+ size = gid->gr_gid;
+ range = NUM_EQ;
+ } else {
+ TEST_SYNTAX_ERROR(test, 1, "Invalid gid or unknown "
+ "group\n");
+ return 0;
+ }
+ }
+
+ number = malloc(sizeof(*number));
+ if(number == NULL)
+ MEM_ERROR();
+
+ number->range = range;
+ number->size= size;
+
+ atom->data = number;
+
+ return 1;
+}
+
+
+/*
+ * Type test specific code
+ */
+struct type_entry type_table[] = {
+ { S_IFSOCK, 's' },
+ { S_IFLNK, 'l' },
+ { S_IFREG, 'f' },
+ { S_IFBLK, 'b' },
+ { S_IFDIR, 'd' },
+ { S_IFCHR, 'c' },
+ { S_IFIFO, 'p' },
+ { 0, 0 },
+};
+
+
+static int parse_type_arg(struct test_entry *test, struct atom *atom)
+{
+ int i;
+
+ if (strlen(atom->argv[0]) != 1)
+ goto failed;
+
+ for(i = 0; type_table[i].type != 0; i++)
+ if (type_table[i].type == atom->argv[0][0])
+ break;
+
+ atom->data = &type_table[i];
+
+ if(type_table[i].type != 0)
+ return 1;
+
+failed:
+ TEST_SYNTAX_ERROR(test, 0, "Unexpected file type, expected 'f', 'd', "
+ "'c', 'b', 'l', 's' or 'p'\n");
+ return 0;
+}
+
+
+static int type_fn(struct atom *atom, struct action_data *action_data)
+{
+ struct type_entry *type = atom->data;
+
+ return (action_data->buf->st_mode & S_IFMT) == type->value;
+}
+
+
+/*
+ * True test specific code
+ */
+static int true_fn(struct atom *atom, struct action_data *action_data)
+{
+ return 1;
+}
+
+
+/*
+ * False test specific code
+ */
+static int false_fn(struct atom *atom, struct action_data *action_data)
+{
+ return 0;
+}
+
+
+/*
+ * File test specific code
+ */
+static int parse_file_arg(struct test_entry *test, struct atom *atom)
+{
+ int res;
+ regex_t *preg = malloc(sizeof(regex_t));
+
+ if (preg == NULL)
+ MEM_ERROR();
+
+ res = regcomp(preg, atom->argv[0], REG_EXTENDED);
+ if (res) {
+ char str[1024]; /* overflow safe */
+
+ regerror(res, preg, str, 1024);
+ free(preg);
+ TEST_SYNTAX_ERROR(test, 0, "invalid regex \"%s\" because "
+ "\"%s\"\n", atom->argv[0], str);
+ return 0;
+ }
+
+ atom->data = preg;
+
+ return 1;
+}
+
+
+static int file_fn(struct atom *atom, struct action_data *action_data)
+{
+ int child, res, size = 0, status;
+ int pipefd[2];
+ char *buffer = NULL;
+ regex_t *preg = atom->data;
+
+ res = pipe(pipefd);
+ if (res == -1)
+ BAD_ERROR("file_fn pipe failed\n");
+
+ child = fork();
+ if (child == -1)
+ BAD_ERROR("file_fn fork_failed\n");
+
+ if (child == 0) {
+ /*
+ * Child process
+ * Connect stdout to pipefd[1] and execute file command
+ */
+ close(STDOUT_FILENO);
+ res = dup(pipefd[1]);
+ if (res == -1)
+ exit(EXIT_FAILURE);
+
+ execlp("file", "file", "-b", action_data->pathname,
+ (char *) NULL);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Parent process. Read stdout from file command
+ */
+ close(pipefd[1]);
+
+ do {
+ buffer = realloc(buffer, size + 512);
+ if (buffer == NULL)
+ MEM_ERROR();
+
+ res = read_bytes(pipefd[0], buffer + size, 512);
+
+ if (res == -1)
+ BAD_ERROR("file_fn pipe read error\n");
+
+ size += 512;
+
+ } while (res == 512);
+
+ size = size + res - 512;
+
+ buffer[size] = '\0';
+
+ res = waitpid(child, &status, 0);
+
+ if (res == -1)
+ BAD_ERROR("file_fn waitpid failed\n");
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ BAD_ERROR("file_fn file returned error\n");
+
+ close(pipefd[0]);
+
+ res = regexec(preg, buffer, (size_t) 0, NULL, 0);
+
+ free(buffer);
+
+ return res == 0;
+}
+
+
+/*
+ * Exec test specific code
+ */
+static int exec_fn(struct atom *atom, struct action_data *action_data)
+{
+ int child, i, res, status;
+
+ child = fork();
+ if (child == -1)
+ BAD_ERROR("exec_fn fork_failed\n");
+
+ if (child == 0) {
+ /*
+ * Child process
+ * redirect stdin, stdout & stderr to /dev/null and
+ * execute atom->argv[0]
+ */
+ int fd = open("/dev/null", O_RDWR);
+ if(fd == -1)
+ exit(EXIT_FAILURE);
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ for(i = 0; i < 3; i++) {
+ res = dup(fd);
+ if (res == -1)
+ exit(EXIT_FAILURE);
+ }
+ close(fd);
+
+ /*
+ * Create environment variables
+ * NAME: name of file
+ * PATHNAME: pathname of file relative to squashfs root
+ * SOURCE_PATHNAME: the pathname of the file in the source
+ * directory
+ */
+ res = setenv("NAME", action_data->name, 1);
+ if(res == -1)
+ exit(EXIT_FAILURE);
+
+ res = setenv("PATHNAME", action_data->subpath, 1);
+ if(res == -1)
+ exit(EXIT_FAILURE);
+
+ res = setenv("SOURCE_PATHNAME", action_data->pathname, 1);
+ if(res == -1)
+ exit(EXIT_FAILURE);
+
+ execl("/bin/sh", "sh", "-c", atom->argv[0], (char *) NULL);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Parent process.
+ */
+
+ res = waitpid(child, &status, 0);
+
+ if (res == -1)
+ BAD_ERROR("exec_fn waitpid failed\n");
+
+ return WIFEXITED(status) ? WEXITSTATUS(status) == 0 : 0;
+}
+
+
+/*
+ * Symbolic link specific test code
+ */
+
+/*
+ * Walk the supplied pathname and return the directory entry corresponding
+ * to the pathname. If any symlinks are encountered whilst walking the
+ * pathname, then recursively walk these, to obtain the fully
+ * dereferenced canonicalised directory entry.
+ *
+ * If follow_path fails to walk a pathname either because a component
+ * doesn't exist, it is a non directory component when a directory
+ * component is expected, a symlink with an absolute path is encountered,
+ * or a symlink is encountered which cannot be recursively walked due to
+ * the above failures, then return NULL.
+ */
+static struct dir_ent *follow_path(struct dir_info *dir, char *pathname)
+{
+ char *comp, *path = pathname;
+ struct dir_ent *dir_ent = NULL;
+
+ /* We cannot follow absolute paths */
+ if(pathname[0] == '/')
+ return NULL;
+
+ for(comp = get_comp(&path); comp; free(comp), comp = get_comp(&path)) {
+ if(strcmp(comp, ".") == 0)
+ continue;
+
+ if(strcmp(comp, "..") == 0) {
+ /* Move to parent if we're not in the root directory */
+ if(dir->depth > 1) {
+ dir = dir->dir_ent->our_dir;
+ dir_ent = NULL; /* lazily eval at loop exit */
+ continue;
+ } else
+ /* Failed to walk pathname */
+ return NULL;
+ }
+
+ /* Lookup comp in current directory */
+ dir_ent = lookup_comp(comp, dir);
+ if(dir_ent == NULL)
+ /* Doesn't exist, failed to walk pathname */
+ return NULL;
+
+ if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFLNK) {
+ /* Symbolic link, try to walk it */
+ dir_ent = follow_path(dir, dir_ent->inode->symlink);
+ if(dir_ent == NULL)
+ /* Failed to follow symlink */
+ return NULL;
+ }
+
+ if((dir_ent->inode->buf.st_mode & S_IFMT) != S_IFDIR)
+ /* Cannot walk further */
+ break;
+
+ dir = dir_ent->dir;
+ }
+
+ /* We will have exited the loop either because we've processed
+ * all the components, which means we've successfully walked the
+ * pathname, or because we've hit a non-directory, in which case
+ * it's success if this is the leaf component */
+ if(comp) {
+ free(comp);
+ comp = get_comp(&path);
+ free(comp);
+ if(comp != NULL)
+ /* Not a leaf component */
+ return NULL;
+ } else {
+ /* Fully walked pathname, dir_ent contains correct value unless
+ * we've walked to the parent ("..") in which case we need
+ * to resolve it here */
+ if(!dir_ent)
+ dir_ent = dir->dir_ent;
+ }
+
+ return dir_ent;
+}
+
+
+static int exists_fn(struct atom *atom, struct action_data *action_data)
+{
+ /*
+ * Test if a symlink exists within the output filesystem, that is,
+ * the symlink has a relative path, and the relative path refers
+ * to an entry within the output filesystem.
+ *
+ * This test function evaluates the path for symlinks - that is it
+ * follows any symlinks in the path (and any symlinks that it contains
+ * etc.), to discover the fully dereferenced canonicalised relative
+ * path.
+ *
+ * If any symlinks within the path do not exist or are absolute
+ * then the symlink is considered to not exist, as it cannot be
+ * fully dereferenced.
+ *
+ * exists operates on symlinks only, other files by definition
+ * exist
+ */
+ if (!file_type_match(action_data->buf->st_mode, ACTION_LNK))
+ return 1;
+
+ /* dereference the symlink, and return TRUE if it exists */
+ return follow_path(action_data->dir_ent->our_dir,
+ action_data->dir_ent->inode->symlink) ? 1 : 0;
+}
+
+
+static int absolute_fn(struct atom *atom, struct action_data *action_data)
+{
+ /*
+ * Test if a symlink has an absolute path, which by definition
+ * means the symbolic link may be broken (even if the absolute path
+ * does point into the filesystem being squashed, because the resultant
+ * filesystem can be mounted/unsquashed anywhere, it is unlikely the
+ * absolute path will still point to the right place). If you know that
+ * an absolute symlink will point to the right place then you don't need
+ * to use this function, and/or these symlinks can be excluded by
+ * use of other test operators.
+ *
+ * absolute operates on symlinks only, other files by definition
+ * don't have problems
+ */
+ if (!file_type_match(action_data->buf->st_mode, ACTION_LNK))
+ return 0;
+
+ return action_data->dir_ent->inode->symlink[0] == '/';
+}
+
+
+static int parse_expr_argX(struct test_entry *test, struct atom *atom,
+ int argno)
+{
+ /* Call parse_expr to parse argument, which should be an expression */
+
+ /* save the current parser state */
+ char *save_cur_ptr = cur_ptr;
+ char *save_source = source;
+
+ cur_ptr = source = atom->argv[argno];
+ atom->data = parse_expr(0);
+
+ cur_ptr = save_cur_ptr;
+ source = save_source;
+
+ if(atom->data == NULL) {
+ /* parse_expr(0) will have reported the exact syntax error,
+ * but, because we recursively evaluated the expression, it
+ * will have been reported without the context of the stat
+ * test(). So here additionally report our failure to parse
+ * the expression in the stat() test to give context */
+ TEST_SYNTAX_ERROR(test, 0, "Failed to parse expression\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int parse_expr_arg0(struct test_entry *test, struct atom *atom)
+{
+ return parse_expr_argX(test, atom, 0);
+}
+
+
+static int parse_expr_arg1(struct test_entry *test, struct atom *atom)
+{
+ return parse_expr_argX(test, atom, 1);
+}
+
+
+static int stat_fn(struct atom *atom, struct action_data *action_data)
+{
+ struct stat buf;
+ struct action_data eval_action;
+ int match, res;
+
+ /* evaluate the expression using the context of the inode
+ * pointed to by the symlink. This allows the inode attributes
+ * of the file pointed to by the symlink to be evaluated, rather
+ * than the symlink itself.
+ *
+ * Note, stat() deliberately does not evaluate the pathname, name or
+ * depth of the symlink, these are left with the symlink values.
+ * This allows stat() to be used on any symlink, rather than
+ * just symlinks which are contained (if the symlink is *not*
+ * contained then pathname, name and depth are meaningless as they
+ * are relative to the filesystem being squashed). */
+
+ /* if this isn't a symlink then stat will just return the current
+ * information, i.e. stat(expr) == expr. This is harmless and
+ * is better than returning TRUE or FALSE in a non symlink case */
+ res = stat(action_data->pathname, &buf);
+ if(res == -1) {
+ if(expr_log_cmnd(LOG_ENABLED)) {
+ expr_log(atom->test->name);
+ expr_log("(");
+ expr_log_match(0);
+ expr_log(")");
+ }
+ return 0;
+ }
+
+ /* fill in the inode values of the file pointed to by the
+ * symlink, but, leave everything else the same */
+ memcpy(&eval_action, action_data, sizeof(struct action_data));
+ eval_action.buf = &buf;
+
+ if(expr_log_cmnd(LOG_ENABLED)) {
+ expr_log(atom->test->name);
+ expr_log("(");
+ match = eval_expr_log(atom->data, &eval_action);
+ expr_log(")");
+ } else
+ match = eval_expr(atom->data, &eval_action);
+
+ return match;
+}
+
+
+static int readlink_fn(struct atom *atom, struct action_data *action_data)
+{
+ int match = 0;
+ struct dir_ent *dir_ent;
+ struct action_data eval_action;
+
+ /* Dereference the symlink and evaluate the expression in the
+ * context of the file pointed to by the symlink.
+ * All attributes are updated to refer to the file that is pointed to.
+ * Thus the inode attributes, pathname, name and depth all refer to
+ * the dereferenced file, and not the symlink.
+ *
+ * If the symlink cannot be dereferenced because it doesn't exist in
+ * the output filesystem, or due to some other failure to
+ * walk the pathname (see follow_path above), then FALSE is returned.
+ *
+ * If you wish to evaluate the inode attributes of symlinks which
+ * exist in the source filestem (but not in the output filesystem then
+ * use stat instead (see above).
+ *
+ * readlink operates on symlinks only */
+ if (!file_type_match(action_data->buf->st_mode, ACTION_LNK))
+ goto finish;
+
+ /* dereference the symlink, and get the directory entry it points to */
+ dir_ent = follow_path(action_data->dir_ent->our_dir,
+ action_data->dir_ent->inode->symlink);
+ if(dir_ent == NULL)
+ goto finish;
+
+ eval_action.name = dir_ent->name;
+ eval_action.pathname = strdup(pathname(dir_ent));
+ eval_action.subpath = strdup(subpathname(dir_ent));
+ eval_action.buf = &dir_ent->inode->buf;
+ eval_action.depth = dir_ent->our_dir->depth;
+ eval_action.dir_ent = dir_ent;
+ eval_action.root = action_data->root;
+
+ if(expr_log_cmnd(LOG_ENABLED)) {
+ expr_log(atom->test->name);
+ expr_log("(");
+ match = eval_expr_log(atom->data, &eval_action);
+ expr_log(")");
+ } else
+ match = eval_expr(atom->data, &eval_action);
+
+ free(eval_action.pathname);
+ free(eval_action.subpath);
+
+ return match;
+
+finish:
+ if(expr_log_cmnd(LOG_ENABLED)) {
+ expr_log(atom->test->name);
+ expr_log("(");
+ expr_log_match(0);
+ expr_log(")");
+ }
+
+ return 0;
+}
+
+
+static int eval_fn(struct atom *atom, struct action_data *action_data)
+{
+ int match;
+ char *path = atom->argv[0];
+ struct dir_ent *dir_ent = action_data->dir_ent;
+ struct stat *buf = action_data->buf;
+ struct action_data eval_action;
+
+ /* Follow path (arg1) and evaluate the expression (arg2)
+ * in the context of the file discovered. All attributes are updated
+ * to refer to the file that is pointed to.
+ *
+ * This test operation allows you to add additional context to the
+ * evaluation of the file being scanned, such as "if current file is
+ * XXX and the parent is YYY, then ..." Often times you need or
+ * want to test a combination of file status
+ *
+ * If the file referenced by the path does not exist in
+ * the output filesystem, or some other failure is experienced in
+ * walking the path (see follow_path above), then FALSE is returned.
+ *
+ * If you wish to evaluate the inode attributes of files which
+ * exist in the source filestem (but not in the output filesystem then
+ * use stat instead (see above). */
+
+ /* try to follow path, and get the directory entry it points to */
+ if(path[0] == '/') {
+ /* absolute, walk from root - first skip the leading / */
+ while(path[0] == '/')
+ path ++;
+ if(path[0] == '\0')
+ dir_ent = action_data->root->dir_ent;
+ else
+ dir_ent = follow_path(action_data->root, path);
+ } else {
+ /* relative, if first component is ".." walk from parent,
+ * otherwise walk from dir_ent.
+ * Note: this has to be handled here because follow_path
+ * will quite correctly refuse to execute ".." on anything
+ * which isn't a directory */
+ if(strncmp(path, "..", 2) == 0 && (path[2] == '\0' ||
+ path[2] == '/')) {
+ /* walk from parent */
+ path += 2;
+ while(path[0] == '/')
+ path ++;
+ if(path[0] == '\0')
+ dir_ent = dir_ent->our_dir->dir_ent;
+ else
+ dir_ent = follow_path(dir_ent->our_dir, path);
+ } else if(!file_type_match(buf->st_mode, ACTION_DIR))
+ dir_ent = NULL;
+ else
+ dir_ent = follow_path(dir_ent->dir, path);
+ }
+
+ if(dir_ent == NULL) {
+ if(expr_log_cmnd(LOG_ENABLED)) {
+ expr_log(atom->test->name);
+ expr_log("(");
+ expr_log(atom->argv[0]);
+ expr_log(",");
+ expr_log_match(0);
+ expr_log(")");
+ }
+
+ return 0;
+ }
+
+ eval_action.name = dir_ent->name;
+ eval_action.pathname = strdup(pathname(dir_ent));
+ eval_action.subpath = strdup(subpathname(dir_ent));
+ eval_action.buf = &dir_ent->inode->buf;
+ eval_action.depth = dir_ent->our_dir->depth;
+ eval_action.dir_ent = dir_ent;
+ eval_action.root = action_data->root;
+
+ if(expr_log_cmnd(LOG_ENABLED)) {
+ expr_log(atom->test->name);
+ expr_log("(");
+ expr_log(eval_action.subpath);
+ expr_log(",");
+ match = eval_expr_log(atom->data, &eval_action);
+ expr_log(")");
+ } else
+ match = eval_expr(atom->data, &eval_action);
+
+ free(eval_action.pathname);
+ free(eval_action.subpath);
+
+ return match;
+}
+
+
+/*
+ * Perm specific test code
+ */
+static int parse_perm_args(struct test_entry *test, struct atom *atom)
+{
+ int res = 1, mode, op, i;
+ char *arg;
+ struct mode_data *head = NULL, *cur = NULL;
+ struct perm_data *perm_data;
+
+ if(atom->args == 0) {
+ TEST_SYNTAX_ERROR(test, 0, "One or more arguments expected\n");
+ return 0;
+ }
+
+ switch(atom->argv[0][0]) {
+ case '-':
+ op = PERM_ALL;
+ arg = atom->argv[0] + 1;
+ break;
+ case '/':
+ op = PERM_ANY;
+ arg = atom->argv[0] + 1;
+ break;
+ default:
+ op = PERM_EXACT;
+ arg = atom->argv[0];
+ break;
+ }
+
+ /* try to parse as an octal number */
+ res = parse_octal_mode_args(atom->args, atom->argv, (void **) &head);
+ if(res == -1) {
+ /* parse as sym mode argument */
+ for(i = 0; i < atom->args && res; i++, arg = atom->argv[i])
+ res = parse_sym_mode_arg(arg, &head, &cur);
+ }
+
+ if (res == 0)
+ goto finish;
+
+ /*
+ * Evaluate the symbolic mode against a permission of 0000 octal
+ */
+ mode = mode_execute(head, 0);
+
+ perm_data = malloc(sizeof(struct perm_data));
+ if (perm_data == NULL)
+ MEM_ERROR();
+
+ perm_data->op = op;
+ perm_data->mode = mode;
+
+ atom->data = perm_data;
+
+finish:
+ while(head) {
+ struct mode_data *tmp = head;
+ head = head->next;
+ free(tmp);
+ }
+
+ return res;
+}
+
+
+static int perm_fn(struct atom *atom, struct action_data *action_data)
+{
+ struct perm_data *perm_data = atom->data;
+ struct stat *buf = action_data->buf;
+
+ switch(perm_data->op) {
+ case PERM_EXACT:
+ return (buf->st_mode & ~S_IFMT) == perm_data->mode;
+ case PERM_ALL:
+ return (buf->st_mode & perm_data->mode) == perm_data->mode;
+ case PERM_ANY:
+ default:
+ /*
+ * if no permission bits are set in perm_data->mode match
+ * on any file, this is to be consistent with find, which
+ * does this to be consistent with the behaviour of
+ * -perm -000
+ */
+ return perm_data->mode == 0 || (buf->st_mode & perm_data->mode);
+ }
+}
+
+
+#ifdef SQUASHFS_TRACE
+static void dump_parse_tree(struct expr *expr)
+{
+ int i;
+
+ if(expr->type == ATOM_TYPE) {
+ printf("%s", expr->atom.test->name);
+ if(expr->atom.args) {
+ printf("(");
+ for(i = 0; i < expr->atom.args; i++) {
+ printf("%s", expr->atom.argv[i]);
+ if (i + 1 < expr->atom.args)
+ printf(",");
+ }
+ printf(")");
+ }
+ } else if (expr->type == UNARY_TYPE) {
+ printf("%s", token_table[expr->unary_op.op].string);
+ dump_parse_tree(expr->unary_op.expr);
+ } else {
+ printf("(");
+ dump_parse_tree(expr->expr_op.lhs);
+ printf("%s", token_table[expr->expr_op.op].string);
+ dump_parse_tree(expr->expr_op.rhs);
+ printf(")");
+ }
+}
+
+
+void dump_action_list(struct action *spec_list, int spec_count)
+{
+ int i;
+
+ for (i = 0; i < spec_count; i++) {
+ printf("%s", spec_list[i].action->name);
+ if (spec_list[i].args) {
+ int n;
+
+ printf("(");
+ for (n = 0; n < spec_list[i].args; n++) {
+ printf("%s", spec_list[i].argv[n]);
+ if (n + 1 < spec_list[i].args)
+ printf(",");
+ }
+ printf(")");
+ }
+ printf("=");
+ dump_parse_tree(spec_list[i].expr);
+ printf("\n");
+ }
+}
+
+
+void dump_actions()
+{
+ dump_action_list(exclude_spec, exclude_count);
+ dump_action_list(fragment_spec, fragment_count);
+ dump_action_list(other_spec, other_count);
+ dump_action_list(move_spec, move_count);
+ dump_action_list(empty_spec, empty_count);
+}
+#else
+void dump_actions()
+{
+}
+#endif
+
+
+static struct test_entry test_table[] = {
+ { "name", 1, name_fn, NULL, 1},
+ { "pathname", 1, pathname_fn, check_pathname, 1, 0},
+ { "subpathname", 1, subpathname_fn, check_pathname, 1, 0},
+ { "filesize", 1, filesize_fn, parse_number_arg, 1, 0},
+ { "dirsize", 1, dirsize_fn, parse_number_arg, 1, 0},
+ { "size", 1, size_fn, parse_number_arg, 1, 0},
+ { "inode", 1, inode_fn, parse_number_arg, 1, 0},
+ { "nlink", 1, nlink_fn, parse_number_arg, 1, 0},
+ { "fileblocks", 1, fileblocks_fn, parse_number_arg, 1, 0},
+ { "dirblocks", 1, dirblocks_fn, parse_number_arg, 1, 0},
+ { "blocks", 1, blocks_fn, parse_number_arg, 1, 0},
+ { "gid", 1, gid_fn, parse_gid_arg, 1, 0},
+ { "uid", 1, uid_fn, parse_uid_arg, 1, 0},
+ { "depth", 1, depth_fn, parse_number_arg, 1, 0},
+ { "dircount", 1, dircount_fn, parse_number_arg, 0, 0},
+ { "filesize_range", 2, filesize_range_fn, parse_range_args, 1, 0},
+ { "dirsize_range", 2, dirsize_range_fn, parse_range_args, 1, 0},
+ { "size_range", 2, size_range_fn, parse_range_args, 1, 0},
+ { "inode_range", 2, inode_range_fn, parse_range_args, 1, 0},
+ { "nlink_range", 2, nlink_range_fn, parse_range_args, 1, 0},
+ { "fileblocks_range", 2, fileblocks_range_fn, parse_range_args, 1, 0},
+ { "dirblocks_range", 2, dirblocks_range_fn, parse_range_args, 1, 0},
+ { "blocks_range", 2, blocks_range_fn, parse_range_args, 1, 0},
+ { "gid_range", 2, gid_range_fn, parse_range_args, 1, 0},
+ { "uid_range", 2, uid_range_fn, parse_range_args, 1, 0},
+ { "depth_range", 2, depth_range_fn, parse_range_args, 1, 0},
+ { "dircount_range", 2, dircount_range_fn, parse_range_args, 0, 0},
+ { "type", 1, type_fn, parse_type_arg, 1, 0},
+ { "true", 0, true_fn, NULL, 1, 0},
+ { "false", 0, false_fn, NULL, 1, 0},
+ { "file", 1, file_fn, parse_file_arg, 1, 0},
+ { "exec", 1, exec_fn, NULL, 1, 0},
+ { "exists", 0, exists_fn, NULL, 0, 0},
+ { "absolute", 0, absolute_fn, NULL, 0, 0},
+ { "stat", 1, stat_fn, parse_expr_arg0, 1, 1},
+ { "readlink", 1, readlink_fn, parse_expr_arg0, 0, 1},
+ { "eval", 2, eval_fn, parse_expr_arg1, 0, 1},
+ { "perm", -2, perm_fn, parse_perm_args, 1, 0},
+ { "", -1 }
+};
+
+
+static struct action_entry action_table[] = {
+ { "fragment", FRAGMENT_ACTION, 1, ACTION_REG, NULL, NULL},
+ { "exclude", EXCLUDE_ACTION, 0, ACTION_ALL_LNK, NULL, NULL},
+ { "fragments", FRAGMENTS_ACTION, 0, ACTION_REG, NULL, frag_action},
+ { "no-fragments", NO_FRAGMENTS_ACTION, 0, ACTION_REG, NULL,
+ no_frag_action},
+ { "always-use-fragments", ALWAYS_FRAGS_ACTION, 0, ACTION_REG, NULL,
+ always_frag_action},
+ { "dont-always-use-fragments", NO_ALWAYS_FRAGS_ACTION, 0, ACTION_REG,
+ NULL, no_always_frag_action},
+ { "compressed", COMPRESSED_ACTION, 0, ACTION_REG, NULL, comp_action},
+ { "uncompressed", UNCOMPRESSED_ACTION, 0, ACTION_REG, NULL,
+ uncomp_action},
+ { "uid", UID_ACTION, 1, ACTION_ALL_LNK, parse_uid_args, uid_action},
+ { "gid", GID_ACTION, 1, ACTION_ALL_LNK, parse_gid_args, gid_action},
+ { "guid", GUID_ACTION, 2, ACTION_ALL_LNK, parse_guid_args, guid_action},
+ { "mode", MODE_ACTION, -2, ACTION_ALL, parse_mode_args, mode_action },
+ { "empty", EMPTY_ACTION, -2, ACTION_DIR, parse_empty_args, NULL},
+ { "move", MOVE_ACTION, 1, ACTION_ALL_LNK, NULL, NULL},
+ { "prune", PRUNE_ACTION, 0, ACTION_ALL_LNK, NULL, NULL},
+ { "chmod", MODE_ACTION, -2, ACTION_ALL, parse_mode_args, mode_action },
+ { "noop", NOOP_ACTION, 0, ACTION_ALL, NULL, noop_action },
+ { "", 0, -1, 0, NULL, NULL}
+};
diff --git a/squashfs-tools/squashfs-tools/action.h b/squashfs-tools/squashfs-tools/action.h
new file mode 100644
index 0000000..0a8de7c
--- /dev/null
+++ b/squashfs-tools/squashfs-tools/action.h
@@ -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;
+ };
+};
+