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; + }; +}; + +/* + * Test operation definitions + */ +#define NUM_EQ 1 +#define NUM_LESS 2 +#define NUM_GREATER 3 + +struct test_number_arg { + long long size; + int range; +}; + +struct test_range_args { + long long start; + long long end; +}; + +struct action; +struct action_data; + +struct test_entry { + char *name; + int args; + int (*fn)(struct atom *, struct action_data *); + int (*parse_args)(struct test_entry *, struct atom *); + int exclude_ok; + int handle_logging; +}; + + +/* + * Type test specific definitions + */ +struct type_entry { + int value; + char type; +}; + + +/* + * Action definitions + */ +#define FRAGMENT_ACTION 0 +#define EXCLUDE_ACTION 1 +#define FRAGMENTS_ACTION 2 +#define NO_FRAGMENTS_ACTION 3 +#define ALWAYS_FRAGS_ACTION 4 +#define NO_ALWAYS_FRAGS_ACTION 5 +#define COMPRESSED_ACTION 6 +#define UNCOMPRESSED_ACTION 7 +#define UID_ACTION 8 +#define GID_ACTION 9 +#define GUID_ACTION 10 +#define MODE_ACTION 11 +#define EMPTY_ACTION 12 +#define MOVE_ACTION 13 +#define PRUNE_ACTION 14 +#define NOOP_ACTION 15 + +/* + * Define what file types each action operates over + */ +#define ACTION_DIR 1 +#define ACTION_REG 2 +#define ACTION_ALL_LNK 3 +#define ACTION_ALL 4 +#define ACTION_LNK 5 + + +/* + * Action logging requested, specified by the various + * -action, -true-action, -false-action and -verbose-action + * options + */ +#define ACTION_LOG_NONE 0 +#define ACTION_LOG_TRUE 1 +#define ACTION_LOG_FALSE 2 +#define ACTION_LOG_VERBOSE ACTION_LOG_TRUE | ACTION_LOG_FALSE + +struct action_entry { + char *name; + int type; + int args; + int file_types; + int (*parse_args)(struct action_entry *, int, char **, void **); + void (*run_action)(struct action *, struct dir_ent *); +}; + + +struct action_data { + int depth; + char *name; + char *pathname; + char *subpath; + struct stat *buf; + struct dir_ent *dir_ent; + struct dir_info *root; +}; + + +struct action { + int type; + struct action_entry *action; + int args; + char **argv; + struct expr *expr; + void *data; + int verbose; +}; + + +/* + * Uid/gid action specific definitions + */ +struct uid_info { + uid_t uid; +}; + +struct gid_info { + gid_t gid; +}; + +struct guid_info { + uid_t uid; + gid_t gid; +}; + + +/* + * Mode action specific definitions + */ +#define ACTION_MODE_SET 0 +#define ACTION_MODE_ADD 1 +#define ACTION_MODE_REM 2 +#define ACTION_MODE_OCT 3 + +struct mode_data { + struct mode_data *next; + int operation; + int mode; + unsigned int mask; + char X; +}; + + +/* + * Empty action specific definitions + */ +#define EMPTY_ALL 0 +#define EMPTY_SOURCE 1 +#define EMPTY_EXCLUDED 2 + +struct empty_data { + int val; +}; + + +/* + * Move action specific definitions + */ +#define ACTION_MOVE_RENAME 1 +#define ACTION_MOVE_MOVE 2 + +struct move_ent { + int ops; + struct dir_ent *dir_ent; + char *name; + struct dir_info *dest; + struct move_ent *next; +}; + + +/* + * Perm test function specific definitions + */ +#define PERM_ALL 1 +#define PERM_ANY 2 +#define PERM_EXACT 3 + +struct perm_data { + int op; + int mode; +}; + + +/* + * External function definitions + */ +extern int parse_action(char *, int verbose); +extern void dump_actions(); +extern void *eval_frag_actions(struct dir_info *, struct dir_ent *); +extern void *get_frag_action(void *); +extern int eval_exclude_actions(char *, char *, char *, struct stat *, int, + struct dir_ent *); +extern void eval_actions(struct dir_info *, struct dir_ent *); +extern int eval_empty_actions(struct dir_info *, struct dir_ent *dir_ent); +extern void eval_move_actions(struct dir_info *, struct dir_ent *); +extern int eval_prune_actions(struct dir_info *, struct dir_ent *); +extern void do_move_actions(); +extern int read_bytes(int, void *, int); +extern int actions(); +extern int move_actions(); +extern int empty_actions(); +extern int read_action_file(char *, int); +extern int exclude_actions(); +extern int prune_actions(); +#endif
diff --git a/squashfs-tools/squashfs-tools/android.c b/squashfs-tools/squashfs-tools/android.c new file mode 100644 index 0000000..eeef2aa --- /dev/null +++ b/squashfs-tools/squashfs-tools/android.c
@@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This file is used to define the properties of the filesystem +** images generated by build tools (mkbootfs and mkyaffs2image) and +** by the device side of adb. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <selinux/label.h> +#include <selinux/selinux.h> + +#include "android.h" +#include "private/android_filesystem_config.h" +#include "private/canned_fs_config.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +void alloc_mounted_path(const char *mount_point, const char *subpath, char **mounted_path) { + *mounted_path = malloc(strlen(mount_point) + strlen(subpath) + 1); + if (*mounted_path == NULL) { + perror("Malloc Failure."); + exit(EXIT_FAILURE); + } + strcpy(*mounted_path, mount_point); + strcat(*mounted_path, subpath); +} + +void android_fs_config(fs_config_func_t fs_config_func, const char *path, struct stat *stat, + const char *target_out_path, uint64_t *capabilities) { + // filesystem_config does not preserve file type bits + mode_t stat_file_type_mask = stat->st_mode & S_IFMT; + if (fs_config_func) + fs_config_func(path, S_ISDIR(stat->st_mode), target_out_path, + &stat->st_uid, &stat->st_gid, &stat->st_mode, capabilities); + stat->st_mode |= stat_file_type_mask; +} + + +struct selabel_handle *get_sehnd(const char *context_file) { + struct selinux_opt seopts[] = { + { + .type = SELABEL_OPT_PATH, + .value = context_file + } + }; + struct selabel_handle *sehnd = + selabel_open(SELABEL_CTX_FILE, seopts, ARRAY_SIZE(seopts)); + + if (!sehnd) { + perror("Error running selabel_open."); + exit(EXIT_FAILURE); + } + return sehnd; +} + + +char *set_selabel(const char *path, unsigned int mode, struct selabel_handle *sehnd) { + char *secontext; + if (sehnd != NULL) { + int full_name_size = strlen(path) + 2; + char* full_name = (char*) malloc(full_name_size); + if (full_name == NULL) { + perror("Malloc Failure."); + exit(EXIT_FAILURE); + } + + full_name[0] = '/'; + strncpy(full_name + 1, path, full_name_size - 1); + + if (selabel_lookup(sehnd, &secontext, full_name, mode)) { + secontext = strdup("u:object_r:unlabeled:s0"); + } + + free(full_name); + return secontext; + } + perror("Selabel handle is NULL."); + exit(EXIT_FAILURE); +} + +struct vfs_cap_data set_caps(uint64_t capabilities) { + struct vfs_cap_data cap_data; + memset(&cap_data, 0, sizeof(cap_data)); + + if (capabilities == 0) + return cap_data; + + cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE; + cap_data.data[0].permitted = (uint32_t) capabilities; + cap_data.data[0].inheritable = 0; + cap_data.data[1].permitted = (uint32_t) (capabilities >> 32); + cap_data.data[1].inheritable = 0; + + return cap_data; +}
diff --git a/squashfs-tools/squashfs-tools/android.h b/squashfs-tools/squashfs-tools/android.h new file mode 100644 index 0000000..998f710 --- /dev/null +++ b/squashfs-tools/squashfs-tools/android.h
@@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_H_ +#define _ANDROID_H_ + +#include <stdint.h> +typedef void (*fs_config_func_t)(const char *path, int dir, const char *target_out_path, + unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities); + +void alloc_mounted_path(const char *mount_point, const char *subpath, char **mounted_path); +void android_fs_config(fs_config_func_t fs_config_func, const char *path, struct stat *stat, const char *target_out_path, uint64_t *capabilities); +struct selabel_handle *get_sehnd(const char *context_file); +char *set_selabel(const char *path, unsigned int mode, struct selabel_handle *sehnd); +struct vfs_cap_data set_caps(uint64_t capabilities); + +#endif
diff --git a/squashfs-tools/squashfs-tools/caches-queues-lists.c b/squashfs-tools/squashfs-tools/caches-queues-lists.c new file mode 100644 index 0000000..7ad54e8 --- /dev/null +++ b/squashfs-tools/squashfs-tools/caches-queues-lists.c
@@ -0,0 +1,644 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * caches-queues-lists.c + */ + +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "error.h" +#include "caches-queues-lists.h" + +extern int add_overflow(int, int); +extern int multiply_overflow(int, int); + +#define TRUE 1 +#define FALSE 0 + +struct queue *queue_init(int size) +{ + struct queue *queue = malloc(sizeof(struct queue)); + + if(queue == NULL) + MEM_ERROR(); + + if(add_overflow(size, 1) || + multiply_overflow(size + 1, sizeof(void *))) + BAD_ERROR("Size too large in queue_init\n"); + + queue->data = malloc(sizeof(void *) * (size + 1)); + if(queue->data == NULL) + MEM_ERROR(); + + queue->size = size + 1; + queue->readp = queue->writep = 0; + pthread_mutex_init(&queue->mutex, NULL); + pthread_cond_init(&queue->empty, NULL); + pthread_cond_init(&queue->full, NULL); + + return queue; +} + + +void queue_put(struct queue *queue, void *data) +{ + int nextp; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + while((nextp = (queue->writep + 1) % queue->size) == queue->readp) + pthread_cond_wait(&queue->full, &queue->mutex); + + queue->data[queue->writep] = data; + queue->writep = nextp; + pthread_cond_signal(&queue->empty); + pthread_cleanup_pop(1); +} + + +void *queue_get(struct queue *queue) +{ + void *data; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + while(queue->readp == queue->writep) + pthread_cond_wait(&queue->empty, &queue->mutex); + + data = queue->data[queue->readp]; + queue->readp = (queue->readp + 1) % queue->size; + pthread_cond_signal(&queue->full); + pthread_cleanup_pop(1); + + return data; +} + + +int queue_empty(struct queue *queue) +{ + int empty; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + empty = queue->readp == queue->writep; + + pthread_cleanup_pop(1); + + return empty; +} + + +void queue_flush(struct queue *queue) +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + queue->readp = queue->writep; + + pthread_cleanup_pop(1); +} + + +void dump_queue(struct queue *queue) +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + printf("\tMax size %d, size %d%s\n", queue->size - 1, + queue->readp <= queue->writep ? queue->writep - queue->readp : + queue->size - queue->readp + queue->writep, + queue->readp == queue->writep ? " (EMPTY)" : + ((queue->writep + 1) % queue->size) == queue->readp ? + " (FULL)" : ""); + + pthread_cleanup_pop(1); +} + + +/* define seq queue hash tables */ +#define CALCULATE_SEQ_HASH(N) CALCULATE_HASH(N) + +/* Called with the seq queue mutex held */ +INSERT_HASH_TABLE(seq, struct seq_queue, CALCULATE_SEQ_HASH, sequence, seq) + +/* Called with the cache mutex held */ +REMOVE_HASH_TABLE(seq, struct seq_queue, CALCULATE_SEQ_HASH, sequence, seq); + +static unsigned int sequence = 0; + + +struct seq_queue *seq_queue_init() +{ + struct seq_queue *queue = malloc(sizeof(struct seq_queue)); + if(queue == NULL) + MEM_ERROR(); + + memset(queue, 0, sizeof(struct seq_queue)); + + pthread_mutex_init(&queue->mutex, NULL); + pthread_cond_init(&queue->wait, NULL); + + return queue; +} + + +void seq_queue_put(struct seq_queue *queue, struct file_buffer *entry) +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + insert_seq_hash_table(queue, entry); + + if(entry->fragment) + queue->fragment_count ++; + else + queue->block_count ++; + + if(entry->sequence == sequence) + pthread_cond_signal(&queue->wait); + + pthread_cleanup_pop(1); +} + + +struct file_buffer *seq_queue_get(struct seq_queue *queue) +{ + /* + * Look-up buffer matching sequence in the queue, if found return + * it, otherwise wait until it arrives + */ + int hash = CALCULATE_SEQ_HASH(sequence); + struct file_buffer *entry; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + while(1) { + for(entry = queue->hash_table[hash]; entry; + entry = entry->seq_next) + if(entry->sequence == sequence) + break; + + if(entry) { + /* + * found the buffer in the queue, decrement the + * appropriate count, and remove from hash list + */ + if(entry->fragment) + queue->fragment_count --; + else + queue->block_count --; + + remove_seq_hash_table(queue, entry); + + sequence ++; + + break; + } + + /* entry not found, wait for it to arrive */ + pthread_cond_wait(&queue->wait, &queue->mutex); + } + + pthread_cleanup_pop(1); + + return entry; +} + + +void seq_queue_flush(struct seq_queue *queue) +{ + int i; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + for(i = 0; i < HASH_SIZE; i++) + queue->hash_table[i] = NULL; + + queue->fragment_count = queue->block_count = 0; + + pthread_cleanup_pop(1); +} + + +void dump_seq_queue(struct seq_queue *queue, int fragment_queue) +{ + int size; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex); + pthread_mutex_lock(&queue->mutex); + + size = fragment_queue ? queue->fragment_count : queue->block_count; + + printf("\tMax size unlimited, size %d%s\n", size, + size == 0 ? " (EMPTY)" : ""); + + pthread_cleanup_pop(1); +} + + +/* define cache hash tables */ +#define CALCULATE_CACHE_HASH(N) CALCULATE_HASH(llabs(N)) + +/* Called with the cache mutex held */ +INSERT_HASH_TABLE(cache, struct cache, CALCULATE_CACHE_HASH, index, hash) + +/* Called with the cache mutex held */ +REMOVE_HASH_TABLE(cache, struct cache, CALCULATE_CACHE_HASH, index, hash); + +/* define cache free list */ + +/* Called with the cache mutex held */ +INSERT_LIST(free, struct file_buffer) + +/* Called with the cache mutex held */ +REMOVE_LIST(free, struct file_buffer) + + +struct cache *cache_init(int buffer_size, int max_buffers, int noshrink_lookup, + int first_freelist) +{ + struct cache *cache = malloc(sizeof(struct cache)); + + if(cache == NULL) + MEM_ERROR(); + + cache->max_buffers = max_buffers; + cache->buffer_size = buffer_size; + cache->count = 0; + cache->used = 0; + cache->free_list = NULL; + + /* + * The cache will grow up to max_buffers in size in response to + * an increase in readhead/number of buffers in flight. But + * once the outstanding buffers gets returned, we can either elect + * to shrink the cache, or to put the freed blocks onto a free list. + * + * For the caches where we want to do lookup (fragment/writer), + * a don't shrink policy is best, for the reader cache it + * makes no sense to keep buffers around longer than necessary as + * we don't do any lookup on those blocks. + */ + cache->noshrink_lookup = noshrink_lookup; + + /* + * The default use freelist before growing cache policy behaves + * poorly with appending - with many duplicates the caches + * do not grow due to the fact that large queues of outstanding + * fragments/writer blocks do not occur, leading to small caches + * and un-uncessary performance loss to frequent cache + * replacement in the small caches. Therefore with appending + * change the policy to grow the caches before reusing blocks + * from the freelist + */ + cache->first_freelist = first_freelist; + + memset(cache->hash_table, 0, sizeof(struct file_buffer *) * 65536); + pthread_mutex_init(&cache->mutex, NULL); + pthread_cond_init(&cache->wait_for_free, NULL); + pthread_cond_init(&cache->wait_for_unlock, NULL); + + return cache; +} + + +struct file_buffer *cache_lookup(struct cache *cache, long long index) +{ + /* Lookup block in the cache, if found return with usage count + * incremented, if not found return NULL */ + int hash = CALCULATE_CACHE_HASH(index); + struct file_buffer *entry; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next) + if(entry->index == index) + break; + + if(entry) { + /* found the block in the cache, increment used count and + * if necessary remove from free list so it won't disappear + */ + if(entry->used == 0) { + remove_free_list(&cache->free_list, entry); + cache->used ++; + } + entry->used ++; + } + + pthread_cleanup_pop(1); + + return entry; +} + + +static struct file_buffer *cache_freelist(struct cache *cache) +{ + struct file_buffer *entry = cache->free_list; + + remove_free_list(&cache->free_list, entry); + + /* a block on the free_list is hashed */ + remove_cache_hash_table(cache, entry); + + cache->used ++; + return entry; +} + + +static struct file_buffer *cache_alloc(struct cache *cache) +{ + struct file_buffer *entry = malloc(sizeof(struct file_buffer) + + cache->buffer_size); + if(entry == NULL) + MEM_ERROR(); + + entry->cache = cache; + entry->free_prev = entry->free_next = NULL; + cache->count ++; + return entry; +} + + +static struct file_buffer *_cache_get(struct cache *cache, long long index, + int hash) +{ + /* Get a free block out of the cache indexed on index. */ + struct file_buffer *entry = NULL; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + while(1) { + if(cache->noshrink_lookup) { + /* first try to get a block from the free list */ + if(cache->first_freelist && cache->free_list) + entry = cache_freelist(cache); + else if(cache->count < cache->max_buffers) { + entry = cache_alloc(cache); + cache->used ++; + } else if(!cache->first_freelist && cache->free_list) + entry = cache_freelist(cache); + } else { /* shrinking non-lookup cache */ + if(cache->count < cache->max_buffers) { + entry = cache_alloc(cache); + if(cache->count > cache->max_count) + cache->max_count = cache->count; + } + } + + if(entry) + break; + + /* wait for a block */ + pthread_cond_wait(&cache->wait_for_free, &cache->mutex); + } + + /* initialise block and if hash is set insert into the hash table */ + entry->used = 1; + entry->locked = FALSE; + entry->wait_on_unlock = FALSE; + entry->error = FALSE; + if(hash) { + entry->index = index; + insert_cache_hash_table(cache, entry); + } + + pthread_cleanup_pop(1); + + return entry; +} + + +struct file_buffer *cache_get(struct cache *cache, long long index) +{ + return _cache_get(cache, index, 1); +} + + +struct file_buffer *cache_get_nohash(struct cache *cache) +{ + return _cache_get(cache, 0, 0); +} + + +void cache_hash(struct file_buffer *entry, long long index) +{ + struct cache *cache = entry->cache; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + entry->index = index; + insert_cache_hash_table(cache, entry); + + pthread_cleanup_pop(1); +} + + +void cache_block_put(struct file_buffer *entry) +{ + struct cache *cache; + + /* + * Finished with this cache entry, once the usage count reaches zero it + * can be reused. + * + * If noshrink_lookup is set, put the block onto the free list. + * As blocks remain accessible via the hash table they can be found + * getting a new lease of life before they are reused. + * + * if noshrink_lookup is not set then shrink the cache. + */ + + if(entry == NULL) + return; + + cache = entry->cache; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + entry->used --; + if(entry->used == 0) { + if(cache->noshrink_lookup) { + insert_free_list(&cache->free_list, entry); + cache->used --; + } else { + free(entry); + cache->count --; + } + + /* One or more threads may be waiting on this block */ + pthread_cond_signal(&cache->wait_for_free); + } + + pthread_cleanup_pop(1); +} + + +void dump_cache(struct cache *cache) +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + if(cache->noshrink_lookup) + printf("\tMax buffers %d, Current size %d, Used %d, %s\n", + cache->max_buffers, cache->count, cache->used, + cache->free_list ? "Free buffers" : "No free buffers"); + else + printf("\tMax buffers %d, Current size %d, Maximum historical " + "size %d\n", cache->max_buffers, cache->count, + cache->max_count); + + pthread_cleanup_pop(1); +} + + +struct file_buffer *cache_get_nowait(struct cache *cache, long long index) +{ + struct file_buffer *entry = NULL; + /* + * block doesn't exist, create it, but return it with the + * locked flag set, so nothing tries to use it while it doesn't + * contain data. + * + * If there's no space in the cache then return NULL. + */ + + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + /* first try to get a block from the free list */ + if(cache->first_freelist && cache->free_list) + entry = cache_freelist(cache); + else if(cache->count < cache->max_buffers) { + entry = cache_alloc(cache); + cache->used ++; + } else if(!cache->first_freelist && cache->free_list) + entry = cache_freelist(cache); + + if(entry) { + /* initialise block and insert into the hash table */ + entry->used = 1; + entry->locked = TRUE; + entry->wait_on_unlock = FALSE; + entry->error = FALSE; + entry->index = index; + insert_cache_hash_table(cache, entry); + } + + pthread_cleanup_pop(1); + + return entry; +} + + +struct file_buffer *cache_lookup_nowait(struct cache *cache, long long index, + char *locked) +{ + /* + * Lookup block in the cache, if found return it with the locked flag + * indicating whether it is currently locked. In both cases increment + * the used count. + * + * If it doesn't exist in the cache return NULL; + */ + int hash = CALCULATE_CACHE_HASH(index); + struct file_buffer *entry; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + /* first check if the entry already exists */ + for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next) + if(entry->index == index) + break; + + if(entry) { + if(entry->used == 0) { + remove_free_list(&cache->free_list, entry); + cache->used ++; + } + entry->used ++; + *locked = entry->locked; + } + + pthread_cleanup_pop(1); + + return entry; +} + + +void cache_wait_unlock(struct file_buffer *buffer) +{ + struct cache *cache = buffer->cache; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + while(buffer->locked) { + /* + * another thread is filling this in, wait until it + * becomes unlocked. Used has been incremented to ensure it + * doesn't get reused. By definition a block can't be + * locked and unused, and so we don't need to worry + * about it being on the freelist now, but, it may + * become unused when unlocked unless used is + * incremented + */ + buffer->wait_on_unlock = TRUE; + pthread_cond_wait(&cache->wait_for_unlock, &cache->mutex); + } + + pthread_cleanup_pop(1); +} + + +void cache_unlock(struct file_buffer *entry) +{ + struct cache *cache = entry->cache; + + /* + * Unlock this locked cache entry. If anything is waiting for this + * to become unlocked, wake it up. + */ + pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex); + pthread_mutex_lock(&cache->mutex); + + entry->locked = FALSE; + + if(entry->wait_on_unlock) { + entry->wait_on_unlock = FALSE; + pthread_cond_broadcast(&cache->wait_for_unlock); + } + + pthread_cleanup_pop(1); +}
diff --git a/squashfs-tools/squashfs-tools/caches-queues-lists.h b/squashfs-tools/squashfs-tools/caches-queues-lists.h new file mode 100644 index 0000000..c4dcf28 --- /dev/null +++ b/squashfs-tools/squashfs-tools/caches-queues-lists.h
@@ -0,0 +1,200 @@ +#ifndef CACHES_QUEUES_LISTS_H +#define CACHES_QUEUES_LISTS_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * caches-queues-lists.h + */ + +#define INSERT_LIST(NAME, TYPE) \ +void insert_##NAME##_list(TYPE **list, TYPE *entry) { \ + if(*list) { \ + entry->NAME##_next = *list; \ + entry->NAME##_prev = (*list)->NAME##_prev; \ + (*list)->NAME##_prev->NAME##_next = entry; \ + (*list)->NAME##_prev = entry; \ + } else { \ + *list = entry; \ + entry->NAME##_prev = entry->NAME##_next = entry; \ + } \ +} + + +#define REMOVE_LIST(NAME, TYPE) \ +void remove_##NAME##_list(TYPE **list, TYPE *entry) { \ + if(entry->NAME##_prev == entry && entry->NAME##_next == entry) { \ + /* only this entry in the list */ \ + *list = NULL; \ + } else if(entry->NAME##_prev != NULL && entry->NAME##_next != NULL) { \ + /* more than one entry in the list */ \ + entry->NAME##_next->NAME##_prev = entry->NAME##_prev; \ + entry->NAME##_prev->NAME##_next = entry->NAME##_next; \ + if(*list == entry) \ + *list = entry->NAME##_next; \ + } \ + entry->NAME##_prev = entry->NAME##_next = NULL; \ +} + + +#define INSERT_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \ +void insert_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \ +{ \ + int hash = HASH_FUNCTION(entry->FIELD); \ +\ + entry->LINK##_next = container->hash_table[hash]; \ + container->hash_table[hash] = entry; \ + entry->LINK##_prev = NULL; \ + if(entry->LINK##_next) \ + entry->LINK##_next->LINK##_prev = entry; \ +} + + +#define REMOVE_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \ +void remove_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \ +{ \ + if(entry->LINK##_prev) \ + entry->LINK##_prev->LINK##_next = entry->LINK##_next; \ + else \ + container->hash_table[HASH_FUNCTION(entry->FIELD)] = \ + entry->LINK##_next; \ + if(entry->LINK##_next) \ + entry->LINK##_next->LINK##_prev = entry->LINK##_prev; \ +\ + entry->LINK##_prev = entry->LINK##_next = NULL; \ +} + +#define HASH_SIZE 65536 +#define CALCULATE_HASH(n) ((n) & 0xffff) + + +/* struct describing a cache entry passed between threads */ +struct file_buffer { + union { + long long index; + long long sequence; + }; + long long file_size; + union { + long long block; + unsigned short checksum; + }; + struct cache *cache; + union { + struct file_info *dupl_start; + struct file_buffer *hash_next; + }; + union { + int duplicate; + struct file_buffer *hash_prev; + }; + union { + struct { + struct file_buffer *free_next; + struct file_buffer *free_prev; + }; + struct { + struct file_buffer *seq_next; + struct file_buffer *seq_prev; + }; + }; + int size; + int c_byte; + char used; + char fragment; + char error; + char locked; + char wait_on_unlock; + char noD; + char data[0]; +}; + + +/* struct describing queues used to pass data between threads */ +struct queue { + int size; + int readp; + int writep; + pthread_mutex_t mutex; + pthread_cond_t empty; + pthread_cond_t full; + void **data; +}; + + +/* + * struct describing seq_queues used to pass data between the read + * thread and the deflate and main threads + */ +struct seq_queue { + int fragment_count; + int block_count; + struct file_buffer *hash_table[HASH_SIZE]; + pthread_mutex_t mutex; + pthread_cond_t wait; +}; + + +/* Cache status struct. Caches are used to keep + track of memory buffers passed between different threads */ +struct cache { + int max_buffers; + int count; + int buffer_size; + int noshrink_lookup; + int first_freelist; + union { + int used; + int max_count; + }; + pthread_mutex_t mutex; + pthread_cond_t wait_for_free; + pthread_cond_t wait_for_unlock; + struct file_buffer *free_list; + struct file_buffer *hash_table[HASH_SIZE]; +}; + + +extern struct queue *queue_init(int); +extern void queue_put(struct queue *, void *); +extern void *queue_get(struct queue *); +extern int queue_empty(struct queue *); +extern void queue_flush(struct queue *); +extern void dump_queue(struct queue *); +extern struct seq_queue *seq_queue_init(); +extern void seq_queue_put(struct seq_queue *, struct file_buffer *); +extern void dump_seq_queue(struct seq_queue *, int); +extern struct file_buffer *seq_queue_get(struct seq_queue *); +extern void seq_queue_flush(struct seq_queue *); +extern struct cache *cache_init(int, int, int, int); +extern struct file_buffer *cache_lookup(struct cache *, long long); +extern struct file_buffer *cache_get(struct cache *, long long); +extern struct file_buffer *cache_get_nohash(struct cache *); +extern void cache_hash(struct file_buffer *, long long); +extern void cache_block_put(struct file_buffer *); +extern void dump_cache(struct cache *); +extern struct file_buffer *cache_get_nowait(struct cache *, long long); +extern struct file_buffer *cache_lookup_nowait(struct cache *, long long, + char *); +extern void cache_wait_unlock(struct file_buffer *); +extern void cache_unlock(struct file_buffer *); + +extern int first_freelist; +#endif
diff --git a/squashfs-tools/squashfs-tools/compressor.c b/squashfs-tools/squashfs-tools/compressor.c new file mode 100644 index 0000000..525e316 --- /dev/null +++ b/squashfs-tools/squashfs-tools/compressor.c
@@ -0,0 +1,137 @@ +/* + * + * Copyright (c) 2009, 2010, 2011 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * compressor.c + */ + +#include <stdio.h> +#include <string.h> +#include "compressor.h" +#include "squashfs_fs.h" + +#ifndef GZIP_SUPPORT +static struct compressor gzip_comp_ops = { + ZLIB_COMPRESSION, "gzip" +}; +#else +extern struct compressor gzip_comp_ops; +#endif + +#ifndef LZMA_SUPPORT +static struct compressor lzma_comp_ops = { + LZMA_COMPRESSION, "lzma" +}; +#else +extern struct compressor lzma_comp_ops; +#endif + +#ifndef LZO_SUPPORT +static struct compressor lzo_comp_ops = { + LZO_COMPRESSION, "lzo" +}; +#else +extern struct compressor lzo_comp_ops; +#endif + +#ifndef LZ4_SUPPORT +static struct compressor lz4_comp_ops = { + LZ4_COMPRESSION, "lz4" +}; +#else +extern struct compressor lz4_comp_ops; +#endif + +#ifndef XZ_SUPPORT +static struct compressor xz_comp_ops = { + XZ_COMPRESSION, "xz" +}; +#else +extern struct compressor xz_comp_ops; +#endif + + +static struct compressor unknown_comp_ops = { + 0, "unknown" +}; + + +struct compressor *compressor[] = { + &gzip_comp_ops, + &lzma_comp_ops, + &lzo_comp_ops, + &lz4_comp_ops, + &xz_comp_ops, + &unknown_comp_ops +}; + + +struct compressor *lookup_compressor(char *name) +{ + int i; + + for(i = 0; compressor[i]->id; i++) + if(strcmp(compressor[i]->name, name) == 0) + break; + + return compressor[i]; +} + + +struct compressor *lookup_compressor_id(int id) +{ + int i; + + for(i = 0; compressor[i]->id; i++) + if(id == compressor[i]->id) + break; + + return compressor[i]; +} + + +void display_compressors(char *indent, char *def_comp) +{ + int i; + + for(i = 0; compressor[i]->id; i++) + if(compressor[i]->supported) + fprintf(stderr, "%s\t%s%s\n", indent, + compressor[i]->name, + strcmp(compressor[i]->name, def_comp) == 0 ? + " (default)" : ""); +} + + +void display_compressor_usage(char *def_comp) +{ + int i; + + for(i = 0; compressor[i]->id; i++) + if(compressor[i]->supported) { + char *str = strcmp(compressor[i]->name, def_comp) == 0 ? + " (default)" : ""; + if(compressor[i]->usage) { + fprintf(stderr, "\t%s%s\n", + compressor[i]->name, str); + compressor[i]->usage(); + } else + fprintf(stderr, "\t%s (no options)%s\n", + compressor[i]->name, str); + } +}
diff --git a/squashfs-tools/squashfs-tools/compressor.h b/squashfs-tools/squashfs-tools/compressor.h new file mode 100644 index 0000000..4679d91 --- /dev/null +++ b/squashfs-tools/squashfs-tools/compressor.h
@@ -0,0 +1,124 @@ +#ifndef COMPRESSOR_H +#define COMPRESSOR_H +/* + * + * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * compressor.h + */ + +struct compressor { + int id; + char *name; + int supported; + int (*init)(void **, int, int); + int (*compress)(void *, void *, void *, int, int, int *); + int (*uncompress)(void *, void *, int, int, int *); + int (*options)(char **, int); + int (*options_post)(int); + void *(*dump_options)(int, int *); + int (*extract_options)(int, void *, int); + int (*check_options)(int, void *, int); + void (*display_options)(void *, int); + void (*usage)(); +}; + +extern struct compressor *lookup_compressor(char *); +extern struct compressor *lookup_compressor_id(int); +extern void display_compressors(char *, char *); +extern void display_compressor_usage(char *); + +static inline int compressor_init(struct compressor *comp, void **stream, + int block_size, int datablock) +{ + if(comp->init == NULL) + return 0; + return comp->init(stream, block_size, datablock); +} + + +static inline int compressor_compress(struct compressor *comp, void *strm, + void *dest, void *src, int size, int block_size, int *error) +{ + return comp->compress(strm, dest, src, size, block_size, error); +} + + +static inline int compressor_uncompress(struct compressor *comp, void *dest, + void *src, int size, int block_size, int *error) +{ + return comp->uncompress(dest, src, size, block_size, error); +} + + +/* + * For the following functions please see the lzo, lz4 or xz + * compressors for commented examples of how they are used. + */ +static inline int compressor_options(struct compressor *comp, char *argv[], + int argc) +{ + if(comp->options == NULL) + return -1; + + return comp->options(argv, argc); +} + + +static inline int compressor_options_post(struct compressor *comp, int block_size) +{ + if(comp->options_post == NULL) + return 0; + return comp->options_post(block_size); +} + + +static inline void *compressor_dump_options(struct compressor *comp, + int block_size, int *size) +{ + if(comp->dump_options == NULL) + return NULL; + return comp->dump_options(block_size, size); +} + + +static inline int compressor_extract_options(struct compressor *comp, + int block_size, void *buffer, int size) +{ + if(comp->extract_options == NULL) + return size ? -1 : 0; + return comp->extract_options(block_size, buffer, size); +} + + +static inline int compressor_check_options(struct compressor *comp, + int block_size, void *buffer, int size) +{ + if(comp->check_options == NULL) + return 0; + return comp->check_options(block_size, buffer, size); +} + + +static inline void compressor_display_options(struct compressor *comp, + void *buffer, int size) +{ + if(comp->display_options != NULL) + comp->display_options(buffer, size); +} +#endif
diff --git a/squashfs-tools/squashfs-tools/error.h b/squashfs-tools/squashfs-tools/error.h new file mode 100644 index 0000000..358ad68 --- /dev/null +++ b/squashfs-tools/squashfs-tools/error.h
@@ -0,0 +1,90 @@ +#ifndef ERROR_H +#define ERROR_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2012, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * error.h + */ + +extern int exit_on_error; + +extern void prep_exit(); +extern void progressbar_error(char *fmt, ...); +extern void progressbar_info(char *fmt, ...); + +#ifdef SQUASHFS_TRACE +#define TRACE(s, args...) \ + do { \ + progressbar_info("squashfs: "s, ## args);\ + } while(0) +#else +#define TRACE(s, args...) +#endif + +#define INFO(s, args...) \ + do {\ + if(!silent)\ + progressbar_info(s, ## args);\ + } while(0) + +#define ERROR(s, args...) \ + do {\ + progressbar_error(s, ## args); \ + } while(0) + +#define ERROR_START(s, args...) \ + do { \ + disable_progress_bar(); \ + fprintf(stderr, s, ## args); \ + } while(0) + +#define ERROR_EXIT(s, args...) \ + do {\ + if (exit_on_error) { \ + fprintf(stderr, "\n"); \ + EXIT_MKSQUASHFS(); \ + } else { \ + fprintf(stderr, s, ## args); \ + enable_progress_bar(); \ + } \ + } while(0) + +#define EXIT_MKSQUASHFS() \ + do {\ + prep_exit();\ + exit(1);\ + } while(0) + +#define BAD_ERROR(s, args...) \ + do {\ + progressbar_error("FATAL ERROR:" s, ##args); \ + EXIT_MKSQUASHFS();\ + } while(0) + +#define EXIT_UNSQUASH(s, args...) BAD_ERROR(s, ##args) + +#define MEM_ERROR() \ + do {\ + progressbar_error("FATAL ERROR: Out of memory (%s)\n", \ + __func__); \ + EXIT_MKSQUASHFS();\ + } while(0) +#endif
diff --git a/squashfs-tools/squashfs-tools/gzip_wrapper.c b/squashfs-tools/squashfs-tools/gzip_wrapper.c new file mode 100644 index 0000000..076a587 --- /dev/null +++ b/squashfs-tools/squashfs-tools/gzip_wrapper.c
@@ -0,0 +1,500 @@ +/* + * Copyright (c) 2009, 2010, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * gzip_wrapper.c + * + * Support for ZLIB compression http://www.zlib.net + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <zlib.h> + +#include "squashfs_fs.h" +#include "gzip_wrapper.h" +#include "compressor.h" + +static struct strategy strategy[] = { + { "default", Z_DEFAULT_STRATEGY, 0 }, + { "filtered", Z_FILTERED, 0 }, + { "huffman_only", Z_HUFFMAN_ONLY, 0 }, + { "run_length_encoded", Z_RLE, 0 }, + { "fixed", Z_FIXED, 0 }, + { NULL, 0, 0 } +}; + +static int strategy_count = 0; + +/* default compression level */ +static int compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL; + +/* default window size */ +static int window_size = GZIP_DEFAULT_WINDOW_SIZE; + +/* + * This function is called by the options parsing code in mksquashfs.c + * to parse any -X compressor option. + * + * This function returns: + * >=0 (number of additional args parsed) on success + * -1 if the option was unrecognised, or + * -2 if the option was recognised, but otherwise bad in + * some way (e.g. invalid parameter) + * + * Note: this function sets internal compressor state, but does not + * pass back the results of the parsing other than success/failure. + * The gzip_dump_options() function is called later to get the options in + * a format suitable for writing to the filesystem. + */ +static int gzip_options(char *argv[], int argc) +{ + if(strcmp(argv[0], "-Xcompression-level") == 0) { + if(argc < 2) { + fprintf(stderr, "gzip: -Xcompression-level missing " + "compression level\n"); + fprintf(stderr, "gzip: -Xcompression-level it " + "should be 1 >= n <= 9\n"); + goto failed; + } + + compression_level = atoi(argv[1]); + if(compression_level < 1 || compression_level > 9) { + fprintf(stderr, "gzip: -Xcompression-level invalid, it " + "should be 1 >= n <= 9\n"); + goto failed; + } + + return 1; + } else if(strcmp(argv[0], "-Xwindow-size") == 0) { + if(argc < 2) { + fprintf(stderr, "gzip: -Xwindow-size missing window " + " size\n"); + fprintf(stderr, "gzip: -Xwindow-size <window-size>\n"); + goto failed; + } + + window_size = atoi(argv[1]); + if(window_size < 8 || window_size > 15) { + fprintf(stderr, "gzip: -Xwindow-size invalid, it " + "should be 8 >= n <= 15\n"); + goto failed; + } + + return 1; + } else if(strcmp(argv[0], "-Xstrategy") == 0) { + char *name; + int i; + + if(argc < 2) { + fprintf(stderr, "gzip: -Xstrategy missing " + "strategies\n"); + goto failed; + } + + name = argv[1]; + while(name[0] != '\0') { + for(i = 0; strategy[i].name; i++) { + int n = strlen(strategy[i].name); + if((strncmp(name, strategy[i].name, n) == 0) && + (name[n] == '\0' || + name[n] == ',')) { + if(strategy[i].selected == 0) { + strategy[i].selected = 1; + strategy_count++; + } + name += name[n] == ',' ? n + 1 : n; + break; + } + } + if(strategy[i].name == NULL) { + fprintf(stderr, "gzip: -Xstrategy unrecognised " + "strategy\n"); + goto failed; + } + } + + return 1; + } + + return -1; + +failed: + return -2; +} + + +/* + * This function is called after all options have been parsed. + * It is used to do post-processing on the compressor options using + * values that were not expected to be known at option parse time. + * + * This function returns 0 on successful post processing, or + * -1 on error + */ +static int gzip_options_post(int block_size) +{ + if(strategy_count == 1 && strategy[0].selected) { + strategy_count = 0; + strategy[0].selected = 0; + } + + return 0; +} + + +/* + * This function is called by mksquashfs to dump the parsed + * compressor options in a format suitable for writing to the + * compressor options field in the filesystem (stored immediately + * after the superblock). + * + * This function returns a pointer to the compression options structure + * to be stored (and the size), or NULL if there are no compression + * options + * + */ +static void *gzip_dump_options(int block_size, int *size) +{ + static struct gzip_comp_opts comp_opts; + int i, strategies = 0; + + /* + * If default compression options of: + * compression-level: 8 and + * window-size: 15 and + * strategy_count == 0 then + * don't store a compression options structure (this is compatible + * with the legacy implementation of GZIP for Squashfs) + */ + if(compression_level == GZIP_DEFAULT_COMPRESSION_LEVEL && + window_size == GZIP_DEFAULT_WINDOW_SIZE && + strategy_count == 0) + return NULL; + + for(i = 0; strategy[i].name; i++) + strategies |= strategy[i].selected << i; + + comp_opts.compression_level = compression_level; + comp_opts.window_size = window_size; + comp_opts.strategy = strategies; + + SQUASHFS_INSWAP_COMP_OPTS(&comp_opts); + + *size = sizeof(comp_opts); + return &comp_opts; +} + + +/* + * This function is a helper specifically for the append mode of + * mksquashfs. Its purpose is to set the internal compressor state + * to the stored compressor options in the passed compressor options + * structure. + * + * In effect this function sets up the compressor options + * to the same state they were when the filesystem was originally + * generated, this is to ensure on appending, the compressor uses + * the same compression options that were used to generate the + * original filesystem. + * + * Note, even if there are no compressor options, this function is still + * called with an empty compressor structure (size == 0), to explicitly + * set the default options, this is to ensure any user supplied + * -X options on the appending mksquashfs command line are over-ridden + * + * This function returns 0 on sucessful extraction of options, and + * -1 on error + */ +static int gzip_extract_options(int block_size, void *buffer, int size) +{ + struct gzip_comp_opts *comp_opts = buffer; + int i; + + if(size == 0) { + /* Set default values */ + compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL; + window_size = GZIP_DEFAULT_WINDOW_SIZE; + strategy_count = 0; + return 0; + } + + /* we expect a comp_opts structure of sufficient size to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* Check comp_opts structure for correctness */ + if(comp_opts->compression_level < 1 || + comp_opts->compression_level > 9) { + fprintf(stderr, "gzip: bad compression level in " + "compression options structure\n"); + goto failed; + } + compression_level = comp_opts->compression_level; + + if(comp_opts->window_size < 8 || + comp_opts->window_size > 15) { + fprintf(stderr, "gzip: bad window size in " + "compression options structure\n"); + goto failed; + } + window_size = comp_opts->window_size; + + strategy_count = 0; + for(i = 0; strategy[i].name; i++) { + if((comp_opts->strategy >> i) & 1) { + strategy[i].selected = 1; + strategy_count ++; + } else + strategy[i].selected = 0; + } + + return 0; + +failed: + fprintf(stderr, "gzip: error reading stored compressor options from " + "filesystem!\n"); + + return -1; +} + + +void gzip_display_options(void *buffer, int size) +{ + struct gzip_comp_opts *comp_opts = buffer; + int i, printed; + + /* we expect a comp_opts structure of sufficient size to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* Check comp_opts structure for correctness */ + if(comp_opts->compression_level < 1 || + comp_opts->compression_level > 9) { + fprintf(stderr, "gzip: bad compression level in " + "compression options structure\n"); + goto failed; + } + printf("\tcompression-level %d\n", comp_opts->compression_level); + + if(comp_opts->window_size < 8 || + comp_opts->window_size > 15) { + fprintf(stderr, "gzip: bad window size in " + "compression options structure\n"); + goto failed; + } + printf("\twindow-size %d\n", comp_opts->window_size); + + for(i = 0, printed = 0; strategy[i].name; i++) { + if((comp_opts->strategy >> i) & 1) { + if(printed) + printf(", "); + else + printf("\tStrategies selected: "); + printf("%s", strategy[i].name); + printed = 1; + } + } + + if(!printed) + printf("\tStrategies selected: default\n"); + else + printf("\n"); + + return; + +failed: + fprintf(stderr, "gzip: error reading stored compressor options from " + "filesystem!\n"); +} + + +/* + * This function is called by mksquashfs to initialise the + * compressor, before compress() is called. + * + * This function returns 0 on success, and + * -1 on error + */ +static int gzip_init(void **strm, int block_size, int datablock) +{ + int i, j, res; + struct gzip_stream *stream; + + if(!datablock || !strategy_count) { + stream = malloc(sizeof(*stream) + sizeof(struct gzip_strategy)); + if(stream == NULL) + goto failed; + + stream->strategies = 1; + stream->strategy[0].strategy = Z_DEFAULT_STRATEGY; + } else { + stream = malloc(sizeof(*stream) + + sizeof(struct gzip_strategy) * strategy_count); + if(stream == NULL) + goto failed; + + memset(stream->strategy, 0, sizeof(struct gzip_strategy) * + strategy_count); + + stream->strategies = strategy_count; + + for(i = 0, j = 0; strategy[i].name; i++) { + if(!strategy[i].selected) + continue; + + stream->strategy[j].strategy = strategy[i].strategy; + if(j) { + stream->strategy[j].buffer = malloc(block_size); + if(stream->strategy[j].buffer == NULL) + goto failed2; + } + j++; + } + } + + stream->stream.zalloc = Z_NULL; + stream->stream.zfree = Z_NULL; + stream->stream.opaque = 0; + + res = deflateInit2(&stream->stream, compression_level, Z_DEFLATED, + window_size, 8, stream->strategy[0].strategy); + if(res != Z_OK) + goto failed2; + + *strm = stream; + return 0; + +failed2: + for(i = 1; i < stream->strategies; i++) + free(stream->strategy[i].buffer); + free(stream); +failed: + return -1; +} + + +static int gzip_compress(void *strm, void *d, void *s, int size, int block_size, + int *error) +{ + int i, res; + struct gzip_stream *stream = strm; + struct gzip_strategy *selected = NULL; + + stream->strategy[0].buffer = d; + + for(i = 0; i < stream->strategies; i++) { + struct gzip_strategy *strategy = &stream->strategy[i]; + + res = deflateReset(&stream->stream); + if(res != Z_OK) + goto failed; + + stream->stream.next_in = s; + stream->stream.avail_in = size; + stream->stream.next_out = strategy->buffer; + stream->stream.avail_out = block_size; + + if(stream->strategies > 1) { + res = deflateParams(&stream->stream, + compression_level, strategy->strategy); + if(res != Z_OK) + goto failed; + } + + res = deflate(&stream->stream, Z_FINISH); + strategy->length = stream->stream.total_out; + if(res == Z_STREAM_END) { + if(!selected || selected->length > strategy->length) + selected = strategy; + } else if(res != Z_OK) + goto failed; + } + + if(!selected) + /* + * Output buffer overflow. Return out of buffer space + */ + return 0; + + if(selected->buffer != d) + memcpy(d, selected->buffer, selected->length); + + return (int) selected->length; + +failed: + /* + * All other errors return failure, with the compressor + * specific error code in *error + */ + *error = res; + return -1; +} + + +static int gzip_uncompress(void *d, void *s, int size, int outsize, int *error) +{ + int res; + unsigned long bytes = outsize; + + res = uncompress(d, &bytes, s, size); + + if(res == Z_OK) + return (int) bytes; + else { + *error = res; + return -1; + } +} + + +void gzip_usage() +{ + fprintf(stderr, "\t -Xcompression-level <compression-level>\n"); + fprintf(stderr, "\t\t<compression-level> should be 1 .. 9 (default " + "%d)\n", GZIP_DEFAULT_COMPRESSION_LEVEL); + fprintf(stderr, "\t -Xwindow-size <window-size>\n"); + fprintf(stderr, "\t\t<window-size> should be 8 .. 15 (default " + "%d)\n", GZIP_DEFAULT_WINDOW_SIZE); + fprintf(stderr, "\t -Xstrategy strategy1,strategy2,...,strategyN\n"); + fprintf(stderr, "\t\tCompress using strategy1,strategy2,...,strategyN" + " in turn\n"); + fprintf(stderr, "\t\tand choose the best compression.\n"); + fprintf(stderr, "\t\tAvailable strategies: default, filtered, " + "huffman_only,\n\t\trun_length_encoded and fixed\n"); +} + + +struct compressor gzip_comp_ops = { + .init = gzip_init, + .compress = gzip_compress, + .uncompress = gzip_uncompress, + .options = gzip_options, + .options_post = gzip_options_post, + .dump_options = gzip_dump_options, + .extract_options = gzip_extract_options, + .display_options = gzip_display_options, + .usage = gzip_usage, + .id = ZLIB_COMPRESSION, + .name = "gzip", + .supported = 1 +};
diff --git a/squashfs-tools/squashfs-tools/gzip_wrapper.h b/squashfs-tools/squashfs-tools/gzip_wrapper.h new file mode 100644 index 0000000..463e9f4 --- /dev/null +++ b/squashfs-tools/squashfs-tools/gzip_wrapper.h
@@ -0,0 +1,75 @@ +#ifndef GZIP_WRAPPER_H +#define GZIP_WRAPPER_H +/* + * Squashfs + * + * Copyright (c) 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * gzip_wrapper.h + * + */ + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include <endian.h> +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +extern unsigned int inswap_le16(unsigned short); +extern unsigned int inswap_le32(unsigned int); + +#define SQUASHFS_INSWAP_COMP_OPTS(s) { \ + (s)->compression_level = inswap_le32((s)->compression_level); \ + (s)->window_size = inswap_le16((s)->window_size); \ + (s)->strategy = inswap_le16((s)->strategy); \ +} +#else +#define SQUASHFS_INSWAP_COMP_OPTS(s) +#endif + +/* Default compression */ +#define GZIP_DEFAULT_COMPRESSION_LEVEL 9 +#define GZIP_DEFAULT_WINDOW_SIZE 15 + +struct gzip_comp_opts { + int compression_level; + short window_size; + short strategy; +}; + +struct strategy { + char *name; + int strategy; + int selected; +}; + +struct gzip_strategy { + int strategy; + int length; + void *buffer; +}; + +struct gzip_stream { + z_stream stream; + int strategies; + struct gzip_strategy strategy[0]; +}; +#endif
diff --git a/squashfs-tools/squashfs-tools/info.c b/squashfs-tools/squashfs-tools/info.c new file mode 100644 index 0000000..60a3f5a --- /dev/null +++ b/squashfs-tools/squashfs-tools/info.c
@@ -0,0 +1,176 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * info.c + */ + +#include <pthread.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#include <stdio.h> +#include <math.h> +#include <stdarg.h> +#include <errno.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> + +#include "squashfs_fs.h" +#include "mksquashfs.h" +#include "error.h" +#include "progressbar.h" +#include "caches-queues-lists.h" + +static int silent = 0; +static struct dir_ent *ent = NULL; + +pthread_t info_thread; + + +void disable_info() +{ + ent = NULL; +} + + +void update_info(struct dir_ent *dir_ent) +{ + ent = dir_ent; +} + + +void print_filename() +{ + struct dir_ent *dir_ent = ent; + + if(dir_ent == NULL) + return; + + if(dir_ent->our_dir->subpath[0] != '\0') + INFO("%s/%s\n", dir_ent->our_dir->subpath, dir_ent->name); + else + INFO("/%s\n", dir_ent->name); +} + + +void dump_state() +{ + disable_progress_bar(); + + printf("Queue and Cache status dump\n"); + printf("===========================\n"); + + printf("file buffer queue (reader thread -> deflate thread(s))\n"); + dump_queue(to_deflate); + + printf("uncompressed fragment queue (reader thread -> fragment" + " thread(s))\n"); + dump_queue(to_process_frag); + + printf("processed fragment queue (fragment thread(s) -> main" + " thread)\n"); + dump_seq_queue(to_main, 1); + + printf("compressed block queue (deflate thread(s) -> main thread)\n"); + dump_seq_queue(to_main, 0); + + printf("uncompressed packed fragment queue (main thread -> fragment" + " deflate thread(s))\n"); + dump_queue(to_frag); + + + printf("locked frag queue (compressed frags waiting while multi-block" + " file is written)\n"); + dump_queue(locked_fragment); + + printf("compressed block queue (main & fragment deflate threads(s) ->" + " writer thread)\n"); + dump_queue(to_writer); + + printf("read cache (uncompressed blocks read by reader thread)\n"); + dump_cache(reader_buffer); + + printf("block write cache (compressed blocks waiting for the writer" + " thread)\n"); + dump_cache(bwriter_buffer); + printf("fragment write cache (compressed fragments waiting for the" + " writer thread)\n"); + dump_cache(fwriter_buffer); + + printf("fragment cache (frags waiting to be compressed by fragment" + " deflate thread(s))\n"); + dump_cache(fragment_buffer); + + printf("fragment reserve cache (avoids pipeline stall if frag cache" + " full in dup check)\n"); + dump_cache(reserve_cache); + + enable_progress_bar(); +} + + +void *info_thrd(void *arg) +{ + sigset_t sigmask; + int sig, err, waiting = 0; + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGHUP); + sigaddset(&sigmask, SIGALRM); + + while(1) { + err = sigwait(&sigmask, &sig); + + if(err == -1) { + switch(errno) { + case EINTR: + continue; + default: + BAD_ERROR("sigwait failed " + "because %s\n", strerror(errno)); + } + } + + if(sig == SIGQUIT && !waiting) { + print_filename(); + + /* set one second interval period, if ^\ received + within then, dump queue and cache status */ + waiting = 1; + alarm(1); + } else if (sig == SIGQUIT) { + dump_state(); + } else if (sig == SIGALRM) { + waiting = 0; + } + } +} + + +void init_info() +{ + pthread_create(&info_thread, NULL, info_thrd, NULL); +}
diff --git a/squashfs-tools/squashfs-tools/info.h b/squashfs-tools/squashfs-tools/info.h new file mode 100644 index 0000000..bcf03a2 --- /dev/null +++ b/squashfs-tools/squashfs-tools/info.h
@@ -0,0 +1,30 @@ +#ifndef INFO_H +#define INFO_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * info.h + */ + +extern void disable_info(); +extern void update_info(struct dir_ent *); +extern void init_info(); +#endif
diff --git a/squashfs-tools/squashfs-tools/lz4_wrapper.c b/squashfs-tools/squashfs-tools/lz4_wrapper.c new file mode 100644 index 0000000..b87cfe0 --- /dev/null +++ b/squashfs-tools/squashfs-tools/lz4_wrapper.c
@@ -0,0 +1,283 @@ +/* + * Copyright (c) 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * lz4_wrapper.c + * + * Support for LZ4 compression http://fastcompression.blogspot.com/p/lz4.html + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <lz4.h> +#include <lz4hc.h> + +#include "squashfs_fs.h" +#include "lz4_wrapper.h" +#include "compressor.h" + +static int hc = 0; + +/* + * This function is called by the options parsing code in mksquashfs.c + * to parse any -X compressor option. + * + * This function returns: + * >=0 (number of additional args parsed) on success + * -1 if the option was unrecognised, or + * -2 if the option was recognised, but otherwise bad in + * some way (e.g. invalid parameter) + * + * Note: this function sets internal compressor state, but does not + * pass back the results of the parsing other than success/failure. + * The lz4_dump_options() function is called later to get the options in + * a format suitable for writing to the filesystem. + */ +static int lz4_options(char *argv[], int argc) +{ + if(strcmp(argv[0], "-Xhc") == 0) { + hc = 1; + return 0; + } + + return -1; +} + + +/* + * This function is called by mksquashfs to dump the parsed + * compressor options in a format suitable for writing to the + * compressor options field in the filesystem (stored immediately + * after the superblock). + * + * This function returns a pointer to the compression options structure + * to be stored (and the size), or NULL if there are no compression + * options + * + * Currently LZ4 always returns a comp_opts structure, with + * the version indicating LZ4_LEGACY stream fomat. This is to + * easily accomodate changes in the kernel code to different + * stream formats + */ +static void *lz4_dump_options(int block_size, int *size) +{ + static struct lz4_comp_opts comp_opts; + + comp_opts.version = LZ4_LEGACY; + comp_opts.flags = hc ? LZ4_HC : 0; + SQUASHFS_INSWAP_COMP_OPTS(&comp_opts); + + *size = sizeof(comp_opts); + return &comp_opts; +} + + +/* + * This function is a helper specifically for the append mode of + * mksquashfs. Its purpose is to set the internal compressor state + * to the stored compressor options in the passed compressor options + * structure. + * + * In effect this function sets up the compressor options + * to the same state they were when the filesystem was originally + * generated, this is to ensure on appending, the compressor uses + * the same compression options that were used to generate the + * original filesystem. + * + * Note, even if there are no compressor options, this function is still + * called with an empty compressor structure (size == 0), to explicitly + * set the default options, this is to ensure any user supplied + * -X options on the appending mksquashfs command line are over-ridden + * + * This function returns 0 on sucessful extraction of options, and + * -1 on error + */ +static int lz4_extract_options(int block_size, void *buffer, int size) +{ + struct lz4_comp_opts *comp_opts = buffer; + + /* we expect a comp_opts structure to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* we expect the stream format to be LZ4_LEGACY */ + if(comp_opts->version != LZ4_LEGACY) { + fprintf(stderr, "lz4: unknown LZ4 version\n"); + goto failed; + } + + /* + * Check compression flags, currently only LZ4_HC ("high compression") + * can be set. + */ + if(comp_opts->flags == LZ4_HC) + hc = 1; + else if(comp_opts->flags != 0) { + fprintf(stderr, "lz4: unknown LZ4 flags\n"); + goto failed; + } + + return 0; + +failed: + fprintf(stderr, "lz4: error reading stored compressor options from " + "filesystem!\n"); + + return -1; +} + + +/* + * This function is a helper specifically for unsquashfs. + * Its purpose is to check that the compression options are + * understood by this version of LZ4. + * + * This is important for LZ4 because the format understood by the + * Linux kernel may change from the already obsolete legacy format + * currently supported. + * + * If this does happen, then this version of LZ4 will not be able to decode + * the newer format. So we need to check for this. + * + * This function returns 0 on sucessful checking of options, and + * -1 on error + */ +static int lz4_check_options(int block_size, void *buffer, int size) +{ + struct lz4_comp_opts *comp_opts = buffer; + + /* we expect a comp_opts structure to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* we expect the stream format to be LZ4_LEGACY */ + if(comp_opts->version != LZ4_LEGACY) { + fprintf(stderr, "lz4: unknown LZ4 version\n"); + goto failed; + } + + return 0; + +failed: + fprintf(stderr, "lz4: error reading stored compressor options from " + "filesystem!\n"); + return -1; +} + + +void lz4_display_options(void *buffer, int size) +{ + struct lz4_comp_opts *comp_opts = buffer; + + /* check passed comp opts struct is of the correct length */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* we expect the stream format to be LZ4_LEGACY */ + if(comp_opts->version != LZ4_LEGACY) { + fprintf(stderr, "lz4: unknown LZ4 version\n"); + goto failed; + } + + /* + * Check compression flags, currently only LZ4_HC ("high compression") + * can be set. + */ + if(comp_opts->flags & ~LZ4_FLAGS_MASK) { + fprintf(stderr, "lz4: unknown LZ4 flags\n"); + goto failed; + } + + if(comp_opts->flags & LZ4_HC) + printf("\tHigh Compression option specified (-Xhc)\n"); + + return; + +failed: + fprintf(stderr, "lz4: error reading stored compressor options from " + "filesystem!\n"); +} + + +static int lz4_compress(void *strm, void *dest, void *src, int size, + int block_size, int *error) +{ + int res; + + if(hc) + res = LZ4_compressHC_limitedOutput(src, dest, size, block_size); + else + res = LZ4_compress_limitedOutput(src, dest, size, block_size); + + if(res == 0) { + /* + * Output buffer overflow. Return out of buffer space + */ + return 0; + } else if(res < 0) { + /* + * All other errors return failure, with the compressor + * specific error code in *error + */ + *error = res; + return -1; + } + + return res; +} + + +static int lz4_uncompress(void *dest, void *src, int size, int outsize, + int *error) +{ + int res = LZ4_decompress_safe(src, dest, size, outsize); + if(res < 0) { + *error = res; + return -1; + } + + return res; +} + + +void lz4_usage() +{ + fprintf(stderr, "\t -Xhc\n"); + fprintf(stderr, "\t\tCompress using LZ4 High Compression\n"); +} + + +struct compressor lz4_comp_ops = { + .compress = lz4_compress, + .uncompress = lz4_uncompress, + .options = lz4_options, + .dump_options = lz4_dump_options, + .extract_options = lz4_extract_options, + .check_options = lz4_check_options, + .display_options = lz4_display_options, + .usage = lz4_usage, + .id = LZ4_COMPRESSION, + .name = "lz4", + .supported = 1 +};
diff --git a/squashfs-tools/squashfs-tools/lz4_wrapper.h b/squashfs-tools/squashfs-tools/lz4_wrapper.h new file mode 100644 index 0000000..d6638a5 --- /dev/null +++ b/squashfs-tools/squashfs-tools/lz4_wrapper.h
@@ -0,0 +1,61 @@ +#ifndef LZ4_WRAPPER_H +#define LZ4_WRAPPER_H +/* + * Squashfs + * + * Copyright (c) 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * lz4_wrapper.h + * + */ + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include <endian.h> +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +extern unsigned int inswap_le32(unsigned int); + +#define SQUASHFS_INSWAP_COMP_OPTS(s) { \ + (s)->version = inswap_le32((s)->version); \ + (s)->flags = inswap_le32((s)->flags); \ +} +#else +#define SQUASHFS_INSWAP_COMP_OPTS(s) +#endif + +/* + * Define the various stream formats recognised. + * Currently omly legacy stream format is supported by the + * kernel + */ +#define LZ4_LEGACY 1 +#define LZ4_FLAGS_MASK 1 + +/* Define the compression flags recognised. */ +#define LZ4_HC 1 + +struct lz4_comp_opts { + int version; + int flags; +}; +#endif
diff --git a/squashfs-tools/squashfs-tools/lzma_wrapper.c b/squashfs-tools/squashfs-tools/lzma_wrapper.c new file mode 100644 index 0000000..8d64e3d --- /dev/null +++ b/squashfs-tools/squashfs-tools/lzma_wrapper.c
@@ -0,0 +1,121 @@ +/* + * Copyright (c) 2009, 2010, 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * lzma_wrapper.c + * + * Support for LZMA1 compression using LZMA SDK (4.65 used in + * development, other versions may work) http://www.7-zip.org/sdk.html + */ + +#include <LzmaLib.h> + +#include "squashfs_fs.h" +#include "compressor.h" + +#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8) + +static int lzma_compress(void *strm, void *dest, void *src, int size, int block_size, + int *error) +{ + unsigned char *d = dest; + size_t props_size = LZMA_PROPS_SIZE, + outlen = block_size - LZMA_HEADER_SIZE; + int res; + + res = LzmaCompress(dest + LZMA_HEADER_SIZE, &outlen, src, size, dest, + &props_size, 5, block_size, 3, 0, 2, 32, 1); + + if(res == SZ_ERROR_OUTPUT_EOF) { + /* + * Output buffer overflow. Return out of buffer space error + */ + return 0; + } + + if(res != SZ_OK) { + /* + * All other errors return failure, with the compressor + * specific error code in *error + */ + *error = res; + return -1; + } + + /* + * Fill in the 8 byte little endian uncompressed size field in the + * LZMA header. 8 bytes is excessively large for squashfs but + * this is the standard LZMA header and which is expected by the kernel + * code + */ + d[LZMA_PROPS_SIZE] = size & 255; + d[LZMA_PROPS_SIZE + 1] = (size >> 8) & 255; + d[LZMA_PROPS_SIZE + 2] = (size >> 16) & 255; + d[LZMA_PROPS_SIZE + 3] = (size >> 24) & 255; + d[LZMA_PROPS_SIZE + 4] = 0; + d[LZMA_PROPS_SIZE + 5] = 0; + d[LZMA_PROPS_SIZE + 6] = 0; + d[LZMA_PROPS_SIZE + 7] = 0; + + /* + * Success, return the compressed size. Outlen returned by the LZMA + * compressor does not include the LZMA header space + */ + return outlen + LZMA_HEADER_SIZE; +} + + +static int lzma_uncompress(void *dest, void *src, int size, int outsize, + int *error) +{ + unsigned char *s = src; + size_t outlen, inlen = size - LZMA_HEADER_SIZE; + int res; + + outlen = s[LZMA_PROPS_SIZE] | + (s[LZMA_PROPS_SIZE + 1] << 8) | + (s[LZMA_PROPS_SIZE + 2] << 16) | + (s[LZMA_PROPS_SIZE + 3] << 24); + + if(outlen > outsize) { + *error = 0; + return -1; + } + + res = LzmaUncompress(dest, &outlen, src + LZMA_HEADER_SIZE, &inlen, src, + LZMA_PROPS_SIZE); + + if(res == SZ_OK) + return outlen; + else { + *error = res; + return -1; + } +} + + +struct compressor lzma_comp_ops = { + .init = NULL, + .compress = lzma_compress, + .uncompress = lzma_uncompress, + .options = NULL, + .usage = NULL, + .id = LZMA_COMPRESSION, + .name = "lzma", + .supported = 1 +}; +
diff --git a/squashfs-tools/squashfs-tools/lzma_xz_wrapper.c b/squashfs-tools/squashfs-tools/lzma_xz_wrapper.c new file mode 100644 index 0000000..55a6813 --- /dev/null +++ b/squashfs-tools/squashfs-tools/lzma_xz_wrapper.c
@@ -0,0 +1,163 @@ +/* + * Copyright (c) 2010, 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * lzma_xz_wrapper.c + * + * Support for LZMA1 compression using XZ Utils liblzma http://tukaani.org/xz/ + */ + +#include <stdio.h> +#include <string.h> +#include <lzma.h> + +#include "squashfs_fs.h" +#include "compressor.h" + +#define LZMA_PROPS_SIZE 5 +#define LZMA_UNCOMP_SIZE 8 +#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMP_SIZE) + +#define LZMA_OPTIONS 5 +#define MEMLIMIT (32 * 1024 * 1024) + +static int lzma_compress(void *dummy, void *dest, void *src, int size, + int block_size, int *error) +{ + unsigned char *d = (unsigned char *) dest; + lzma_options_lzma opt; + lzma_stream strm = LZMA_STREAM_INIT; + int res; + + lzma_lzma_preset(&opt, LZMA_OPTIONS); + opt.dict_size = block_size; + + res = lzma_alone_encoder(&strm, &opt); + if(res != LZMA_OK) { + lzma_end(&strm); + goto failed; + } + + strm.next_out = dest; + strm.avail_out = block_size; + strm.next_in = src; + strm.avail_in = size; + + res = lzma_code(&strm, LZMA_FINISH); + lzma_end(&strm); + + if(res == LZMA_STREAM_END) { + /* + * Fill in the 8 byte little endian uncompressed size field in + * the LZMA header. 8 bytes is excessively large for squashfs + * but this is the standard LZMA header and which is expected by + * the kernel code + */ + + d[LZMA_PROPS_SIZE] = size & 255; + d[LZMA_PROPS_SIZE + 1] = (size >> 8) & 255; + d[LZMA_PROPS_SIZE + 2] = (size >> 16) & 255; + d[LZMA_PROPS_SIZE + 3] = (size >> 24) & 255; + d[LZMA_PROPS_SIZE + 4] = 0; + d[LZMA_PROPS_SIZE + 5] = 0; + d[LZMA_PROPS_SIZE + 6] = 0; + d[LZMA_PROPS_SIZE + 7] = 0; + + return (int) strm.total_out; + } + + if(res == LZMA_OK) + /* + * Output buffer overflow. Return out of buffer space + */ + return 0; + +failed: + /* + * All other errors return failure, with the compressor + * specific error code in *error + */ + *error = res; + return -1; +} + + +static int lzma_uncompress(void *dest, void *src, int size, int outsize, + int *error) +{ + lzma_stream strm = LZMA_STREAM_INIT; + int uncompressed_size = 0, res; + unsigned char lzma_header[LZMA_HEADER_SIZE]; + + res = lzma_alone_decoder(&strm, MEMLIMIT); + if(res != LZMA_OK) { + lzma_end(&strm); + goto failed; + } + + memcpy(lzma_header, src, LZMA_HEADER_SIZE); + uncompressed_size = lzma_header[LZMA_PROPS_SIZE] | + (lzma_header[LZMA_PROPS_SIZE + 1] << 8) | + (lzma_header[LZMA_PROPS_SIZE + 2] << 16) | + (lzma_header[LZMA_PROPS_SIZE + 3] << 24); + + if(uncompressed_size > outsize) { + res = 0; + goto failed; + } + + memset(lzma_header + LZMA_PROPS_SIZE, 255, LZMA_UNCOMP_SIZE); + + strm.next_out = dest; + strm.avail_out = outsize; + strm.next_in = lzma_header; + strm.avail_in = LZMA_HEADER_SIZE; + + res = lzma_code(&strm, LZMA_RUN); + + if(res != LZMA_OK || strm.avail_in != 0) { + lzma_end(&strm); + goto failed; + } + + strm.next_in = src + LZMA_HEADER_SIZE; + strm.avail_in = size - LZMA_HEADER_SIZE; + + res = lzma_code(&strm, LZMA_FINISH); + lzma_end(&strm); + + if(res == LZMA_STREAM_END || (res == LZMA_OK && + strm.total_out >= uncompressed_size && strm.avail_in == 0)) + return uncompressed_size; + +failed: + *error = res; + return -1; +} + + +struct compressor lzma_comp_ops = { + .init = NULL, + .compress = lzma_compress, + .uncompress = lzma_uncompress, + .options = NULL, + .usage = NULL, + .id = LZMA_COMPRESSION, + .name = "lzma", + .supported = 1 +}; +
diff --git a/squashfs-tools/squashfs-tools/lzo_wrapper.c b/squashfs-tools/squashfs-tools/lzo_wrapper.c new file mode 100644 index 0000000..8c9bf95 --- /dev/null +++ b/squashfs-tools/squashfs-tools/lzo_wrapper.c
@@ -0,0 +1,439 @@ +/* + * Copyright (c) 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * lzo_wrapper.c + * + * Support for LZO compression http://www.oberhumer.com/opensource/lzo + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <lzo/lzoconf.h> +#include <lzo/lzo1x.h> + +#include "squashfs_fs.h" +#include "lzo_wrapper.h" +#include "compressor.h" + +static struct lzo_algorithm lzo[] = { + { "lzo1x_1", LZO1X_1_MEM_COMPRESS, lzo1x_1_compress }, + { "lzo1x_1_11", LZO1X_1_11_MEM_COMPRESS, lzo1x_1_11_compress }, + { "lzo1x_1_12", LZO1X_1_12_MEM_COMPRESS, lzo1x_1_12_compress }, + { "lzo1x_1_15", LZO1X_1_15_MEM_COMPRESS, lzo1x_1_15_compress }, + { "lzo1x_999", LZO1X_999_MEM_COMPRESS, lzo1x_999_wrapper }, + { NULL, 0, NULL } +}; + +/* default LZO compression algorithm and compression level */ +static int algorithm = SQUASHFS_LZO1X_999; +static int compression_level = SQUASHFS_LZO1X_999_COMP_DEFAULT; + +/* user specified compression level */ +static int user_comp_level = -1; + + +/* + * This function is called by the options parsing code in mksquashfs.c + * to parse any -X compressor option. + * + * This function returns: + * >=0 (number of additional args parsed) on success + * -1 if the option was unrecognised, or + * -2 if the option was recognised, but otherwise bad in + * some way (e.g. invalid parameter) + * + * Note: this function sets internal compressor state, but does not + * pass back the results of the parsing other than success/failure. + * The lzo_dump_options() function is called later to get the options in + * a format suitable for writing to the filesystem. + */ +static int lzo_options(char *argv[], int argc) +{ + int i; + + if(strcmp(argv[0], "-Xalgorithm") == 0) { + if(argc < 2) { + fprintf(stderr, "lzo: -Xalgorithm missing algorithm\n"); + fprintf(stderr, "lzo: -Xalgorithm <algorithm>\n"); + goto failed2; + } + + for(i = 0; lzo[i].name; i++) { + if(strcmp(argv[1], lzo[i].name) == 0) { + algorithm = i; + return 1; + } + } + + fprintf(stderr, "lzo: -Xalgorithm unrecognised algorithm\n"); + goto failed2; + } else if(strcmp(argv[0], "-Xcompression-level") == 0) { + if(argc < 2) { + fprintf(stderr, "lzo: -Xcompression-level missing " + "compression level\n"); + fprintf(stderr, "lzo: -Xcompression-level it " + "should be 1 >= n <= 9\n"); + goto failed; + } + + user_comp_level = atoi(argv[1]); + if(user_comp_level < 1 || user_comp_level > 9) { + fprintf(stderr, "lzo: -Xcompression-level invalid, it " + "should be 1 >= n <= 9\n"); + goto failed; + } + + return 1; + } + + return -1; + +failed: + return -2; + +failed2: + fprintf(stderr, "lzo: compression algorithm should be one of:\n"); + for(i = 0; lzo[i].name; i++) + fprintf(stderr, "\t%s\n", lzo[i].name); + return -2; +} + + +/* + * This function is called after all options have been parsed. + * It is used to do post-processing on the compressor options using + * values that were not expected to be known at option parse time. + * + * In this case the LZO algorithm may not be known until after the + * compression level has been set (-Xalgorithm used after -Xcompression-level) + * + * This function returns 0 on successful post processing, or + * -1 on error + */ +static int lzo_options_post(int block_size) +{ + /* + * Use of compression level only makes sense for + * LZO1X_999 algorithm + */ + if(user_comp_level != -1) { + if(algorithm != SQUASHFS_LZO1X_999) { + fprintf(stderr, "lzo: -Xcompression-level not " + "supported by selected %s algorithm\n", + lzo[algorithm].name); + fprintf(stderr, "lzo: -Xcompression-level is only " + "applicable for the lzo1x_999 algorithm\n"); + goto failed; + } + compression_level = user_comp_level; + } + + return 0; + +failed: + return -1; +} + + +/* + * This function is called by mksquashfs to dump the parsed + * compressor options in a format suitable for writing to the + * compressor options field in the filesystem (stored immediately + * after the superblock). + * + * This function returns a pointer to the compression options structure + * to be stored (and the size), or NULL if there are no compression + * options + * + */ +static void *lzo_dump_options(int block_size, int *size) +{ + static struct lzo_comp_opts comp_opts; + + /* + * If default compression options of SQUASHFS_LZO1X_999 and + * compression level of SQUASHFS_LZO1X_999_COMP_DEFAULT then + * don't store a compression options structure (this is compatible + * with the legacy implementation of LZO for Squashfs) + */ + if(algorithm == SQUASHFS_LZO1X_999 && + compression_level == SQUASHFS_LZO1X_999_COMP_DEFAULT) + return NULL; + + comp_opts.algorithm = algorithm; + comp_opts.compression_level = algorithm == SQUASHFS_LZO1X_999 ? + compression_level : 0; + + SQUASHFS_INSWAP_COMP_OPTS(&comp_opts); + + *size = sizeof(comp_opts); + return &comp_opts; +} + + +/* + * This function is a helper specifically for the append mode of + * mksquashfs. Its purpose is to set the internal compressor state + * to the stored compressor options in the passed compressor options + * structure. + * + * In effect this function sets up the compressor options + * to the same state they were when the filesystem was originally + * generated, this is to ensure on appending, the compressor uses + * the same compression options that were used to generate the + * original filesystem. + * + * Note, even if there are no compressor options, this function is still + * called with an empty compressor structure (size == 0), to explicitly + * set the default options, this is to ensure any user supplied + * -X options on the appending mksquashfs command line are over-ridden + * + * This function returns 0 on sucessful extraction of options, and + * -1 on error + */ +static int lzo_extract_options(int block_size, void *buffer, int size) +{ + struct lzo_comp_opts *comp_opts = buffer; + + if(size == 0) { + /* Set default values */ + algorithm = SQUASHFS_LZO1X_999; + compression_level = SQUASHFS_LZO1X_999_COMP_DEFAULT; + return 0; + } + + /* we expect a comp_opts structure of sufficient size to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* Check comp_opts structure for correctness */ + switch(comp_opts->algorithm) { + case SQUASHFS_LZO1X_1: + case SQUASHFS_LZO1X_1_11: + case SQUASHFS_LZO1X_1_12: + case SQUASHFS_LZO1X_1_15: + if(comp_opts->compression_level != 0) { + fprintf(stderr, "lzo: bad compression level in " + "compression options structure\n"); + goto failed; + } + break; + case SQUASHFS_LZO1X_999: + if(comp_opts->compression_level < 1 || + comp_opts->compression_level > 9) { + fprintf(stderr, "lzo: bad compression level in " + "compression options structure\n"); + goto failed; + } + compression_level = comp_opts->compression_level; + break; + default: + fprintf(stderr, "lzo: bad algorithm in compression options " + "structure\n"); + goto failed; + } + + algorithm = comp_opts->algorithm; + + return 0; + +failed: + fprintf(stderr, "lzo: error reading stored compressor options from " + "filesystem!\n"); + + return -1; +} + + +void lzo_display_options(void *buffer, int size) +{ + struct lzo_comp_opts *comp_opts = buffer; + + /* we expect a comp_opts structure of sufficient size to be present */ + if(size < sizeof(*comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + /* Check comp_opts structure for correctness */ + switch(comp_opts->algorithm) { + case SQUASHFS_LZO1X_1: + case SQUASHFS_LZO1X_1_11: + case SQUASHFS_LZO1X_1_12: + case SQUASHFS_LZO1X_1_15: + printf("\talgorithm %s\n", lzo[comp_opts->algorithm].name); + break; + case SQUASHFS_LZO1X_999: + if(comp_opts->compression_level < 1 || + comp_opts->compression_level > 9) { + fprintf(stderr, "lzo: bad compression level in " + "compression options structure\n"); + goto failed; + } + printf("\talgorithm %s\n", lzo[comp_opts->algorithm].name); + printf("\tcompression level %d\n", + comp_opts->compression_level); + break; + default: + fprintf(stderr, "lzo: bad algorithm in compression options " + "structure\n"); + goto failed; + } + + return; + +failed: + fprintf(stderr, "lzo: error reading stored compressor options from " + "filesystem!\n"); +} + + +/* + * This function is called by mksquashfs to initialise the + * compressor, before compress() is called. + * + * This function returns 0 on success, and + * -1 on error + */ +static int squashfs_lzo_init(void **strm, int block_size, int datablock) +{ + struct lzo_stream *stream; + + stream = *strm = malloc(sizeof(struct lzo_stream)); + if(stream == NULL) + goto failed; + + stream->workspace = malloc(lzo[algorithm].size); + if(stream->workspace == NULL) + goto failed2; + + stream->buffer = malloc(LZO_MAX_EXPANSION(block_size)); + if(stream->buffer != NULL) + return 0; + + free(stream->workspace); +failed2: + free(stream); +failed: + return -1; +} + + +static int lzo_compress(void *strm, void *dest, void *src, int size, + int block_size, int *error) +{ + int res; + lzo_uint compsize, orig_size = size; + struct lzo_stream *stream = strm; + + res = lzo[algorithm].compress(src, size, stream->buffer, &compsize, + stream->workspace); + if(res != LZO_E_OK) + goto failed; + + /* Successful compression, however, we need to check that + * the compressed size is not larger than the available + * buffer space. Normally in other compressor APIs they take + * a destination buffer size, and overflows return an error. + * With LZO it lacks a destination size and so we must output + * to a temporary buffer large enough to accomodate any + * result, and explictly check here for overflow + */ + if(compsize > block_size) + return 0; + + res = lzo1x_optimize(stream->buffer, compsize, src, &orig_size, NULL); + + if (res != LZO_E_OK || orig_size != size) + goto failed; + + memcpy(dest, stream->buffer, compsize); + return compsize; + +failed: + /* fail, compressor specific error code returned in error */ + *error = res; + return -1; +} + + +static int lzo_uncompress(void *dest, void *src, int size, int outsize, + int *error) +{ + int res; + lzo_uint outlen = outsize; + + res = lzo1x_decompress_safe(src, size, dest, &outlen, NULL); + if(res != LZO_E_OK) { + *error = res; + return -1; + } + + return outlen; +} + + +void lzo_usage() +{ + int i; + + fprintf(stderr, "\t -Xalgorithm <algorithm>\n"); + fprintf(stderr, "\t\tWhere <algorithm> is one of:\n"); + + for(i = 0; lzo[i].name; i++) + fprintf(stderr, "\t\t\t%s%s\n", lzo[i].name, + i == SQUASHFS_LZO1X_999 ? " (default)" : ""); + + fprintf(stderr, "\t -Xcompression-level <compression-level>\n"); + fprintf(stderr, "\t\t<compression-level> should be 1 .. 9 (default " + "%d)\n", SQUASHFS_LZO1X_999_COMP_DEFAULT); + fprintf(stderr, "\t\tOnly applies to lzo1x_999 algorithm\n"); +} + + +/* + * Helper function for lzo1x_999 compression algorithm. + * All other lzo1x_xxx compressors do not take a compression level, + * so we need to wrap lzo1x_999 to pass the compression level which + * is applicable to it + */ +int lzo1x_999_wrapper(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, + lzo_uintp compsize, lzo_voidp workspace) +{ + return lzo1x_999_compress_level(src, src_len, dst, compsize, + workspace, NULL, 0, 0, compression_level); +} + + +struct compressor lzo_comp_ops = { + .init = squashfs_lzo_init, + .compress = lzo_compress, + .uncompress = lzo_uncompress, + .options = lzo_options, + .options_post = lzo_options_post, + .dump_options = lzo_dump_options, + .extract_options = lzo_extract_options, + .display_options = lzo_display_options, + .usage = lzo_usage, + .id = LZO_COMPRESSION, + .name = "lzo", + .supported = 1 +};
diff --git a/squashfs-tools/squashfs-tools/lzo_wrapper.h b/squashfs-tools/squashfs-tools/lzo_wrapper.h new file mode 100644 index 0000000..804e53c --- /dev/null +++ b/squashfs-tools/squashfs-tools/lzo_wrapper.h
@@ -0,0 +1,78 @@ +#ifndef LZO_WRAPPER_H +#define LZO_WRAPPER_H +/* + * Squashfs + * + * Copyright (c) 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * lzo_wrapper.h + * + */ + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include <endian.h> +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +extern unsigned int inswap_le32(unsigned int); + +#define SQUASHFS_INSWAP_COMP_OPTS(s) { \ + (s)->algorithm = inswap_le32((s)->algorithm); \ + (s)->compression_level = inswap_le32((s)->compression_level); \ +} +#else +#define SQUASHFS_INSWAP_COMP_OPTS(s) +#endif + +/* Define the compression flags recognised. */ +#define SQUASHFS_LZO1X_1 0 +#define SQUASHFS_LZO1X_1_11 1 +#define SQUASHFS_LZO1X_1_12 2 +#define SQUASHFS_LZO1X_1_15 3 +#define SQUASHFS_LZO1X_999 4 + +/* Default compression level used by SQUASHFS_LZO1X_999 */ +#define SQUASHFS_LZO1X_999_COMP_DEFAULT 8 + +struct lzo_comp_opts { + int algorithm; + int compression_level; +}; + +struct lzo_algorithm { + char *name; + int size; + int (*compress) (const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp, + lzo_voidp); +}; + +struct lzo_stream { + void *workspace; + void *buffer; +}; + +#define LZO_MAX_EXPANSION(size) (size + (size / 16) + 64 + 3) + +int lzo1x_999_wrapper(const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp, + lzo_voidp); + +#endif
diff --git a/squashfs-tools/squashfs-tools/mksquashfs.c b/squashfs-tools/squashfs-tools/mksquashfs.c new file mode 100644 index 0000000..033bc8e --- /dev/null +++ b/squashfs-tools/squashfs-tools/mksquashfs.c
@@ -0,0 +1,6192 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, + * 2012, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * mksquashfs.c + */ + +#define FALSE 0 +#define TRUE 1 +#define MAX_LINE 16384 + +#include <pwd.h> +#include <grp.h> +#include <time.h> +#include <unistd.h> +#include <stdio.h> +#include <stddef.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <dirent.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <setjmp.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <pthread.h> +#include <regex.h> +#include <fnmatch.h> +#include <sys/wait.h> +#include <limits.h> +#include <ctype.h> + +#ifndef FNM_EXTMATCH /* glibc extension */ + #define FNM_EXTMATCH 0 +#endif + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#include <sys/sysctl.h> +#else +#include <endian.h> +#include <sys/sysinfo.h> +#endif + +#include "squashfs_fs.h" +#include "squashfs_swap.h" +#include "mksquashfs.h" +#include "sort.h" +#include "pseudo.h" +#include "compressor.h" +#include "xattr.h" +#include "action.h" +#include "error.h" +#include "progressbar.h" +#include "info.h" +#include "caches-queues-lists.h" +#include "read_fs.h" +#include "restore.h" +#include "process_fragments.h" + +/* ANDROID CHANGES START*/ +#ifdef ANDROID +#include "android.h" +#include "private/android_filesystem_config.h" +#include "private/canned_fs_config.h" +int android_config = FALSE; +char *context_file = NULL; +char *mount_point = NULL; +char *target_out_path = NULL; +fs_config_func_t fs_config_func = NULL; +#endif +/* ANDROID CHANGES END */ + +int delete = FALSE; +int fd; +struct squashfs_super_block sBlk; + +/* filesystem flags for building */ +int comp_opts = FALSE; +int no_xattrs = XATTR_DEF; +int noX = FALSE; +int duplicate_checking = TRUE; +int noF = FALSE; +int no_fragments = FALSE; +int always_use_fragments = FALSE; +int noI = FALSE; +int noD = FALSE; +int silent = TRUE; +int exportable = TRUE; +int sparse_files = TRUE; +int old_exclude = TRUE; +int use_regex = FALSE; +int nopad = FALSE; +int exit_on_error = FALSE; + +long long global_uid = -1, global_gid = -1; + +/* superblock attributes */ +int block_size = SQUASHFS_FILE_SIZE, block_log; +unsigned int id_count = 0; +int file_count = 0, sym_count = 0, dev_count = 0, dir_count = 0, fifo_count = 0, + sock_count = 0; + +/* write position within data section */ +long long bytes = 0, total_bytes = 0; + +/* in memory directory table - possibly compressed */ +char *directory_table = NULL; +unsigned int directory_bytes = 0, directory_size = 0, total_directory_bytes = 0; + +/* cached directory table */ +char *directory_data_cache = NULL; +unsigned int directory_cache_bytes = 0, directory_cache_size = 0; + +/* in memory inode table - possibly compressed */ +char *inode_table = NULL; +unsigned int inode_bytes = 0, inode_size = 0, total_inode_bytes = 0; + +/* cached inode table */ +char *data_cache = NULL; +unsigned int cache_bytes = 0, cache_size = 0, inode_count = 0; + +/* inode lookup table */ +squashfs_inode *inode_lookup_table = NULL; + +/* in memory directory data */ +#define I_COUNT_SIZE 128 +#define DIR_ENTRIES 32 +#define INODE_HASH_SIZE 65536 +#define INODE_HASH_MASK (INODE_HASH_SIZE - 1) +#define INODE_HASH(dev, ino) (ino & INODE_HASH_MASK) + +struct cached_dir_index { + struct squashfs_dir_index index; + char *name; +}; + +struct directory { + unsigned int start_block; + unsigned int size; + unsigned char *buff; + unsigned char *p; + unsigned int entry_count; + unsigned char *entry_count_p; + unsigned int i_count; + unsigned int i_size; + struct cached_dir_index *index; + unsigned char *index_count_p; + unsigned int inode_number; +}; + +struct inode_info *inode_info[INODE_HASH_SIZE]; + +/* hash tables used to do fast duplicate searches in duplicate check */ +struct file_info *dupl[65536]; +int dup_files = 0; + +/* exclude file handling */ +/* list of exclude dirs/files */ +struct exclude_info { + dev_t st_dev; + ino_t st_ino; +}; + +#define EXCLUDE_SIZE 8192 +int exclude = 0; +struct exclude_info *exclude_paths = NULL; +int old_excluded(char *filename, struct stat *buf); + +struct path_entry { + char *name; + regex_t *preg; + struct pathname *paths; +}; + +struct pathname { + int names; + struct path_entry *name; +}; + +struct pathnames { + int count; + struct pathname *path[0]; +}; +#define PATHS_ALLOC_SIZE 10 + +struct pathnames *paths = NULL; +struct pathname *path = NULL; +struct pathname *stickypath = NULL; +int excluded(char *name, struct pathnames *paths, struct pathnames **new); + +int fragments = 0; + +#define FRAG_SIZE 32768 + +struct squashfs_fragment_entry *fragment_table = NULL; +int fragments_outstanding = 0; + +int fragments_locked = FALSE; + +/* current inode number for directories and non directories */ +unsigned int inode_no = 1; +unsigned int root_inode_number = 0; + +/* list of source dirs/files */ +int source = 0; +char **source_path; + +/* list of root directory entries read from original filesystem */ +int old_root_entries = 0; +struct old_root_entry_info { + char *name; + struct inode_info inode; +}; +struct old_root_entry_info *old_root_entry; + +/* restore orignal filesystem state if appending to existing filesystem is + * cancelled */ +int appending = FALSE; +char *sdata_cache, *sdirectory_data_cache, *sdirectory_compressed; + +long long sbytes, stotal_bytes; + +unsigned int sinode_bytes, scache_bytes, sdirectory_bytes, + sdirectory_cache_bytes, sdirectory_compressed_bytes, + stotal_inode_bytes, stotal_directory_bytes, + sinode_count = 0, sfile_count, ssym_count, sdev_count, + sdir_count, sfifo_count, ssock_count, sdup_files; +int sfragments; +int threads; + +/* flag whether destination file is a block device */ +int block_device = FALSE; + +/* flag indicating whether files are sorted using sort list(s) */ +int sorted = FALSE; + +/* save destination file name for deleting on error */ +char *destination_file = NULL; + +/* recovery file for abnormal exit on appending */ +char *recovery_file = NULL; +int recover = TRUE; + +struct id *id_hash_table[ID_ENTRIES]; +struct id *id_table[SQUASHFS_IDS], *sid_table[SQUASHFS_IDS]; +unsigned int uid_count = 0, guid_count = 0; +unsigned int sid_count = 0, suid_count = 0, sguid_count = 0; + +struct cache *reader_buffer, *fragment_buffer, *reserve_cache; +struct cache *bwriter_buffer, *fwriter_buffer; +struct queue *to_reader, *to_deflate, *to_writer, *from_writer, + *to_frag, *locked_fragment, *to_process_frag; +struct seq_queue *to_main; +pthread_t reader_thread, writer_thread, main_thread; +pthread_t *deflator_thread, *frag_deflator_thread, *frag_thread; +pthread_t *restore_thread = NULL; +pthread_mutex_t fragment_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t pos_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t dup_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* user options that control parallelisation */ +int processors = -1; +int bwriter_size; + +/* compression operations */ +struct compressor *comp = NULL; +int compressor_opt_parsed = FALSE; +void *stream = NULL; + +/* xattr stats */ +unsigned int xattr_bytes = 0, total_xattr_bytes = 0; + +/* fragment to file mapping used when appending */ +int append_fragments = 0; +struct append_file **file_mapping; + +/* root of the in-core directory structure */ +struct dir_info *root_dir; + +static char *read_from_disk(long long start, unsigned int avail_bytes); +void add_old_root_entry(char *name, squashfs_inode inode, int inode_number, + int type); +struct file_info *duplicate(long long file_size, long long bytes, + unsigned int **block_list, long long *start, struct fragment **fragment, + struct file_buffer *file_buffer, int blocks, unsigned short checksum, + int checksum_flag); +struct dir_info *dir_scan1(char *, char *, struct pathnames *, + struct dir_ent *(_readdir)(struct dir_info *), int); +void dir_scan2(struct dir_info *dir, struct pseudo *pseudo); +void dir_scan3(struct dir_info *dir); +void dir_scan4(struct dir_info *dir); +void dir_scan5(struct dir_info *dir); +void dir_scan6(struct dir_info *dir); +void dir_scan7(squashfs_inode *inode, struct dir_info *dir_info); +struct file_info *add_non_dup(long long file_size, long long bytes, + unsigned int *block_list, long long start, struct fragment *fragment, + unsigned short checksum, unsigned short fragment_checksum, + int checksum_flag, int checksum_frag_flag); +long long generic_write_table(int, void *, int, void *, int); +void restorefs(); +struct dir_info *scan1_opendir(char *pathname, char *subpath, int depth); +void write_filesystem_tables(struct squashfs_super_block *sBlk, int nopad); +unsigned short get_checksum_mem(char *buff, int bytes); +void check_usable_phys_mem(int total_mem); + + +void prep_exit() +{ + if(restore_thread) { + if(pthread_self() == *restore_thread) { + /* + * Recursive failure when trying to restore filesystem! + * Nothing to do except to exit, otherwise we'll just + * appear to hang. The user should be able to restore + * from the recovery file (which is why it was added, in + * case of catastrophic failure in Mksquashfs) + */ + exit(1); + } else { + /* signal the restore thread to restore */ + pthread_kill(*restore_thread, SIGUSR1); + pthread_exit(NULL); + } + } else if(delete) { + if(destination_file && !block_device) + unlink(destination_file); + } else if(recovery_file) + unlink(recovery_file); +} + + +int add_overflow(int a, int b) +{ + return (INT_MAX - a) < b; +} + + +int shift_overflow(int a, int shift) +{ + return (INT_MAX >> shift) < a; +} + + +int multiply_overflow(int a, int multiplier) +{ + return (INT_MAX / multiplier) < a; +} + + +int multiply_overflowll(long long a, int multiplier) +{ + return (LLONG_MAX / multiplier) < a; +} + + +#define MKINODE(A) ((squashfs_inode)(((squashfs_inode) inode_bytes << 16) \ + + (((char *)A) - data_cache))) + + +void restorefs() +{ + ERROR("Exiting - restoring original filesystem!\n\n"); + + bytes = sbytes; + memcpy(data_cache, sdata_cache, cache_bytes = scache_bytes); + memcpy(directory_data_cache, sdirectory_data_cache, + sdirectory_cache_bytes); + directory_cache_bytes = sdirectory_cache_bytes; + inode_bytes = sinode_bytes; + directory_bytes = sdirectory_bytes; + memcpy(directory_table + directory_bytes, sdirectory_compressed, + sdirectory_compressed_bytes); + directory_bytes += sdirectory_compressed_bytes; + total_bytes = stotal_bytes; + total_inode_bytes = stotal_inode_bytes; + total_directory_bytes = stotal_directory_bytes; + inode_count = sinode_count; + file_count = sfile_count; + sym_count = ssym_count; + dev_count = sdev_count; + dir_count = sdir_count; + fifo_count = sfifo_count; + sock_count = ssock_count; + dup_files = sdup_files; + fragments = sfragments; + id_count = sid_count; + restore_xattrs(); + write_filesystem_tables(&sBlk, nopad); + exit(1); +} + + +void sighandler() +{ + EXIT_MKSQUASHFS(); +} + + +int mangle2(void *strm, char *d, char *s, int size, + int block_size, int uncompressed, int data_block) +{ + int error, c_byte = 0; + + if(!uncompressed) { + c_byte = compressor_compress(comp, strm, d, s, size, block_size, + &error); + if(c_byte == -1) + BAD_ERROR("mangle2:: %s compress failed with error " + "code %d\n", comp->name, error); + } + + if(c_byte == 0 || c_byte >= size) { + memcpy(d, s, size); + return size | (data_block ? SQUASHFS_COMPRESSED_BIT_BLOCK : + SQUASHFS_COMPRESSED_BIT); + } + + return c_byte; +} + + +int mangle(char *d, char *s, int size, int block_size, + int uncompressed, int data_block) +{ + return mangle2(stream, d, s, size, block_size, uncompressed, + data_block); +} + + +void *get_inode(int req_size) +{ + int data_space; + unsigned short c_byte; + + while(cache_bytes >= SQUASHFS_METADATA_SIZE) { + if((inode_size - inode_bytes) < + ((SQUASHFS_METADATA_SIZE << 1)) + 2) { + void *it = realloc(inode_table, inode_size + + (SQUASHFS_METADATA_SIZE << 1) + 2); + if(it == NULL) + MEM_ERROR(); + inode_table = it; + inode_size += (SQUASHFS_METADATA_SIZE << 1) + 2; + } + + c_byte = mangle(inode_table + inode_bytes + BLOCK_OFFSET, + data_cache, SQUASHFS_METADATA_SIZE, + SQUASHFS_METADATA_SIZE, noI, 0); + TRACE("Inode block @ 0x%x, size %d\n", inode_bytes, c_byte); + SQUASHFS_SWAP_SHORTS(&c_byte, inode_table + inode_bytes, 1); + inode_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET; + total_inode_bytes += SQUASHFS_METADATA_SIZE + BLOCK_OFFSET; + memmove(data_cache, data_cache + SQUASHFS_METADATA_SIZE, + cache_bytes - SQUASHFS_METADATA_SIZE); + cache_bytes -= SQUASHFS_METADATA_SIZE; + } + + data_space = (cache_size - cache_bytes); + if(data_space < req_size) { + int realloc_size = cache_size == 0 ? + ((req_size + SQUASHFS_METADATA_SIZE) & + ~(SQUASHFS_METADATA_SIZE - 1)) : req_size - + data_space; + + void *dc = realloc(data_cache, cache_size + + realloc_size); + if(dc == NULL) + MEM_ERROR(); + cache_size += realloc_size; + data_cache = dc; + } + + cache_bytes += req_size; + + return data_cache + cache_bytes - req_size; +} + + +int read_bytes(int fd, void *buff, int bytes) +{ + int res, count; + + for(count = 0; count < bytes; count += res) { + res = read(fd, buff + count, bytes - count); + if(res < 1) { + if(res == 0) + goto bytes_read; + else if(errno != EINTR) { + ERROR("Read failed because %s\n", + strerror(errno)); + return -1; + } else + res = 0; + } + } + +bytes_read: + return count; +} + + +int read_fs_bytes(int fd, long long byte, int bytes, void *buff) +{ + off_t off = byte; + int res = 1; + + TRACE("read_fs_bytes: reading from position 0x%llx, bytes %d\n", + byte, bytes); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &pos_mutex); + pthread_mutex_lock(&pos_mutex); + if(lseek(fd, off, SEEK_SET) == -1) { + ERROR("read_fs_bytes: Lseek on destination failed because %s, " + "offset=0x%llx\n", strerror(errno), off); + res = 0; + } else if(read_bytes(fd, buff, bytes) < bytes) { + ERROR("Read on destination failed\n"); + res = 0; + } + + pthread_cleanup_pop(1); + return res; +} + + +int write_bytes(int fd, void *buff, int bytes) +{ + int res, count; + + for(count = 0; count < bytes; count += res) { + res = write(fd, buff + count, bytes - count); + if(res == -1) { + if(errno != EINTR) { + ERROR("Write failed because %s\n", + strerror(errno)); + return -1; + } + res = 0; + } + } + + return 0; +} + + +void write_destination(int fd, long long byte, int bytes, void *buff) +{ + off_t off = byte; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &pos_mutex); + pthread_mutex_lock(&pos_mutex); + + if(lseek(fd, off, SEEK_SET) == -1) { + ERROR("write_destination: Lseek on destination " + "failed because %s, offset=0x%llx\n", strerror(errno), + off); + BAD_ERROR("Probably out of space on output %s\n", + block_device ? "block device" : "filesystem"); + } + + if(write_bytes(fd, buff, bytes) == -1) + BAD_ERROR("Failed to write to output %s\n", + block_device ? "block device" : "filesystem"); + + pthread_cleanup_pop(1); +} + + +long long write_inodes() +{ + unsigned short c_byte; + int avail_bytes; + char *datap = data_cache; + long long start_bytes = bytes; + + while(cache_bytes) { + if(inode_size - inode_bytes < + ((SQUASHFS_METADATA_SIZE << 1) + 2)) { + void *it = realloc(inode_table, inode_size + + ((SQUASHFS_METADATA_SIZE << 1) + 2)); + if(it == NULL) + MEM_ERROR(); + inode_size += (SQUASHFS_METADATA_SIZE << 1) + 2; + inode_table = it; + } + avail_bytes = cache_bytes > SQUASHFS_METADATA_SIZE ? + SQUASHFS_METADATA_SIZE : cache_bytes; + c_byte = mangle(inode_table + inode_bytes + BLOCK_OFFSET, datap, + avail_bytes, SQUASHFS_METADATA_SIZE, noI, 0); + TRACE("Inode block @ 0x%x, size %d\n", inode_bytes, c_byte); + SQUASHFS_SWAP_SHORTS(&c_byte, inode_table + inode_bytes, 1); + inode_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET; + total_inode_bytes += avail_bytes + BLOCK_OFFSET; + datap += avail_bytes; + cache_bytes -= avail_bytes; + } + + write_destination(fd, bytes, inode_bytes, inode_table); + bytes += inode_bytes; + + return start_bytes; +} + + +long long write_directories() +{ + unsigned short c_byte; + int avail_bytes; + char *directoryp = directory_data_cache; + long long start_bytes = bytes; + + while(directory_cache_bytes) { + if(directory_size - directory_bytes < + ((SQUASHFS_METADATA_SIZE << 1) + 2)) { + void *dt = realloc(directory_table, + directory_size + ((SQUASHFS_METADATA_SIZE << 1) + + 2)); + if(dt == NULL) + MEM_ERROR(); + directory_size += (SQUASHFS_METADATA_SIZE << 1) + 2; + directory_table = dt; + } + avail_bytes = directory_cache_bytes > SQUASHFS_METADATA_SIZE ? + SQUASHFS_METADATA_SIZE : directory_cache_bytes; + c_byte = mangle(directory_table + directory_bytes + + BLOCK_OFFSET, directoryp, avail_bytes, + SQUASHFS_METADATA_SIZE, noI, 0); + TRACE("Directory block @ 0x%x, size %d\n", directory_bytes, + c_byte); + SQUASHFS_SWAP_SHORTS(&c_byte, + directory_table + directory_bytes, 1); + directory_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + + BLOCK_OFFSET; + total_directory_bytes += avail_bytes + BLOCK_OFFSET; + directoryp += avail_bytes; + directory_cache_bytes -= avail_bytes; + } + write_destination(fd, bytes, directory_bytes, directory_table); + bytes += directory_bytes; + + return start_bytes; +} + + +long long write_id_table() +{ + unsigned int id_bytes = SQUASHFS_ID_BYTES(id_count); + unsigned int p[id_count]; + int i; + + TRACE("write_id_table: ids %d, id_bytes %d\n", id_count, id_bytes); + for(i = 0; i < id_count; i++) { + TRACE("write_id_table: id index %d, id %d", i, id_table[i]->id); + SQUASHFS_SWAP_INTS(&id_table[i]->id, p + i, 1); + } + + return generic_write_table(id_bytes, p, 0, NULL, noI); +} + + +struct id *get_id(unsigned int id) +{ + int hash = ID_HASH(id); + struct id *entry = id_hash_table[hash]; + + for(; entry; entry = entry->next) + if(entry->id == id) + break; + + return entry; +} + + +struct id *create_id(unsigned int id) +{ + int hash = ID_HASH(id); + struct id *entry = malloc(sizeof(struct id)); + if(entry == NULL) + MEM_ERROR(); + entry->id = id; + entry->index = id_count ++; + entry->flags = 0; + entry->next = id_hash_table[hash]; + id_hash_table[hash] = entry; + id_table[entry->index] = entry; + return entry; +} + + +unsigned int get_uid(unsigned int uid) +{ + struct id *entry = get_id(uid); + + if(entry == NULL) { + if(id_count == SQUASHFS_IDS) + BAD_ERROR("Out of uids!\n"); + entry = create_id(uid); + } + + if((entry->flags & ISA_UID) == 0) { + entry->flags |= ISA_UID; + uid_count ++; + } + + return entry->index; +} + + +unsigned int get_guid(unsigned int guid) +{ + struct id *entry = get_id(guid); + + if(entry == NULL) { + if(id_count == SQUASHFS_IDS) + BAD_ERROR("Out of gids!\n"); + entry = create_id(guid); + } + + if((entry->flags & ISA_GID) == 0) { + entry->flags |= ISA_GID; + guid_count ++; + } + + return entry->index; +} + + +#define ALLOC_SIZE 128 + +char *_pathname(struct dir_ent *dir_ent, char *pathname, int *size) +{ + if(pathname == NULL) { + pathname = malloc(ALLOC_SIZE); + if(pathname == NULL) + MEM_ERROR(); + } + + for(;;) { + int res = snprintf(pathname, *size, "%s/%s", + dir_ent->our_dir->pathname, + dir_ent->source_name ? : dir_ent->name); + + if(res < 0) + BAD_ERROR("snprintf failed in pathname\n"); + else if(res >= *size) { + /* + * pathname is too small to contain the result, so + * increase it and try again + */ + *size = (res + ALLOC_SIZE) & ~(ALLOC_SIZE - 1); + pathname = realloc(pathname, *size); + if(pathname == NULL) + MEM_ERROR(); + } else + break; + } + + return pathname; +} + + +char *pathname(struct dir_ent *dir_ent) +{ + static char *pathname = NULL; + static int size = ALLOC_SIZE; + + if (dir_ent->nonstandard_pathname) + return dir_ent->nonstandard_pathname; + + return pathname = _pathname(dir_ent, pathname, &size); +} + + +char *pathname_reader(struct dir_ent *dir_ent) +{ + static char *pathname = NULL; + static int size = ALLOC_SIZE; + + if (dir_ent->nonstandard_pathname) + return dir_ent->nonstandard_pathname; + + return pathname = _pathname(dir_ent, pathname, &size); +} + + +char *subpathname(struct dir_ent *dir_ent) +{ + static char *subpath = NULL; + static int size = ALLOC_SIZE; + int res; + + if(subpath == NULL) { + subpath = malloc(ALLOC_SIZE); + if(subpath == NULL) + MEM_ERROR(); + } + + for(;;) { + if(dir_ent->our_dir->subpath[0] != '\0') + res = snprintf(subpath, size, "%s/%s", + dir_ent->our_dir->subpath, dir_ent->name); + else + res = snprintf(subpath, size, "/%s", dir_ent->name); + + if(res < 0) + BAD_ERROR("snprintf failed in subpathname\n"); + else if(res >= size) { + /* + * subpath is too small to contain the result, so + * increase it and try again + */ + size = (res + ALLOC_SIZE) & ~(ALLOC_SIZE - 1); + subpath = realloc(subpath, size); + if(subpath == NULL) + MEM_ERROR(); + } else + break; + } + + return subpath; +} + + +static inline unsigned int get_inode_no(struct inode_info *inode) +{ + return inode->inode_number; +} + + +static inline unsigned int get_parent_no(struct dir_info *dir) +{ + return dir->depth ? get_inode_no(dir->dir_ent->inode) : inode_no; +} + + +int create_inode(squashfs_inode *i_no, struct dir_info *dir_info, + struct dir_ent *dir_ent, int type, long long byte_size, + long long start_block, unsigned int offset, unsigned int *block_list, + struct fragment *fragment, struct directory *dir_in, long long sparse) +{ + struct stat *buf = &dir_ent->inode->buf; + union squashfs_inode_header inode_header; + struct squashfs_base_inode_header *base = &inode_header.base; + void *inode; + char *filename = pathname(dir_ent); + int nlink = dir_ent->inode->nlink; + int xattr = read_xattrs(dir_ent); + + switch(type) { + case SQUASHFS_FILE_TYPE: + if(dir_ent->inode->nlink > 1 || + byte_size >= (1LL << 32) || + start_block >= (1LL << 32) || + sparse || IS_XATTR(xattr)) + type = SQUASHFS_LREG_TYPE; + break; + case SQUASHFS_DIR_TYPE: + if(dir_info->dir_is_ldir || IS_XATTR(xattr)) + type = SQUASHFS_LDIR_TYPE; + break; + case SQUASHFS_SYMLINK_TYPE: + if(IS_XATTR(xattr)) + type = SQUASHFS_LSYMLINK_TYPE; + break; + case SQUASHFS_BLKDEV_TYPE: + if(IS_XATTR(xattr)) + type = SQUASHFS_LBLKDEV_TYPE; + break; + case SQUASHFS_CHRDEV_TYPE: + if(IS_XATTR(xattr)) + type = SQUASHFS_LCHRDEV_TYPE; + break; + case SQUASHFS_FIFO_TYPE: + if(IS_XATTR(xattr)) + type = SQUASHFS_LFIFO_TYPE; + break; + case SQUASHFS_SOCKET_TYPE: + if(IS_XATTR(xattr)) + type = SQUASHFS_LSOCKET_TYPE; + break; + } + + base->mode = SQUASHFS_MODE(buf->st_mode); + base->uid = get_uid((unsigned int) global_uid == -1 ? + buf->st_uid : global_uid); + base->inode_type = type; + base->guid = get_guid((unsigned int) global_gid == -1 ? + buf->st_gid : global_gid); + base->mtime = buf->st_mtime; + base->inode_number = get_inode_no(dir_ent->inode); + + if(type == SQUASHFS_FILE_TYPE) { + int i; + struct squashfs_reg_inode_header *reg = &inode_header.reg; + size_t off = offsetof(struct squashfs_reg_inode_header, block_list); + + inode = get_inode(sizeof(*reg) + offset * sizeof(unsigned int)); + reg->file_size = byte_size; + reg->start_block = start_block; + reg->fragment = fragment->index; + reg->offset = fragment->offset; + SQUASHFS_SWAP_REG_INODE_HEADER(reg, inode); + SQUASHFS_SWAP_INTS(block_list, inode + off, offset); + TRACE("File inode, file_size %lld, start_block 0x%llx, blocks " + "%d, fragment %d, offset %d, size %d\n", byte_size, + start_block, offset, fragment->index, fragment->offset, + fragment->size); + for(i = 0; i < offset; i++) + TRACE("Block %d, size %d\n", i, block_list[i]); + } + else if(type == SQUASHFS_LREG_TYPE) { + int i; + struct squashfs_lreg_inode_header *reg = &inode_header.lreg; + size_t off = offsetof(struct squashfs_lreg_inode_header, block_list); + + inode = get_inode(sizeof(*reg) + offset * sizeof(unsigned int)); + reg->nlink = nlink; + reg->file_size = byte_size; + reg->start_block = start_block; + reg->fragment = fragment->index; + reg->offset = fragment->offset; + if(sparse && sparse >= byte_size) + sparse = byte_size - 1; + reg->sparse = sparse; + reg->xattr = xattr; + SQUASHFS_SWAP_LREG_INODE_HEADER(reg, inode); + SQUASHFS_SWAP_INTS(block_list, inode + off, offset); + TRACE("Long file inode, file_size %lld, start_block 0x%llx, " + "blocks %d, fragment %d, offset %d, size %d, nlink %d" + "\n", byte_size, start_block, offset, fragment->index, + fragment->offset, fragment->size, nlink); + for(i = 0; i < offset; i++) + TRACE("Block %d, size %d\n", i, block_list[i]); + } + else if(type == SQUASHFS_LDIR_TYPE) { + int i; + unsigned char *p; + struct squashfs_ldir_inode_header *dir = &inode_header.ldir; + struct cached_dir_index *index = dir_in->index; + unsigned int i_count = dir_in->i_count; + unsigned int i_size = dir_in->i_size; + + if(byte_size >= 1 << 27) + BAD_ERROR("directory greater than 2^27-1 bytes!\n"); + + inode = get_inode(sizeof(*dir) + i_size); + dir->inode_type = SQUASHFS_LDIR_TYPE; + dir->nlink = dir_ent->dir->directory_count + 2; + dir->file_size = byte_size; + dir->offset = offset; + dir->start_block = start_block; + dir->i_count = i_count; + dir->parent_inode = get_parent_no(dir_ent->our_dir); + dir->xattr = xattr; + + SQUASHFS_SWAP_LDIR_INODE_HEADER(dir, inode); + p = inode + offsetof(struct squashfs_ldir_inode_header, index); + for(i = 0; i < i_count; i++) { + SQUASHFS_SWAP_DIR_INDEX(&index[i].index, p); + p += offsetof(struct squashfs_dir_index, name); + memcpy(p, index[i].name, index[i].index.size + 1); + p += index[i].index.size + 1; + } + TRACE("Long directory inode, file_size %lld, start_block " + "0x%llx, offset 0x%x, nlink %d\n", byte_size, + start_block, offset, dir_ent->dir->directory_count + 2); + } + else if(type == SQUASHFS_DIR_TYPE) { + struct squashfs_dir_inode_header *dir = &inode_header.dir; + + inode = get_inode(sizeof(*dir)); + dir->nlink = dir_ent->dir->directory_count + 2; + dir->file_size = byte_size; + dir->offset = offset; + dir->start_block = start_block; + dir->parent_inode = get_parent_no(dir_ent->our_dir); + SQUASHFS_SWAP_DIR_INODE_HEADER(dir, inode); + TRACE("Directory inode, file_size %lld, start_block 0x%llx, " + "offset 0x%x, nlink %d\n", byte_size, start_block, + offset, dir_ent->dir->directory_count + 2); + } + else if(type == SQUASHFS_CHRDEV_TYPE || type == SQUASHFS_BLKDEV_TYPE) { + struct squashfs_dev_inode_header *dev = &inode_header.dev; + unsigned int major = major(buf->st_rdev); + unsigned int minor = minor(buf->st_rdev); + + if(major > 0xfff) { + ERROR("Major %d out of range in device node %s, " + "truncating to %d\n", major, filename, + major & 0xfff); + major &= 0xfff; + } + if(minor > 0xfffff) { + ERROR("Minor %d out of range in device node %s, " + "truncating to %d\n", minor, filename, + minor & 0xfffff); + minor &= 0xfffff; + } + inode = get_inode(sizeof(*dev)); + dev->nlink = nlink; + dev->rdev = (major << 8) | (minor & 0xff) | + ((minor & ~0xff) << 12); + SQUASHFS_SWAP_DEV_INODE_HEADER(dev, inode); + TRACE("Device inode, rdev 0x%x, nlink %d\n", dev->rdev, nlink); + } + else if(type == SQUASHFS_LCHRDEV_TYPE || type == SQUASHFS_LBLKDEV_TYPE) { + struct squashfs_ldev_inode_header *dev = &inode_header.ldev; + unsigned int major = major(buf->st_rdev); + unsigned int minor = minor(buf->st_rdev); + + if(major > 0xfff) { + ERROR("Major %d out of range in device node %s, " + "truncating to %d\n", major, filename, + major & 0xfff); + major &= 0xfff; + } + if(minor > 0xfffff) { + ERROR("Minor %d out of range in device node %s, " + "truncating to %d\n", minor, filename, + minor & 0xfffff); + minor &= 0xfffff; + } + inode = get_inode(sizeof(*dev)); + dev->nlink = nlink; + dev->rdev = (major << 8) | (minor & 0xff) | + ((minor & ~0xff) << 12); + dev->xattr = xattr; + SQUASHFS_SWAP_LDEV_INODE_HEADER(dev, inode); + TRACE("Device inode, rdev 0x%x, nlink %d\n", dev->rdev, nlink); + } + else if(type == SQUASHFS_SYMLINK_TYPE) { + struct squashfs_symlink_inode_header *symlink = &inode_header.symlink; + int byte = strlen(dir_ent->inode->symlink); + size_t off = offsetof(struct squashfs_symlink_inode_header, symlink); + + inode = get_inode(sizeof(*symlink) + byte); + symlink->nlink = nlink; + symlink->symlink_size = byte; + SQUASHFS_SWAP_SYMLINK_INODE_HEADER(symlink, inode); + strncpy(inode + off, dir_ent->inode->symlink, byte); + TRACE("Symbolic link inode, symlink_size %d, nlink %d\n", byte, + nlink); + } + else if(type == SQUASHFS_LSYMLINK_TYPE) { + struct squashfs_symlink_inode_header *symlink = &inode_header.symlink; + int byte = strlen(dir_ent->inode->symlink); + size_t off = offsetof(struct squashfs_symlink_inode_header, symlink); + + inode = get_inode(sizeof(*symlink) + byte + + sizeof(unsigned int)); + symlink->nlink = nlink; + symlink->symlink_size = byte; + SQUASHFS_SWAP_SYMLINK_INODE_HEADER(symlink, inode); + strncpy(inode + off, dir_ent->inode->symlink, byte); + SQUASHFS_SWAP_INTS(&xattr, inode + off + byte, 1); + TRACE("Symbolic link inode, symlink_size %d, nlink %d\n", byte, + nlink); + } + else if(type == SQUASHFS_FIFO_TYPE || type == SQUASHFS_SOCKET_TYPE) { + struct squashfs_ipc_inode_header *ipc = &inode_header.ipc; + + inode = get_inode(sizeof(*ipc)); + ipc->nlink = nlink; + SQUASHFS_SWAP_IPC_INODE_HEADER(ipc, inode); + TRACE("ipc inode, type %s, nlink %d\n", type == + SQUASHFS_FIFO_TYPE ? "fifo" : "socket", nlink); + } + else if(type == SQUASHFS_LFIFO_TYPE || type == SQUASHFS_LSOCKET_TYPE) { + struct squashfs_lipc_inode_header *ipc = &inode_header.lipc; + + inode = get_inode(sizeof(*ipc)); + ipc->nlink = nlink; + ipc->xattr = xattr; + SQUASHFS_SWAP_LIPC_INODE_HEADER(ipc, inode); + TRACE("ipc inode, type %s, nlink %d\n", type == + SQUASHFS_FIFO_TYPE ? "fifo" : "socket", nlink); + } else + BAD_ERROR("Unrecognised inode %d in create_inode\n", type); + + *i_no = MKINODE(inode); + inode_count ++; + + TRACE("Created inode 0x%llx, type %d, uid %d, guid %d\n", *i_no, type, + base->uid, base->guid); + + return TRUE; +} + + +void add_dir(squashfs_inode inode, unsigned int inode_number, char *name, + int type, struct directory *dir) +{ + unsigned char *buff; + struct squashfs_dir_entry idir; + unsigned int start_block = inode >> 16; + unsigned int offset = inode & 0xffff; + unsigned int size = strlen(name); + size_t name_off = offsetof(struct squashfs_dir_entry, name); + + if(size > SQUASHFS_NAME_LEN) { + size = SQUASHFS_NAME_LEN; + ERROR("Filename is greater than %d characters, truncating! ..." + "\n", SQUASHFS_NAME_LEN); + } + + if(dir->p + sizeof(struct squashfs_dir_entry) + size + + sizeof(struct squashfs_dir_header) + >= dir->buff + dir->size) { + buff = realloc(dir->buff, dir->size += SQUASHFS_METADATA_SIZE); + if(buff == NULL) + MEM_ERROR(); + + dir->p = (dir->p - dir->buff) + buff; + if(dir->entry_count_p) + dir->entry_count_p = (dir->entry_count_p - dir->buff + + buff); + dir->index_count_p = dir->index_count_p - dir->buff + buff; + dir->buff = buff; + } + + if(dir->entry_count == 256 || start_block != dir->start_block || + ((dir->entry_count_p != NULL) && + ((dir->p + sizeof(struct squashfs_dir_entry) + size - + dir->index_count_p) > SQUASHFS_METADATA_SIZE)) || + ((long long) inode_number - dir->inode_number) > 32767 + || ((long long) inode_number - dir->inode_number) + < -32768) { + if(dir->entry_count_p) { + struct squashfs_dir_header dir_header; + + if((dir->p + sizeof(struct squashfs_dir_entry) + size - + dir->index_count_p) > + SQUASHFS_METADATA_SIZE) { + if(dir->i_count % I_COUNT_SIZE == 0) { + dir->index = realloc(dir->index, + (dir->i_count + I_COUNT_SIZE) * + sizeof(struct cached_dir_index)); + if(dir->index == NULL) + MEM_ERROR(); + } + dir->index[dir->i_count].index.index = + dir->p - dir->buff; + dir->index[dir->i_count].index.size = size - 1; + dir->index[dir->i_count++].name = name; + dir->i_size += sizeof(struct squashfs_dir_index) + + size; + dir->index_count_p = dir->p; + } + + dir_header.count = dir->entry_count - 1; + dir_header.start_block = dir->start_block; + dir_header.inode_number = dir->inode_number; + SQUASHFS_SWAP_DIR_HEADER(&dir_header, + dir->entry_count_p); + + } + + + dir->entry_count_p = dir->p; + dir->start_block = start_block; + dir->entry_count = 0; + dir->inode_number = inode_number; + dir->p += sizeof(struct squashfs_dir_header); + } + + idir.offset = offset; + idir.type = type; + idir.size = size - 1; + idir.inode_number = ((long long) inode_number - dir->inode_number); + SQUASHFS_SWAP_DIR_ENTRY(&idir, dir->p); + strncpy((char *) dir->p + name_off, name, size); + dir->p += sizeof(struct squashfs_dir_entry) + size; + dir->entry_count ++; +} + + +void write_dir(squashfs_inode *inode, struct dir_info *dir_info, + struct directory *dir) +{ + unsigned int dir_size = dir->p - dir->buff; + int data_space = directory_cache_size - directory_cache_bytes; + unsigned int directory_block, directory_offset, i_count, index; + unsigned short c_byte; + + if(data_space < dir_size) { + int realloc_size = directory_cache_size == 0 ? + ((dir_size + SQUASHFS_METADATA_SIZE) & + ~(SQUASHFS_METADATA_SIZE - 1)) : dir_size - data_space; + + void *dc = realloc(directory_data_cache, + directory_cache_size + realloc_size); + if(dc == NULL) + MEM_ERROR(); + directory_cache_size += realloc_size; + directory_data_cache = dc; + } + + if(dir_size) { + struct squashfs_dir_header dir_header; + + dir_header.count = dir->entry_count - 1; + dir_header.start_block = dir->start_block; + dir_header.inode_number = dir->inode_number; + SQUASHFS_SWAP_DIR_HEADER(&dir_header, dir->entry_count_p); + memcpy(directory_data_cache + directory_cache_bytes, dir->buff, + dir_size); + } + directory_offset = directory_cache_bytes; + directory_block = directory_bytes; + directory_cache_bytes += dir_size; + i_count = 0; + index = SQUASHFS_METADATA_SIZE - directory_offset; + + while(1) { + while(i_count < dir->i_count && + dir->index[i_count].index.index < index) + dir->index[i_count++].index.start_block = + directory_bytes; + index += SQUASHFS_METADATA_SIZE; + + if(directory_cache_bytes < SQUASHFS_METADATA_SIZE) + break; + + if((directory_size - directory_bytes) < + ((SQUASHFS_METADATA_SIZE << 1) + 2)) { + void *dt = realloc(directory_table, + directory_size + (SQUASHFS_METADATA_SIZE << 1) + + 2); + if(dt == NULL) + MEM_ERROR(); + directory_size += SQUASHFS_METADATA_SIZE << 1; + directory_table = dt; + } + + c_byte = mangle(directory_table + directory_bytes + + BLOCK_OFFSET, directory_data_cache, + SQUASHFS_METADATA_SIZE, SQUASHFS_METADATA_SIZE, + noI, 0); + TRACE("Directory block @ 0x%x, size %d\n", directory_bytes, + c_byte); + SQUASHFS_SWAP_SHORTS(&c_byte, + directory_table + directory_bytes, 1); + directory_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + + BLOCK_OFFSET; + total_directory_bytes += SQUASHFS_METADATA_SIZE + BLOCK_OFFSET; + memmove(directory_data_cache, directory_data_cache + + SQUASHFS_METADATA_SIZE, directory_cache_bytes - + SQUASHFS_METADATA_SIZE); + directory_cache_bytes -= SQUASHFS_METADATA_SIZE; + } + + create_inode(inode, dir_info, dir_info->dir_ent, SQUASHFS_DIR_TYPE, + dir_size + 3, directory_block, directory_offset, NULL, NULL, + dir, 0); + +#ifdef SQUASHFS_TRACE + { + unsigned char *dirp; + int count; + + TRACE("Directory contents of inode 0x%llx\n", *inode); + dirp = dir->buff; + while(dirp < dir->p) { + char buffer[SQUASHFS_NAME_LEN + 1]; + struct squashfs_dir_entry idir, *idirp; + struct squashfs_dir_header dirh; + SQUASHFS_SWAP_DIR_HEADER((struct squashfs_dir_header *) dirp, + &dirh); + count = dirh.count + 1; + dirp += sizeof(struct squashfs_dir_header); + + TRACE("\tStart block 0x%x, count %d\n", + dirh.start_block, count); + + while(count--) { + idirp = (struct squashfs_dir_entry *) dirp; + SQUASHFS_SWAP_DIR_ENTRY(idirp, &idir); + strncpy(buffer, idirp->name, idir.size + 1); + buffer[idir.size + 1] = '\0'; + TRACE("\t\tname %s, inode offset 0x%x, type " + "%d\n", buffer, idir.offset, idir.type); + dirp += sizeof(struct squashfs_dir_entry) + idir.size + + 1; + } + } + } +#endif + dir_count ++; +} + + +static struct file_buffer *get_fragment(struct fragment *fragment) +{ + struct squashfs_fragment_entry *disk_fragment; + struct file_buffer *buffer, *compressed_buffer; + long long start_block; + int res, size, index = fragment->index; + char locked; + + /* + * Lookup fragment block in cache. + * If the fragment block doesn't exist, then get the compressed version + * from the writer cache or off disk, and decompress it. + * + * This routine has two things which complicate the code: + * + * 1. Multiple threads can simultaneously lookup/create the + * same buffer. This means a buffer needs to be "locked" + * when it is being filled in, to prevent other threads from + * using it when it is not ready. This is because we now do + * fragment duplicate checking in parallel. + * 2. We have two caches which need to be checked for the + * presence of fragment blocks: the normal fragment cache + * and a "reserve" cache. The reserve cache is used to + * prevent an unnecessary pipeline stall when the fragment cache + * is full of fragments waiting to be compressed. + */ + + if(fragment->index == SQUASHFS_INVALID_FRAG) + return NULL; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + pthread_mutex_lock(&dup_mutex); + +again: + buffer = cache_lookup_nowait(fragment_buffer, index, &locked); + if(buffer) { + pthread_mutex_unlock(&dup_mutex); + if(locked) + /* got a buffer being filled in. Wait for it */ + cache_wait_unlock(buffer); + goto finished; + } + + /* not in fragment cache, is it in the reserve cache? */ + buffer = cache_lookup_nowait(reserve_cache, index, &locked); + if(buffer) { + pthread_mutex_unlock(&dup_mutex); + if(locked) + /* got a buffer being filled in. Wait for it */ + cache_wait_unlock(buffer); + goto finished; + } + + /* in neither cache, try to get it from the fragment cache */ + buffer = cache_get_nowait(fragment_buffer, index); + if(!buffer) { + /* + * no room, get it from the reserve cache, this is + * dimensioned so it will always have space (no more than + * processors + 1 can have an outstanding reserve buffer) + */ + buffer = cache_get_nowait(reserve_cache, index); + if(!buffer) { + /* failsafe */ + ERROR("no space in reserve cache\n"); + goto again; + } + } + + pthread_mutex_unlock(&dup_mutex); + + compressed_buffer = cache_lookup(fwriter_buffer, index); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); + pthread_mutex_lock(&fragment_mutex); + disk_fragment = &fragment_table[index]; + size = SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment->size); + start_block = disk_fragment->start_block; + pthread_cleanup_pop(1); + + if(SQUASHFS_COMPRESSED_BLOCK(disk_fragment->size)) { + int error; + char *data; + + if(compressed_buffer) + data = compressed_buffer->data; + else { + data = read_from_disk(start_block, size); + if(data == NULL) { + ERROR("Failed to read fragment from output" + " filesystem\n"); + BAD_ERROR("Output filesystem corrupted?\n"); + } + } + + res = compressor_uncompress(comp, buffer->data, data, size, + block_size, &error); + if(res == -1) + BAD_ERROR("%s uncompress failed with error code %d\n", + comp->name, error); + } else if(compressed_buffer) + memcpy(buffer->data, compressed_buffer->data, size); + else { + res = read_fs_bytes(fd, start_block, size, buffer->data); + if(res == 0) { + ERROR("Failed to read fragment from output " + "filesystem\n"); + BAD_ERROR("Output filesystem corrupted?\n"); + } + } + + cache_unlock(buffer); + cache_block_put(compressed_buffer); + +finished: + pthread_cleanup_pop(0); + + return buffer; +} + + +unsigned short get_fragment_checksum(struct file_info *file) +{ + struct file_buffer *frag_buffer; + struct append_file *append; + int res, index = file->fragment->index; + unsigned short checksum; + + if(index == SQUASHFS_INVALID_FRAG) + return 0; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + pthread_mutex_lock(&dup_mutex); + res = file->have_frag_checksum; + checksum = file->fragment_checksum; + pthread_cleanup_pop(1); + + if(res) + return checksum; + + frag_buffer = get_fragment(file->fragment); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + + for(append = file_mapping[index]; append; append = append->next) { + int offset = append->file->fragment->offset; + int size = append->file->fragment->size; + unsigned short cksum = + get_checksum_mem(frag_buffer->data + offset, size); + + if(file == append->file) + checksum = cksum; + + pthread_mutex_lock(&dup_mutex); + append->file->fragment_checksum = cksum; + append->file->have_frag_checksum = TRUE; + pthread_mutex_unlock(&dup_mutex); + } + + cache_block_put(frag_buffer); + pthread_cleanup_pop(0); + + return checksum; +} + + +void lock_fragments() +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); + pthread_mutex_lock(&fragment_mutex); + fragments_locked = TRUE; + pthread_cleanup_pop(1); +} + + +void unlock_fragments() +{ + int frg, size; + struct file_buffer *write_buffer; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); + pthread_mutex_lock(&fragment_mutex); + + /* + * Note queue_empty() is inherently racy with respect to concurrent + * queue get and pushes. We avoid this because we're holding the + * fragment_mutex which ensures no other threads can be using the + * queue at this time. + */ + while(!queue_empty(locked_fragment)) { + write_buffer = queue_get(locked_fragment); + frg = write_buffer->block; + size = SQUASHFS_COMPRESSED_SIZE_BLOCK(fragment_table[frg].size); + fragment_table[frg].start_block = bytes; + write_buffer->block = bytes; + bytes += size; + fragments_outstanding --; + queue_put(to_writer, write_buffer); + TRACE("fragment_locked writing fragment %d, compressed size %d" + "\n", frg, size); + } + fragments_locked = FALSE; + pthread_cleanup_pop(1); +} + +/* Called with the fragment_mutex locked */ +void add_pending_fragment(struct file_buffer *write_buffer, int c_byte, + int fragment) +{ + fragment_table[fragment].size = c_byte; + write_buffer->block = fragment; + + queue_put(locked_fragment, write_buffer); +} + + +void write_fragment(struct file_buffer *fragment) +{ + if(fragment == NULL) + return; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); + pthread_mutex_lock(&fragment_mutex); + fragment_table[fragment->block].unused = 0; + fragments_outstanding ++; + queue_put(to_frag, fragment); + pthread_cleanup_pop(1); +} + + +struct file_buffer *allocate_fragment() +{ + struct file_buffer *fragment = cache_get(fragment_buffer, fragments); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); + pthread_mutex_lock(&fragment_mutex); + + if(fragments % FRAG_SIZE == 0) { + void *ft = realloc(fragment_table, (fragments + + FRAG_SIZE) * sizeof(struct squashfs_fragment_entry)); + if(ft == NULL) + MEM_ERROR(); + fragment_table = ft; + } + + fragment->size = 0; + fragment->block = fragments ++; + + pthread_cleanup_pop(1); + + return fragment; +} + + +static struct fragment empty_fragment = {SQUASHFS_INVALID_FRAG, 0, 0}; + + +void free_fragment(struct fragment *fragment) +{ + if(fragment != &empty_fragment) + free(fragment); +} + + +struct fragment *get_and_fill_fragment(struct file_buffer *file_buffer, + struct dir_ent *dir_ent) +{ + struct fragment *ffrg; + struct file_buffer **fragment; + + if(file_buffer == NULL || file_buffer->size == 0) + return &empty_fragment; + + fragment = eval_frag_actions(root_dir, dir_ent); + + if((*fragment) && (*fragment)->size + file_buffer->size > block_size) { + write_fragment(*fragment); + *fragment = NULL; + } + + ffrg = malloc(sizeof(struct fragment)); + if(ffrg == NULL) + MEM_ERROR(); + + if(*fragment == NULL) + *fragment = allocate_fragment(); + + ffrg->index = (*fragment)->block; + ffrg->offset = (*fragment)->size; + ffrg->size = file_buffer->size; + memcpy((*fragment)->data + (*fragment)->size, file_buffer->data, + file_buffer->size); + (*fragment)->size += file_buffer->size; + + return ffrg; +} + + +long long generic_write_table(int length, void *buffer, int length2, + void *buffer2, int uncompressed) +{ + int meta_blocks = (length + SQUASHFS_METADATA_SIZE - 1) / + SQUASHFS_METADATA_SIZE; + long long *list, start_bytes; + int compressed_size, i, list_size = meta_blocks * sizeof(long long); + unsigned short c_byte; + char cbuffer[(SQUASHFS_METADATA_SIZE << 2) + 2]; + +#ifdef SQUASHFS_TRACE + long long obytes = bytes; + int olength = length; +#endif + + list = malloc(list_size); + if(list == NULL) + MEM_ERROR(); + + for(i = 0; i < meta_blocks; i++) { + int avail_bytes = length > SQUASHFS_METADATA_SIZE ? + SQUASHFS_METADATA_SIZE : length; + c_byte = mangle(cbuffer + BLOCK_OFFSET, buffer + i * + SQUASHFS_METADATA_SIZE , avail_bytes, + SQUASHFS_METADATA_SIZE, uncompressed, 0); + SQUASHFS_SWAP_SHORTS(&c_byte, cbuffer, 1); + list[i] = bytes; + compressed_size = SQUASHFS_COMPRESSED_SIZE(c_byte) + + BLOCK_OFFSET; + TRACE("block %d @ 0x%llx, compressed size %d\n", i, bytes, + compressed_size); + write_destination(fd, bytes, compressed_size, cbuffer); + bytes += compressed_size; + total_bytes += avail_bytes; + length -= avail_bytes; + } + + start_bytes = bytes; + if(length2) { + write_destination(fd, bytes, length2, buffer2); + bytes += length2; + total_bytes += length2; + } + + SQUASHFS_INSWAP_LONG_LONGS(list, meta_blocks); + write_destination(fd, bytes, list_size, list); + bytes += list_size; + total_bytes += list_size; + + TRACE("generic_write_table: total uncompressed %d compressed %lld\n", + olength, bytes - obytes); + + free(list); + + return start_bytes; +} + + +long long write_fragment_table() +{ + unsigned int frag_bytes = SQUASHFS_FRAGMENT_BYTES(fragments); + int i; + + TRACE("write_fragment_table: fragments %d, frag_bytes %d\n", fragments, + frag_bytes); + for(i = 0; i < fragments; i++) { + TRACE("write_fragment_table: fragment %d, start_block 0x%llx, " + "size %d\n", i, fragment_table[i].start_block, + fragment_table[i].size); + SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]); + } + + return generic_write_table(frag_bytes, fragment_table, 0, NULL, noF); +} + + +char read_from_file_buffer[SQUASHFS_FILE_MAX_SIZE]; +static char *read_from_disk(long long start, unsigned int avail_bytes) +{ + int res; + + res = read_fs_bytes(fd, start, avail_bytes, read_from_file_buffer); + if(res == 0) + return NULL; + + return read_from_file_buffer; +} + + +char read_from_file_buffer2[SQUASHFS_FILE_MAX_SIZE]; +char *read_from_disk2(long long start, unsigned int avail_bytes) +{ + int res; + + res = read_fs_bytes(fd, start, avail_bytes, read_from_file_buffer2); + if(res == 0) + return NULL; + + return read_from_file_buffer2; +} + + +/* + * Compute 16 bit BSD checksum over the data + */ +unsigned short get_checksum(char *buff, int bytes, unsigned short chksum) +{ + unsigned char *b = (unsigned char *) buff; + + while(bytes --) { + chksum = (chksum & 1) ? (chksum >> 1) | 0x8000 : chksum >> 1; + chksum += *b++; + } + + return chksum; +} + + +unsigned short get_checksum_disk(long long start, long long l, + unsigned int *blocks) +{ + unsigned short chksum = 0; + unsigned int bytes; + struct file_buffer *write_buffer; + int i; + + for(i = 0; l; i++) { + bytes = SQUASHFS_COMPRESSED_SIZE_BLOCK(blocks[i]); + if(bytes == 0) /* sparse block */ + continue; + write_buffer = cache_lookup(bwriter_buffer, start); + if(write_buffer) { + chksum = get_checksum(write_buffer->data, bytes, + chksum); + cache_block_put(write_buffer); + } else { + void *data = read_from_disk(start, bytes); + if(data == NULL) { + ERROR("Failed to checksum data from output" + " filesystem\n"); + BAD_ERROR("Output filesystem corrupted?\n"); + } + + chksum = get_checksum(data, bytes, chksum); + } + + l -= bytes; + start += bytes; + } + + return chksum; +} + + +unsigned short get_checksum_mem(char *buff, int bytes) +{ + return get_checksum(buff, bytes, 0); +} + + +unsigned short get_checksum_mem_buffer(struct file_buffer *file_buffer) +{ + if(file_buffer == NULL) + return 0; + else + return get_checksum(file_buffer->data, file_buffer->size, 0); +} + + +#define DUP_HASH(a) (a & 0xffff) +void add_file(long long start, long long file_size, long long file_bytes, + unsigned int *block_listp, int blocks, unsigned int fragment, + int offset, int bytes) +{ + struct fragment *frg; + unsigned int *block_list = block_listp; + struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)]; + struct append_file *append_file; + struct file_info *file; + + if(!duplicate_checking || file_size == 0) + return; + + for(; dupl_ptr; dupl_ptr = dupl_ptr->next) { + if(file_size != dupl_ptr->file_size) + continue; + if(blocks != 0 && start != dupl_ptr->start) + continue; + if(fragment != dupl_ptr->fragment->index) + continue; + if(fragment != SQUASHFS_INVALID_FRAG && (offset != + dupl_ptr->fragment->offset || bytes != + dupl_ptr->fragment->size)) + continue; + return; + } + + frg = malloc(sizeof(struct fragment)); + if(frg == NULL) + MEM_ERROR(); + + frg->index = fragment; + frg->offset = offset; + frg->size = bytes; + + file = add_non_dup(file_size, file_bytes, block_list, start, frg, 0, 0, + FALSE, FALSE); + + if(fragment == SQUASHFS_INVALID_FRAG) + return; + + append_file = malloc(sizeof(struct append_file)); + if(append_file == NULL) + MEM_ERROR(); + + append_file->file = file; + append_file->next = file_mapping[fragment]; + file_mapping[fragment] = append_file; +} + + +int pre_duplicate(long long file_size) +{ + struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)]; + + for(; dupl_ptr; dupl_ptr = dupl_ptr->next) + if(dupl_ptr->file_size == file_size) + return TRUE; + + return FALSE; +} + + +struct file_info *add_non_dup(long long file_size, long long bytes, + unsigned int *block_list, long long start, struct fragment *fragment, + unsigned short checksum, unsigned short fragment_checksum, + int checksum_flag, int checksum_frag_flag) +{ + struct file_info *dupl_ptr = malloc(sizeof(struct file_info)); + + if(dupl_ptr == NULL) + MEM_ERROR(); + + dupl_ptr->file_size = file_size; + dupl_ptr->bytes = bytes; + dupl_ptr->block_list = block_list; + dupl_ptr->start = start; + dupl_ptr->fragment = fragment; + dupl_ptr->checksum = checksum; + dupl_ptr->fragment_checksum = fragment_checksum; + dupl_ptr->have_frag_checksum = checksum_frag_flag; + dupl_ptr->have_checksum = checksum_flag; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + pthread_mutex_lock(&dup_mutex); + dupl_ptr->next = dupl[DUP_HASH(file_size)]; + dupl[DUP_HASH(file_size)] = dupl_ptr; + dup_files ++; + pthread_cleanup_pop(1); + + return dupl_ptr; +} + + +struct fragment *frag_duplicate(struct file_buffer *file_buffer, char *dont_put) +{ + struct file_info *dupl_ptr; + struct file_buffer *buffer; + struct file_info *dupl_start = file_buffer->dupl_start; + long long file_size = file_buffer->file_size; + unsigned short checksum = file_buffer->checksum; + int res; + + if(file_buffer->duplicate) { + TRACE("Found duplicate file, fragment %d, size %d, offset %d, " + "checksum 0x%x\n", dupl_start->fragment->index, + file_size, dupl_start->fragment->offset, checksum); + *dont_put = TRUE; + return dupl_start->fragment; + } else { + *dont_put = FALSE; + dupl_ptr = dupl[DUP_HASH(file_size)]; + } + + for(; dupl_ptr && dupl_ptr != dupl_start; dupl_ptr = dupl_ptr->next) { + if(file_size == dupl_ptr->file_size && file_size == + dupl_ptr->fragment->size) { + if(get_fragment_checksum(dupl_ptr) == checksum) { + buffer = get_fragment(dupl_ptr->fragment); + res = memcmp(file_buffer->data, buffer->data + + dupl_ptr->fragment->offset, file_size); + cache_block_put(buffer); + if(res == 0) + break; + } + } + } + + if(!dupl_ptr || dupl_ptr == dupl_start) + return NULL; + + TRACE("Found duplicate file, fragment %d, size %d, offset %d, " + "checksum 0x%x\n", dupl_ptr->fragment->index, file_size, + dupl_ptr->fragment->offset, checksum); + + return dupl_ptr->fragment; +} + + +struct file_info *duplicate(long long file_size, long long bytes, + unsigned int **block_list, long long *start, struct fragment **fragment, + struct file_buffer *file_buffer, int blocks, unsigned short checksum, + int checksum_flag) +{ + struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)]; + int frag_bytes = file_buffer ? file_buffer->size : 0; + unsigned short fragment_checksum = file_buffer ? + file_buffer->checksum : 0; + + for(; dupl_ptr; dupl_ptr = dupl_ptr->next) + if(file_size == dupl_ptr->file_size && bytes == dupl_ptr->bytes + && frag_bytes == dupl_ptr->fragment->size) { + long long target_start, dup_start = dupl_ptr->start; + int block; + + if(memcmp(*block_list, dupl_ptr->block_list, blocks * + sizeof(unsigned int)) != 0) + continue; + + if(checksum_flag == FALSE) { + checksum = get_checksum_disk(*start, bytes, + *block_list); + checksum_flag = TRUE; + } + + if(!dupl_ptr->have_checksum) { + dupl_ptr->checksum = + get_checksum_disk(dupl_ptr->start, + dupl_ptr->bytes, dupl_ptr->block_list); + dupl_ptr->have_checksum = TRUE; + } + + if(checksum != dupl_ptr->checksum || + fragment_checksum != + get_fragment_checksum(dupl_ptr)) + continue; + + target_start = *start; + for(block = 0; block < blocks; block ++) { + int size = SQUASHFS_COMPRESSED_SIZE_BLOCK + ((*block_list)[block]); + struct file_buffer *target_buffer = NULL; + struct file_buffer *dup_buffer = NULL; + char *target_data, *dup_data; + int res; + + if(size == 0) + continue; + target_buffer = cache_lookup(bwriter_buffer, + target_start); + if(target_buffer) + target_data = target_buffer->data; + else { + target_data = + read_from_disk(target_start, + size); + if(target_data == NULL) { + ERROR("Failed to read data from" + " output filesystem\n"); + BAD_ERROR("Output filesystem" + " corrupted?\n"); + } + } + + dup_buffer = cache_lookup(bwriter_buffer, + dup_start); + if(dup_buffer) + dup_data = dup_buffer->data; + else { + dup_data = read_from_disk2(dup_start, + size); + if(dup_data == NULL) { + ERROR("Failed to read data from" + " output filesystem\n"); + BAD_ERROR("Output filesystem" + " corrupted?\n"); + } + } + + res = memcmp(target_data, dup_data, size); + cache_block_put(target_buffer); + cache_block_put(dup_buffer); + if(res != 0) + break; + target_start += size; + dup_start += size; + } + if(block == blocks) { + struct file_buffer *frag_buffer = + get_fragment(dupl_ptr->fragment); + + if(frag_bytes == 0 || + memcmp(file_buffer->data, + frag_buffer->data + + dupl_ptr->fragment->offset, + frag_bytes) == 0) { + TRACE("Found duplicate file, start " + "0x%llx, size %lld, checksum " + "0x%x, fragment %d, size %d, " + "offset %d, checksum 0x%x\n", + dupl_ptr->start, + dupl_ptr->bytes, + dupl_ptr->checksum, + dupl_ptr->fragment->index, + frag_bytes, + dupl_ptr->fragment->offset, + fragment_checksum); + *block_list = dupl_ptr->block_list; + *start = dupl_ptr->start; + *fragment = dupl_ptr->fragment; + cache_block_put(frag_buffer); + return 0; + } + cache_block_put(frag_buffer); + } + } + + + return add_non_dup(file_size, bytes, *block_list, *start, *fragment, + checksum, fragment_checksum, checksum_flag, TRUE); +} + + +static inline int is_fragment(struct inode_info *inode) +{ + off_t file_size = inode->buf.st_size; + + /* + * If this block is to be compressed differently to the + * fragment compression then it cannot be a fragment + */ + if(inode->noF != noF) + return FALSE; + + return !inode->no_fragments && file_size && (file_size < block_size || + (inode->always_use_fragments && file_size & (block_size - 1))); +} + + +void put_file_buffer(struct file_buffer *file_buffer) +{ + /* + * Decide where to send the file buffer: + * - compressible non-fragment blocks go to the deflate threads, + * - fragments go to the process fragment threads, + * - all others go directly to the main thread + */ + if(file_buffer->error) { + file_buffer->fragment = 0; + seq_queue_put(to_main, file_buffer); + } else if (file_buffer->file_size == 0) + seq_queue_put(to_main, file_buffer); + else if(file_buffer->fragment) + queue_put(to_process_frag, file_buffer); + else + queue_put(to_deflate, file_buffer); +} + + +static int seq = 0; +void reader_read_process(struct dir_ent *dir_ent) +{ + long long bytes = 0; + struct inode_info *inode = dir_ent->inode; + struct file_buffer *prev_buffer = NULL, *file_buffer; + int status, byte, res, child; + int file = pseudo_exec_file(get_pseudo_file(inode->pseudo_id), &child); + + if(!file) { + file_buffer = cache_get_nohash(reader_buffer); + file_buffer->sequence = seq ++; + goto read_err; + } + + while(1) { + file_buffer = cache_get_nohash(reader_buffer); + file_buffer->sequence = seq ++; + file_buffer->noD = inode->noD; + + byte = read_bytes(file, file_buffer->data, block_size); + if(byte == -1) + goto read_err2; + + file_buffer->size = byte; + file_buffer->file_size = -1; + file_buffer->error = FALSE; + file_buffer->fragment = FALSE; + bytes += byte; + + if(byte == 0) + break; + + /* + * Update progress bar size. This is done + * on every block rather than waiting for all blocks to be + * read incase write_file_process() is running in parallel + * with this. Otherwise the current progress bar position + * may get ahead of the progress bar size. + */ + progress_bar_size(1); + + if(prev_buffer) + put_file_buffer(prev_buffer); + prev_buffer = file_buffer; + } + + /* + * Update inode file size now that the size of the dynamic pseudo file + * is known. This is needed for the -info option. + */ + inode->buf.st_size = bytes; + + res = waitpid(child, &status, 0); + close(file); + + if(res == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) + goto read_err; + + if(prev_buffer == NULL) + prev_buffer = file_buffer; + else { + cache_block_put(file_buffer); + seq --; + } + prev_buffer->file_size = bytes; + prev_buffer->fragment = is_fragment(inode); + put_file_buffer(prev_buffer); + + return; + +read_err2: + close(file); +read_err: + if(prev_buffer) { + cache_block_put(file_buffer); + seq --; + file_buffer = prev_buffer; + } + file_buffer->error = TRUE; + put_file_buffer(file_buffer); +} + + +void reader_read_file(struct dir_ent *dir_ent) +{ + struct stat *buf = &dir_ent->inode->buf, buf2; + struct file_buffer *file_buffer; + int blocks, file, res; + long long bytes, read_size; + struct inode_info *inode = dir_ent->inode; + + if(inode->read) + return; + + inode->read = TRUE; +again: + bytes = 0; + read_size = buf->st_size; + blocks = (read_size + block_size - 1) >> block_log; + + file = open(pathname_reader(dir_ent), O_RDONLY); + if(file == -1) { + file_buffer = cache_get_nohash(reader_buffer); + file_buffer->sequence = seq ++; + goto read_err2; + } + + do { + file_buffer = cache_get_nohash(reader_buffer); + file_buffer->file_size = read_size; + file_buffer->sequence = seq ++; + file_buffer->noD = inode->noD; + file_buffer->error = FALSE; + + /* + * Always try to read block_size bytes from the file rather + * than expected bytes (which will be less than the block_size + * at the file tail) to check that the file hasn't grown + * since being stated. If it is longer (or shorter) than + * expected, then restat, and try again. Note the special + * case where the file is an exact multiple of the block_size + * is dealt with later. + */ + file_buffer->size = read_bytes(file, file_buffer->data, + block_size); + if(file_buffer->size == -1) + goto read_err; + + bytes += file_buffer->size; + + if(blocks > 1) { + /* non-tail block should be exactly block_size */ + if(file_buffer->size < block_size) + goto restat; + + file_buffer->fragment = FALSE; + put_file_buffer(file_buffer); + } + } while(-- blocks > 0); + + /* Overall size including tail should match */ + if(read_size != bytes) + goto restat; + + if(read_size && read_size % block_size == 0) { + /* + * Special case where we've not tried to read past the end of + * the file. We expect to get EOF, i.e. the file isn't larger + * than we expect. + */ + char buffer; + int res; + + res = read_bytes(file, &buffer, 1); + if(res == -1) + goto read_err; + + if(res != 0) + goto restat; + } + + file_buffer->fragment = is_fragment(inode); + put_file_buffer(file_buffer); + + close(file); + + return; + +restat: + res = fstat(file, &buf2); + if(res == -1) { + ERROR("Cannot stat dir/file %s because %s\n", + pathname_reader(dir_ent), strerror(errno)); + goto read_err; + } + + if(read_size != buf2.st_size) { + close(file); + memcpy(buf, &buf2, sizeof(struct stat)); + file_buffer->error = 2; + put_file_buffer(file_buffer); + goto again; + } +read_err: + close(file); +read_err2: + file_buffer->error = TRUE; + put_file_buffer(file_buffer); +} + + +void reader_scan(struct dir_info *dir) { + struct dir_ent *dir_ent = dir->list; + + for(; dir_ent; dir_ent = dir_ent->next) { + struct stat *buf = &dir_ent->inode->buf; + if(dir_ent->inode->root_entry) + continue; + + if(IS_PSEUDO_PROCESS(dir_ent->inode)) { + reader_read_process(dir_ent); + continue; + } + + switch(buf->st_mode & S_IFMT) { + case S_IFREG: + reader_read_file(dir_ent); + break; + case S_IFDIR: + reader_scan(dir_ent->dir); + break; + } + } +} + + +void *reader(void *arg) +{ + if(!sorted) + reader_scan(queue_get(to_reader)); + else { + int i; + struct priority_entry *entry; + + queue_get(to_reader); + for(i = 65535; i >= 0; i--) + for(entry = priority_list[i]; entry; + entry = entry->next) + reader_read_file(entry->dir); + } + + pthread_exit(NULL); +} + + +void *writer(void *arg) +{ + while(1) { + struct file_buffer *file_buffer = queue_get(to_writer); + off_t off; + + if(file_buffer == NULL) { + queue_put(from_writer, NULL); + continue; + } + + off = file_buffer->block; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &pos_mutex); + pthread_mutex_lock(&pos_mutex); + + if(lseek(fd, off, SEEK_SET) == -1) { + ERROR("writer: Lseek on destination failed because " + "%s, offset=0x%llx\n", strerror(errno), off); + BAD_ERROR("Probably out of space on output " + "%s\n", block_device ? "block device" : + "filesystem"); + } + + if(write_bytes(fd, file_buffer->data, + file_buffer->size) == -1) + BAD_ERROR("Failed to write to output %s\n", + block_device ? "block device" : "filesystem"); + + pthread_cleanup_pop(1); + + cache_block_put(file_buffer); + } +} + + +int all_zero(struct file_buffer *file_buffer) +{ + int i; + long entries = file_buffer->size / sizeof(long); + long *p = (long *) file_buffer->data; + + for(i = 0; i < entries && p[i] == 0; i++); + + if(i == entries) { + for(i = file_buffer->size & ~(sizeof(long) - 1); + i < file_buffer->size && file_buffer->data[i] == 0; + i++); + + return i == file_buffer->size; + } + + return 0; +} + + +void *deflator(void *arg) +{ + struct file_buffer *write_buffer = cache_get_nohash(bwriter_buffer); + void *stream = NULL; + int res; + + res = compressor_init(comp, &stream, block_size, 1); + if(res) + BAD_ERROR("deflator:: compressor_init failed\n"); + + while(1) { + struct file_buffer *file_buffer = queue_get(to_deflate); + + if(sparse_files && all_zero(file_buffer)) { + file_buffer->c_byte = 0; + seq_queue_put(to_main, file_buffer); + } else { + write_buffer->c_byte = mangle2(stream, + write_buffer->data, file_buffer->data, + file_buffer->size, block_size, + file_buffer->noD, 1); + write_buffer->sequence = file_buffer->sequence; + write_buffer->file_size = file_buffer->file_size; + write_buffer->block = file_buffer->block; + write_buffer->size = SQUASHFS_COMPRESSED_SIZE_BLOCK + (write_buffer->c_byte); + write_buffer->fragment = FALSE; + write_buffer->error = FALSE; + cache_block_put(file_buffer); + seq_queue_put(to_main, write_buffer); + write_buffer = cache_get_nohash(bwriter_buffer); + } + } +} + + +void *frag_deflator(void *arg) +{ + void *stream = NULL; + int res; + + res = compressor_init(comp, &stream, block_size, 1); + if(res) + BAD_ERROR("frag_deflator:: compressor_init failed\n"); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); + + while(1) { + int c_byte, compressed_size; + struct file_buffer *file_buffer = queue_get(to_frag); + struct file_buffer *write_buffer = + cache_get(fwriter_buffer, file_buffer->block); + + c_byte = mangle2(stream, write_buffer->data, file_buffer->data, + file_buffer->size, block_size, noF, 1); + compressed_size = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); + write_buffer->size = compressed_size; + pthread_mutex_lock(&fragment_mutex); + if(fragments_locked == FALSE) { + fragment_table[file_buffer->block].size = c_byte; + fragment_table[file_buffer->block].start_block = bytes; + write_buffer->block = bytes; + bytes += compressed_size; + fragments_outstanding --; + queue_put(to_writer, write_buffer); + pthread_mutex_unlock(&fragment_mutex); + TRACE("Writing fragment %lld, uncompressed size %d, " + "compressed size %d\n", file_buffer->block, + file_buffer->size, compressed_size); + } else { + add_pending_fragment(write_buffer, c_byte, + file_buffer->block); + pthread_mutex_unlock(&fragment_mutex); + } + cache_block_put(file_buffer); + } + + pthread_cleanup_pop(0); +} + + +struct file_buffer *get_file_buffer() +{ + struct file_buffer *file_buffer = seq_queue_get(to_main); + + return file_buffer; +} + + +void write_file_empty(squashfs_inode *inode, struct dir_ent *dir_ent, + struct file_buffer *file_buffer, int *duplicate_file) +{ + file_count ++; + *duplicate_file = FALSE; + cache_block_put(file_buffer); + create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, 0, 0, 0, + NULL, &empty_fragment, NULL, 0); +} + + +void write_file_frag(squashfs_inode *inode, struct dir_ent *dir_ent, + struct file_buffer *file_buffer, int *duplicate_file) +{ + int size = file_buffer->file_size; + struct fragment *fragment; + unsigned short checksum = file_buffer->checksum; + char dont_put; + + fragment = frag_duplicate(file_buffer, &dont_put); + *duplicate_file = !fragment; + if(!fragment) { + fragment = get_and_fill_fragment(file_buffer, dir_ent); + if(duplicate_checking) + add_non_dup(size, 0, NULL, 0, fragment, 0, checksum, + TRUE, TRUE); + } + + if(dont_put) + free(file_buffer); + else + cache_block_put(file_buffer); + + total_bytes += size; + file_count ++; + + inc_progress_bar(); + + create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, size, 0, + 0, NULL, fragment, NULL, 0); + + if(!duplicate_checking) + free_fragment(fragment); +} + + +int write_file_process(squashfs_inode *inode, struct dir_ent *dir_ent, + struct file_buffer *read_buffer, int *duplicate_file) +{ + long long read_size, file_bytes, start; + struct fragment *fragment; + unsigned int *block_list = NULL; + int block = 0, status; + long long sparse = 0; + struct file_buffer *fragment_buffer = NULL; + + *duplicate_file = FALSE; + + lock_fragments(); + + file_bytes = 0; + start = bytes; + while (1) { + read_size = read_buffer->file_size; + if(read_buffer->fragment) + fragment_buffer = read_buffer; + else { + block_list = realloc(block_list, (block + 1) * + sizeof(unsigned int)); + if(block_list == NULL) + MEM_ERROR(); + block_list[block ++] = read_buffer->c_byte; + if(read_buffer->c_byte) { + read_buffer->block = bytes; + bytes += read_buffer->size; + cache_hash(read_buffer, read_buffer->block); + file_bytes += read_buffer->size; + queue_put(to_writer, read_buffer); + } else { + sparse += read_buffer->size; + cache_block_put(read_buffer); + } + } + inc_progress_bar(); + + if(read_size != -1) + break; + + read_buffer = get_file_buffer(); + if(read_buffer->error) + goto read_err; + } + + unlock_fragments(); + fragment = get_and_fill_fragment(fragment_buffer, dir_ent); + + if(duplicate_checking) + add_non_dup(read_size, file_bytes, block_list, start, fragment, + 0, fragment_buffer ? fragment_buffer->checksum : 0, + FALSE, TRUE); + cache_block_put(fragment_buffer); + file_count ++; + total_bytes += read_size; + + create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, start, + block, block_list, fragment, NULL, sparse); + + if(duplicate_checking == FALSE) { + free(block_list); + free_fragment(fragment); + } + + return 0; + +read_err: + dec_progress_bar(block); + status = read_buffer->error; + bytes = start; + if(!block_device) { + int res; + + queue_put(to_writer, NULL); + if(queue_get(from_writer) != 0) + EXIT_MKSQUASHFS(); + res = ftruncate(fd, bytes); + if(res != 0) + BAD_ERROR("Failed to truncate dest file because %s\n", + strerror(errno)); + } + unlock_fragments(); + free(block_list); + cache_block_put(read_buffer); + return status; +} + + +int write_file_blocks_dup(squashfs_inode *inode, struct dir_ent *dir_ent, + struct file_buffer *read_buffer, int *duplicate_file) +{ + int block, thresh; + long long read_size = read_buffer->file_size; + long long file_bytes, dup_start, start; + struct fragment *fragment; + struct file_info *dupl_ptr; + int blocks = (read_size + block_size - 1) >> block_log; + unsigned int *block_list, *block_listp; + struct file_buffer **buffer_list; + int status; + long long sparse = 0; + struct file_buffer *fragment_buffer = NULL; + + block_list = malloc(blocks * sizeof(unsigned int)); + if(block_list == NULL) + MEM_ERROR(); + block_listp = block_list; + + buffer_list = malloc(blocks * sizeof(struct file_buffer *)); + if(buffer_list == NULL) + MEM_ERROR(); + + lock_fragments(); + + file_bytes = 0; + start = dup_start = bytes; + thresh = blocks > bwriter_size ? blocks - bwriter_size : 0; + + for(block = 0; block < blocks;) { + if(read_buffer->fragment) { + block_list[block] = 0; + buffer_list[block] = NULL; + fragment_buffer = read_buffer; + blocks = read_size >> block_log; + } else { + block_list[block] = read_buffer->c_byte; + + if(read_buffer->c_byte) { + read_buffer->block = bytes; + bytes += read_buffer->size; + file_bytes += read_buffer->size; + cache_hash(read_buffer, read_buffer->block); + if(block < thresh) { + buffer_list[block] = NULL; + queue_put(to_writer, read_buffer); + } else + buffer_list[block] = read_buffer; + } else { + buffer_list[block] = NULL; + sparse += read_buffer->size; + cache_block_put(read_buffer); + } + } + inc_progress_bar(); + + if(++block < blocks) { + read_buffer = get_file_buffer(); + if(read_buffer->error) + goto read_err; + } + } + + dupl_ptr = duplicate(read_size, file_bytes, &block_listp, &dup_start, + &fragment, fragment_buffer, blocks, 0, FALSE); + + if(dupl_ptr) { + *duplicate_file = FALSE; + for(block = thresh; block < blocks; block ++) + if(buffer_list[block]) + queue_put(to_writer, buffer_list[block]); + fragment = get_and_fill_fragment(fragment_buffer, dir_ent); + dupl_ptr->fragment = fragment; + } else { + *duplicate_file = TRUE; + for(block = thresh; block < blocks; block ++) + cache_block_put(buffer_list[block]); + bytes = start; + if(thresh && !block_device) { + int res; + + queue_put(to_writer, NULL); + if(queue_get(from_writer) != 0) + EXIT_MKSQUASHFS(); + res = ftruncate(fd, bytes); + if(res != 0) + BAD_ERROR("Failed to truncate dest file because" + " %s\n", strerror(errno)); + } + } + + unlock_fragments(); + cache_block_put(fragment_buffer); + free(buffer_list); + file_count ++; + total_bytes += read_size; + + /* + * sparse count is needed to ensure squashfs correctly reports a + * a smaller block count on stat calls to sparse files. This is + * to ensure intelligent applications like cp correctly handle the + * file as a sparse file. If the file in the original filesystem isn't + * stored as a sparse file then still store it sparsely in squashfs, but + * report it as non-sparse on stat calls to preserve semantics + */ + if(sparse && (dir_ent->inode->buf.st_blocks << 9) >= read_size) + sparse = 0; + + create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, + dup_start, blocks, block_listp, fragment, NULL, sparse); + + if(*duplicate_file == TRUE) + free(block_list); + + return 0; + +read_err: + dec_progress_bar(block); + status = read_buffer->error; + bytes = start; + if(thresh && !block_device) { + int res; + + queue_put(to_writer, NULL); + if(queue_get(from_writer) != 0) + EXIT_MKSQUASHFS(); + res = ftruncate(fd, bytes); + if(res != 0) + BAD_ERROR("Failed to truncate dest file because %s\n", + strerror(errno)); + } + unlock_fragments(); + for(blocks = thresh; blocks < block; blocks ++) + cache_block_put(buffer_list[blocks]); + free(buffer_list); + free(block_list); + cache_block_put(read_buffer); + return status; +} + + +int write_file_blocks(squashfs_inode *inode, struct dir_ent *dir_ent, + struct file_buffer *read_buffer, int *dup) +{ + long long read_size = read_buffer->file_size; + long long file_bytes, start; + struct fragment *fragment; + unsigned int *block_list; + int block, status; + int blocks = (read_size + block_size - 1) >> block_log; + long long sparse = 0; + struct file_buffer *fragment_buffer = NULL; + + if(pre_duplicate(read_size)) + return write_file_blocks_dup(inode, dir_ent, read_buffer, dup); + + *dup = FALSE; + + block_list = malloc(blocks * sizeof(unsigned int)); + if(block_list == NULL) + MEM_ERROR(); + + lock_fragments(); + + file_bytes = 0; + start = bytes; + for(block = 0; block < blocks;) { + if(read_buffer->fragment) { + block_list[block] = 0; + fragment_buffer = read_buffer; + blocks = read_size >> block_log; + } else { + block_list[block] = read_buffer->c_byte; + if(read_buffer->c_byte) { + read_buffer->block = bytes; + bytes += read_buffer->size; + cache_hash(read_buffer, read_buffer->block); + file_bytes += read_buffer->size; + queue_put(to_writer, read_buffer); + } else { + sparse += read_buffer->size; + cache_block_put(read_buffer); + } + } + inc_progress_bar(); + + if(++block < blocks) { + read_buffer = get_file_buffer(); + if(read_buffer->error) + goto read_err; + } + } + + unlock_fragments(); + fragment = get_and_fill_fragment(fragment_buffer, dir_ent); + + if(duplicate_checking) + add_non_dup(read_size, file_bytes, block_list, start, fragment, + 0, fragment_buffer ? fragment_buffer->checksum : 0, + FALSE, TRUE); + cache_block_put(fragment_buffer); + file_count ++; + total_bytes += read_size; + + /* + * sparse count is needed to ensure squashfs correctly reports a + * a smaller block count on stat calls to sparse files. This is + * to ensure intelligent applications like cp correctly handle the + * file as a sparse file. If the file in the original filesystem isn't + * stored as a sparse file then still store it sparsely in squashfs, but + * report it as non-sparse on stat calls to preserve semantics + */ + if(sparse && (dir_ent->inode->buf.st_blocks << 9) >= read_size) + sparse = 0; + + create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, start, + blocks, block_list, fragment, NULL, sparse); + + if(duplicate_checking == FALSE) { + free(block_list); + free_fragment(fragment); + } + + return 0; + +read_err: + dec_progress_bar(block); + status = read_buffer->error; + bytes = start; + if(!block_device) { + int res; + + queue_put(to_writer, NULL); + if(queue_get(from_writer) != 0) + EXIT_MKSQUASHFS(); + res = ftruncate(fd, bytes); + if(res != 0) + BAD_ERROR("Failed to truncate dest file because %s\n", + strerror(errno)); + } + unlock_fragments(); + free(block_list); + cache_block_put(read_buffer); + return status; +} + + +void write_file(squashfs_inode *inode, struct dir_ent *dir, int *dup) +{ + int status; + struct file_buffer *read_buffer; + +again: + read_buffer = get_file_buffer(); + status = read_buffer->error; + + if(status) + cache_block_put(read_buffer); + else if(read_buffer->file_size == -1) + status = write_file_process(inode, dir, read_buffer, dup); + else if(read_buffer->file_size == 0) + write_file_empty(inode, dir, read_buffer, dup); + else if(read_buffer->fragment && read_buffer->c_byte) + write_file_frag(inode, dir, read_buffer, dup); + else + status = write_file_blocks(inode, dir, read_buffer, dup); + + if(status == 2) { + ERROR("File %s changed size while reading filesystem, " + "attempting to re-read\n", pathname(dir)); + goto again; + } else if(status == 1) { + ERROR_START("Failed to read file %s", pathname(dir)); + ERROR_EXIT(", creating empty file\n"); + write_file_empty(inode, dir, NULL, dup); + } +} + + +#define BUFF_SIZE 512 +char *name; +char *basename_r(); + +char *getbase(char *pathname) +{ + static char *b_buffer = NULL; + static int b_size = BUFF_SIZE; + char *result; + + if(b_buffer == NULL) { + b_buffer = malloc(b_size); + if(b_buffer == NULL) + MEM_ERROR(); + } + + while(1) { + if(*pathname != '/') { + result = getcwd(b_buffer, b_size); + if(result == NULL && errno != ERANGE) + BAD_ERROR("Getcwd failed in getbase\n"); + + /* enough room for pathname + "/" + '\0' terminator? */ + if(result && strlen(pathname) + 2 <= + b_size - strlen(b_buffer)) { + strcat(strcat(b_buffer, "/"), pathname); + break; + } + } else if(strlen(pathname) < b_size) { + strcpy(b_buffer, pathname); + break; + } + + /* Buffer not large enough, realloc and try again */ + b_buffer = realloc(b_buffer, b_size += BUFF_SIZE); + if(b_buffer == NULL) + MEM_ERROR(); + } + + name = b_buffer; + if(((result = basename_r()) == NULL) || (strcmp(result, "..") == 0)) + return NULL; + else + return result; +} + + +char *basename_r() +{ + char *s; + char *p; + int n = 1; + + for(;;) { + s = name; + if(*name == '\0') + return NULL; + if(*name != '/') { + while(*name != '\0' && *name != '/') name++; + n = name - s; + } + while(*name == '/') name++; + if(strncmp(s, ".", n) == 0) + continue; + if((*name == '\0') || (strncmp(s, "..", n) == 0) || + ((p = basename_r()) == NULL)) { + s[n] = '\0'; + return s; + } + if(strcmp(p, "..") == 0) + continue; + return p; + } +} + + +struct inode_info *lookup_inode3(struct stat *buf, int pseudo, int id, + char *symlink, int bytes) +{ + int ino_hash = INODE_HASH(buf->st_dev, buf->st_ino); + struct inode_info *inode; + + /* + * Look-up inode in hash table, if it already exists we have a + * hard-link, so increment the nlink count and return it. + * Don't do the look-up for directories because we don't hard-link + * directories. + */ + if ((buf->st_mode & S_IFMT) != S_IFDIR) { + for(inode = inode_info[ino_hash]; inode; inode = inode->next) { + if(memcmp(buf, &inode->buf, sizeof(struct stat)) == 0) { + inode->nlink ++; + return inode; + } + } + } + + inode = malloc(sizeof(struct inode_info) + bytes); + if(inode == NULL) + MEM_ERROR(); + + if(bytes) + memcpy(&inode->symlink, symlink, bytes); + memcpy(&inode->buf, buf, sizeof(struct stat)); + inode->read = FALSE; + inode->root_entry = FALSE; + inode->pseudo_file = pseudo; + inode->pseudo_id = id; + inode->inode = SQUASHFS_INVALID_BLK; + inode->nlink = 1; + inode->inode_number = 0; + + /* + * Copy filesystem wide defaults into inode, these filesystem + * wide defaults may be altered on an individual inode basis by + * user specified actions + * + */ + inode->no_fragments = no_fragments; + inode->always_use_fragments = always_use_fragments; + inode->noD = noD; + inode->noF = noF; + + inode->next = inode_info[ino_hash]; + inode_info[ino_hash] = inode; + + return inode; +} + + +static inline struct inode_info *lookup_inode2(struct stat *buf, int pseudo, int id) +{ + return lookup_inode3(buf, pseudo, id, NULL, 0); +} + + +static inline struct inode_info *lookup_inode(struct stat *buf) +{ + return lookup_inode2(buf, 0, 0); +} + + +static inline void alloc_inode_no(struct inode_info *inode, unsigned int use_this) +{ + if (inode->inode_number == 0) { + inode->inode_number = use_this ? : inode_no ++; + if((inode->buf.st_mode & S_IFMT) == S_IFREG) + progress_bar_size((inode->buf.st_size + block_size - 1) + >> block_log); + } +} + + +static inline struct dir_ent *create_dir_entry(char *name, char *source_name, + char *nonstandard_pathname, struct dir_info *dir) +{ + struct dir_ent *dir_ent = malloc(sizeof(struct dir_ent)); + if(dir_ent == NULL) + MEM_ERROR(); + + dir_ent->name = name; + dir_ent->source_name = source_name; + dir_ent->nonstandard_pathname = nonstandard_pathname; + dir_ent->our_dir = dir; + dir_ent->inode = NULL; + dir_ent->next = NULL; +/* ANDROID CHANGES START*/ +#ifdef ANDROID + dir_ent->capabilities = 0; +#endif +/* ANDROID CHANGES END */ + + return dir_ent; +} + + +static inline void add_dir_entry(struct dir_ent *dir_ent, struct dir_info *sub_dir, + struct inode_info *inode_info) +{ + struct dir_info *dir = dir_ent->our_dir; + + if(sub_dir) + sub_dir->dir_ent = dir_ent; + +/* ANDROID CHANGES START*/ +#ifdef ANDROID + if (android_config) { + if (mount_point) { + char *mounted_path; + char *rel_path; + + alloc_mounted_path(mount_point, subpathname(dir_ent), &mounted_path); + rel_path = mounted_path; + while (rel_path && *rel_path == '/') + rel_path++; + android_fs_config(fs_config_func, rel_path, &inode_info->buf, target_out_path, &dir_ent->capabilities); + free(mounted_path); + } else { + android_fs_config(fs_config_func, pathname(dir_ent), &inode_info->buf, target_out_path, &dir_ent->capabilities); + } + } +#endif +/* ANDROID CHANGES END */ + + dir_ent->inode = inode_info; + dir_ent->dir = sub_dir; + + dir_ent->next = dir->list; + dir->list = dir_ent; + dir->count++; +} + +static inline void add_dir_entry2(char *name, char *source_name, + char *nonstandard_pathname, struct dir_info *sub_dir, + struct inode_info *inode_info, struct dir_info *dir) +{ + struct dir_ent *dir_ent = create_dir_entry(name, source_name, + nonstandard_pathname, dir); + + + add_dir_entry(dir_ent, sub_dir, inode_info); +} + + +static inline void free_dir_entry(struct dir_ent *dir_ent) +{ + if(dir_ent->name) + free(dir_ent->name); + + if(dir_ent->source_name) + free(dir_ent->source_name); + + if(dir_ent->nonstandard_pathname) + free(dir_ent->nonstandard_pathname); + + /* if this entry has been associated with an inode, then we need + * to update the inode nlink count. Orphaned inodes are harmless, and + * is easier to leave them than go to the bother of deleting them */ + if(dir_ent->inode && !dir_ent->inode->root_entry) + dir_ent->inode->nlink --; + + free(dir_ent); +} + + +static inline void add_excluded(struct dir_info *dir) +{ + dir->excluded ++; +} + + +void dir_scan(squashfs_inode *inode, char *pathname, + struct dir_ent *(_readdir)(struct dir_info *), int progress) +{ + struct stat buf; + struct dir_ent *dir_ent; +/* ANDROID CHANGES START*/ +#ifdef ANDROID + uint64_t caps = 0; +#endif +/* ANDROID CHANGES END */ + + root_dir = dir_scan1(pathname, "", paths, _readdir, 1); + if(root_dir == NULL) + return; + + /* Create root directory dir_ent and associated inode, and connect + * it to the root directory dir_info structure */ + dir_ent = create_dir_entry("", NULL, pathname, + scan1_opendir("", "", 0)); + + if(pathname[0] == '\0') { + /* + * dummy top level directory, if multiple sources specified on + * command line + */ + memset(&buf, 0, sizeof(buf)); + buf.st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFDIR; + buf.st_uid = getuid(); + buf.st_gid = getgid(); + buf.st_mtime = time(NULL); + buf.st_dev = 0; + buf.st_ino = 0; + dir_ent->inode = lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0); + } else { + if(lstat(pathname, &buf) == -1) + /* source directory has disappeared? */ + BAD_ERROR("Cannot stat source directory %s because %s\n", + pathname, strerror(errno)); +/* ANDROID CHANGES START*/ +#ifdef ANDROID + if (android_config) { + if (mount_point) + android_fs_config(fs_config_func, mount_point, &buf, target_out_path, &caps); + else + android_fs_config(fs_config_func, pathname, &buf, target_out_path, &caps); + } +#endif +/* ANDROID CHANGES END */ + dir_ent->inode = lookup_inode(&buf); + } + +/* ANDROID CHANGES START*/ +#ifdef ANDROID + dir_ent->capabilities = caps; +#endif +/* ANDROID CHANGES END */ + + dir_ent->dir = root_dir; + root_dir->dir_ent = dir_ent; + + /* + * Process most actions and any pseudo files + */ + if(actions() || get_pseudo()) + dir_scan2(root_dir, get_pseudo()); + + /* + * Process move actions + */ + if(move_actions()) { + dir_scan3(root_dir); + do_move_actions(); + } + + /* + * Process prune actions + */ + if(prune_actions()) + dir_scan4(root_dir); + + /* + * Process empty actions + */ + if(empty_actions()) + dir_scan5(root_dir); + + /* + * Sort directories and compute the inode numbers + */ + dir_scan6(root_dir); + + alloc_inode_no(dir_ent->inode, root_inode_number); + + eval_actions(root_dir, dir_ent); + + if(sorted) + generate_file_priorities(root_dir, 0, + &root_dir->dir_ent->inode->buf); + + if(appending) { + sigset_t sigmask; + + restore_thread = init_restore_thread(); + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGUSR1); + if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == -1) + BAD_ERROR("Failed to set signal mask\n"); + write_destination(fd, SQUASHFS_START, 4, "\0\0\0\0"); + } + + queue_put(to_reader, root_dir); + + set_progressbar_state(progress); + + if(sorted) + sort_files_and_write(root_dir); + + dir_scan7(inode, root_dir); + dir_ent->inode->inode = *inode; + dir_ent->inode->type = SQUASHFS_DIR_TYPE; +} + + +/* + * dir_scan1 routines... + * These scan the source directories into memory for processing. + * Exclude actions are processed here (in contrast to the other actions) + * because they affect what is scanned. + */ +struct dir_info *scan1_opendir(char *pathname, char *subpath, int depth) +{ + struct dir_info *dir; + + dir = malloc(sizeof(struct dir_info)); + if(dir == NULL) + MEM_ERROR(); + + if(pathname[0] != '\0') { + dir->linuxdir = opendir(pathname); + if(dir->linuxdir == NULL) { + free(dir); + return NULL; + } + } + + dir->pathname = strdup(pathname); + dir->subpath = strdup(subpath); + dir->count = 0; + dir->directory_count = 0; + dir->dir_is_ldir = TRUE; + dir->list = NULL; + dir->depth = depth; + dir->excluded = 0; + + return dir; +} + + +struct dir_ent *scan1_encomp_readdir(struct dir_info *dir) +{ + static int index = 0; + + if(dir->count < old_root_entries) { + int i; + + for(i = 0; i < old_root_entries; i++) { + if(old_root_entry[i].inode.type == SQUASHFS_DIR_TYPE) + dir->directory_count ++; + add_dir_entry2(old_root_entry[i].name, NULL, NULL, NULL, + &old_root_entry[i].inode, dir); + } + } + + while(index < source) { + char *basename = NULL; + char *dir_name = getbase(source_path[index]); + int pass = 1, res; + + if(dir_name == NULL) { + ERROR_START("Bad source directory %s", + source_path[index]); + ERROR_EXIT(" - skipping ...\n"); + index ++; + continue; + } + dir_name = strdup(dir_name); + for(;;) { + struct dir_ent *dir_ent = dir->list; + + for(; dir_ent && strcmp(dir_ent->name, dir_name) != 0; + dir_ent = dir_ent->next); + if(dir_ent == NULL) + break; + ERROR("Source directory entry %s already used! - trying" + " ", dir_name); + if(pass == 1) + basename = dir_name; + else + free(dir_name); + res = asprintf(&dir_name, "%s_%d", basename, pass++); + if(res == -1) + BAD_ERROR("asprintf failed in " + "scan1_encomp_readdir\n"); + ERROR("%s\n", dir_name); + } + return create_dir_entry(dir_name, basename, + strdup(source_path[index ++]), dir); + } + return NULL; +} + + +struct dir_ent *scan1_single_readdir(struct dir_info *dir) +{ + struct dirent *d_name; + int i; + + if(dir->count < old_root_entries) { + for(i = 0; i < old_root_entries; i++) { + if(old_root_entry[i].inode.type == SQUASHFS_DIR_TYPE) + dir->directory_count ++; + add_dir_entry2(old_root_entry[i].name, NULL, NULL, NULL, + &old_root_entry[i].inode, dir); + } + } + + if((d_name = readdir(dir->linuxdir)) != NULL) { + char *basename = NULL; + char *dir_name = strdup(d_name->d_name); + int pass = 1, res; + + for(;;) { + struct dir_ent *dir_ent = dir->list; + + for(; dir_ent && strcmp(dir_ent->name, dir_name) != 0; + dir_ent = dir_ent->next); + if(dir_ent == NULL) + break; + ERROR("Source directory entry %s already used! - trying" + " ", dir_name); + if (pass == 1) + basename = dir_name; + else + free(dir_name); + res = asprintf(&dir_name, "%s_%d", d_name->d_name, pass++); + if(res == -1) + BAD_ERROR("asprintf failed in " + "scan1_single_readdir\n"); + ERROR("%s\n", dir_name); + } + return create_dir_entry(dir_name, basename, NULL, dir); + } + + return NULL; +} + + +struct dir_ent *scan1_readdir(struct dir_info *dir) +{ + struct dirent *d_name = readdir(dir->linuxdir); + + return d_name ? + create_dir_entry(strdup(d_name->d_name), NULL, NULL, dir) : + NULL; +} + + +void scan1_freedir(struct dir_info *dir) +{ + if(dir->pathname[0] != '\0') + closedir(dir->linuxdir); +} + + +struct dir_info *dir_scan1(char *filename, char *subpath, + struct pathnames *paths, + struct dir_ent *(_readdir)(struct dir_info *), int depth) +{ + struct dir_info *dir = scan1_opendir(filename, subpath, depth); + struct dir_ent *dir_ent; + + if(dir == NULL) { + ERROR_START("Could not open %s", filename); + ERROR_EXIT(", skipping...\n"); + return NULL; + } + + while((dir_ent = _readdir(dir))) { + struct dir_info *sub_dir; + struct stat buf; + struct pathnames *new = NULL; + char *filename = pathname(dir_ent); + char *subpath = NULL; + char *dir_name = dir_ent->name; + + if(strcmp(dir_name, ".") == 0 || strcmp(dir_name, "..") == 0) { + free_dir_entry(dir_ent); + continue; + } + + if(lstat(filename, &buf) == -1) { + ERROR_START("Cannot stat dir/file %s because %s", + filename, strerror(errno)); + ERROR_EXIT(", ignoring\n"); + free_dir_entry(dir_ent); + continue; + } + + if((buf.st_mode & S_IFMT) != S_IFREG && + (buf.st_mode & S_IFMT) != S_IFDIR && + (buf.st_mode & S_IFMT) != S_IFLNK && + (buf.st_mode & S_IFMT) != S_IFCHR && + (buf.st_mode & S_IFMT) != S_IFBLK && + (buf.st_mode & S_IFMT) != S_IFIFO && + (buf.st_mode & S_IFMT) != S_IFSOCK) { + ERROR_START("File %s has unrecognised filetype %d", + filename, buf.st_mode & S_IFMT); + ERROR_EXIT(", ignoring\n"); + free_dir_entry(dir_ent); + continue; + } + + if((old_exclude && old_excluded(filename, &buf)) || + (!old_exclude && excluded(dir_name, paths, &new))) { + add_excluded(dir); + free_dir_entry(dir_ent); + continue; + } + + if(exclude_actions()) { + subpath = subpathname(dir_ent); + + if(eval_exclude_actions(dir_name, filename, subpath, + &buf, depth, dir_ent)) { + add_excluded(dir); + free_dir_entry(dir_ent); + continue; + } + } + + switch(buf.st_mode & S_IFMT) { + case S_IFDIR: + if(subpath == NULL) + subpath = subpathname(dir_ent); + + sub_dir = dir_scan1(filename, subpath, new, + scan1_readdir, depth + 1); + if(sub_dir) { + dir->directory_count ++; + add_dir_entry(dir_ent, sub_dir, + lookup_inode(&buf)); + } else + free_dir_entry(dir_ent); + break; + case S_IFLNK: { + int byte; + static char buff[65536]; /* overflow safe */ + + byte = readlink(filename, buff, 65536); + if(byte == -1) { + ERROR_START("Failed to read symlink %s", + filename); + ERROR_EXIT(", ignoring\n"); + } else if(byte == 65536) { + ERROR_START("Symlink %s is greater than 65536 " + "bytes!", filename); + ERROR_EXIT(", ignoring\n"); + } else { + /* readlink doesn't 0 terminate the returned + * path */ + buff[byte] = '\0'; + add_dir_entry(dir_ent, NULL, lookup_inode3(&buf, + 0, 0, buff, byte + 1)); + } + break; + } + default: + add_dir_entry(dir_ent, NULL, lookup_inode(&buf)); + } + + free(new); + } + + scan1_freedir(dir); + + return dir; +} + + +/* + * dir_scan2 routines... + * This processes most actions and any pseudo files + */ +struct dir_ent *scan2_readdir(struct dir_info *dir, struct dir_ent *dir_ent) +{ + if (dir_ent == NULL) + dir_ent = dir->list; + else + dir_ent = dir_ent->next; + + for(; dir_ent && dir_ent->inode->root_entry; dir_ent = dir_ent->next); + + return dir_ent; +} + + +struct dir_ent *scan2_lookup(struct dir_info *dir, char *name) +{ + struct dir_ent *dir_ent = dir->list; + + for(; dir_ent && strcmp(dir_ent->name, name) != 0; + dir_ent = dir_ent->next); + + return dir_ent; +} + + +void dir_scan2(struct dir_info *dir, struct pseudo *pseudo) +{ + struct dir_ent *dir_ent = NULL; + struct pseudo_entry *pseudo_ent; + struct stat buf; + static int pseudo_ino = 1; + + while((dir_ent = scan2_readdir(dir, dir_ent)) != NULL) { + struct inode_info *inode_info = dir_ent->inode; + struct stat *buf = &inode_info->buf; + char *name = dir_ent->name; + + eval_actions(root_dir, dir_ent); + + if((buf->st_mode & S_IFMT) == S_IFDIR) + dir_scan2(dir_ent->dir, pseudo_subdir(name, pseudo)); + } + + while((pseudo_ent = pseudo_readdir(pseudo)) != NULL) { + dir_ent = scan2_lookup(dir, pseudo_ent->name); + if(pseudo_ent->dev->type == 'm') { + struct stat *buf; + if(dir_ent == NULL) { + ERROR_START("Pseudo modify file \"%s\" does " + "not exist in source filesystem.", + pseudo_ent->pathname); + ERROR_EXIT(" Ignoring.\n"); + continue; + } + if(dir_ent->inode->root_entry) { + ERROR_START("Pseudo modify file \"%s\" is a " + "pre-existing file in the filesystem " + "being appended to. It cannot be "\ + "modified.", pseudo_ent->pathname); + ERROR_EXIT(" Ignoring.\n"); + continue; + } + buf = &dir_ent->inode->buf; + buf->st_mode = (buf->st_mode & S_IFMT) | + pseudo_ent->dev->mode; + buf->st_uid = pseudo_ent->dev->uid; + buf->st_gid = pseudo_ent->dev->gid; + continue; + } + + if(dir_ent) { + if(dir_ent->inode->root_entry) { + ERROR_START("Pseudo file \"%s\" is a " + "pre-existing file in the filesystem " + "being appended to.", + pseudo_ent->pathname); + ERROR_EXIT(" Ignoring.\n"); + } else { + ERROR_START("Pseudo file \"%s\" exists in " + "source filesystem \"%s\".", + pseudo_ent->pathname, + pathname(dir_ent)); + ERROR_EXIT("\nIgnoring, exclude it (-e/-ef) to " + "override.\n"); + } + continue; + } + + memset(&buf, 0, sizeof(buf)); + buf.st_mode = pseudo_ent->dev->mode; + buf.st_uid = pseudo_ent->dev->uid; + buf.st_gid = pseudo_ent->dev->gid; + buf.st_rdev = makedev(pseudo_ent->dev->major, + pseudo_ent->dev->minor); + buf.st_mtime = time(NULL); + buf.st_ino = pseudo_ino ++; + + if(pseudo_ent->dev->type == 'd') { + struct dir_ent *dir_ent = + create_dir_entry(pseudo_ent->name, NULL, + pseudo_ent->pathname, dir); + char *subpath = strdup(subpathname(dir_ent)); + struct dir_info *sub_dir = scan1_opendir("", subpath, + dir->depth + 1); + if(sub_dir == NULL) { + ERROR_START("Could not create pseudo directory " + "\"%s\"", pseudo_ent->pathname); + ERROR_EXIT(", skipping...\n"); + free(subpath); + pseudo_ino --; + continue; + } + dir_scan2(sub_dir, pseudo_ent->pseudo); + dir->directory_count ++; + add_dir_entry(dir_ent, sub_dir, + lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0)); + } else if(pseudo_ent->dev->type == 'f') { + add_dir_entry2(pseudo_ent->name, NULL, + pseudo_ent->pathname, NULL, + lookup_inode2(&buf, PSEUDO_FILE_PROCESS, + pseudo_ent->dev->pseudo_id), dir); + } else { + add_dir_entry2(pseudo_ent->name, NULL, + pseudo_ent->pathname, NULL, + lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0), dir); + } + } +} + + +/* + * dir_scan3 routines... + * This processes the move action + */ +void dir_scan3(struct dir_info *dir) +{ + struct dir_ent *dir_ent = NULL; + + while((dir_ent = scan2_readdir(dir, dir_ent)) != NULL) { + + eval_move_actions(root_dir, dir_ent); + + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) + dir_scan3(dir_ent->dir); + } +} + + +/* + * dir_scan4 routines... + * This processes the prune action. This action is designed to do fine + * grained tuning of the in-core directory structure after the exclude, + * move and pseudo actions have been performed. This allows complex + * tests to be performed which are impossible at exclude time (i.e. + * tests which rely on the in-core directory structure) + */ +void free_dir(struct dir_info *dir) +{ + struct dir_ent *dir_ent = dir->list; + + while(dir_ent) { + struct dir_ent *tmp = dir_ent; + + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) + free_dir(dir_ent->dir); + + dir_ent = dir_ent->next; + free_dir_entry(tmp); + } + + free(dir->pathname); + free(dir->subpath); + free(dir); +} + + +void dir_scan4(struct dir_info *dir) +{ + struct dir_ent *dir_ent = dir->list, *prev = NULL; + + while(dir_ent) { + if(dir_ent->inode->root_entry) { + prev = dir_ent; + dir_ent = dir_ent->next; + continue; + } + + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) + dir_scan4(dir_ent->dir); + + if(eval_prune_actions(root_dir, dir_ent)) { + struct dir_ent *tmp = dir_ent; + + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) { + free_dir(dir_ent->dir); + dir->directory_count --; + } + + dir->count --; + + /* remove dir_ent from list */ + dir_ent = dir_ent->next; + if(prev) + prev->next = dir_ent; + else + dir->list = dir_ent; + + /* free it */ + free_dir_entry(tmp); + + add_excluded(dir); + continue; + } + + prev = dir_ent; + dir_ent = dir_ent->next; + } +} + + +/* + * dir_scan5 routines... + * This processes the empty action. This action has to be processed after + * all other actions because the previous exclude and move actions and the + * pseudo actions affect whether a directory is empty + */ +void dir_scan5(struct dir_info *dir) +{ + struct dir_ent *dir_ent = dir->list, *prev = NULL; + + while(dir_ent) { + if(dir_ent->inode->root_entry) { + prev = dir_ent; + dir_ent = dir_ent->next; + continue; + } + + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) { + dir_scan5(dir_ent->dir); + + if(eval_empty_actions(root_dir, dir_ent)) { + struct dir_ent *tmp = dir_ent; + + /* + * delete sub-directory, this is by definition + * empty + */ + free(dir_ent->dir->pathname); + free(dir_ent->dir->subpath); + free(dir_ent->dir); + + /* remove dir_ent from list */ + dir_ent = dir_ent->next; + if(prev) + prev->next = dir_ent; + else + dir->list = dir_ent; + + /* free it */ + free_dir_entry(tmp); + + /* update counts */ + dir->directory_count --; + dir->count --; + add_excluded(dir); + continue; + } + } + + prev = dir_ent; + dir_ent = dir_ent->next; + } +} + + +/* + * dir_scan6 routines... + * This sorts every directory and computes the inode numbers + */ + +/* + * Bottom up linked list merge sort. + * + * Qsort and other O(n log n) algorithms work well with arrays but not + * linked lists. Merge sort another O(n log n) sort algorithm on the other hand + * is not ideal for arrays (as it needs an additonal n storage locations + * as sorting is not done in place), but it is ideal for linked lists because + * it doesn't require any extra storage, + */ +void sort_directory(struct dir_info *dir) +{ + struct dir_ent *cur, *l1, *l2, *next; + int len1, len2, stride = 1; + + if(dir->list == NULL || dir->count < 2) + return; + + /* + * We can consider our linked-list to be made up of stride length + * sublists. Eacn iteration around this loop merges adjacent + * stride length sublists into larger 2*stride sublists. We stop + * when stride becomes equal to the entire list. + * + * Initially stride = 1 (by definition a sublist of 1 is sorted), and + * these 1 element sublists are merged into 2 element sublists, which + * are then merged into 4 element sublists and so on. + */ + do { + l2 = dir->list; /* head of current linked list */ + cur = NULL; /* empty output list */ + + /* + * Iterate through the linked list, merging adjacent sublists. + * On each interation l2 points to the next sublist pair to be + * merged (if there's only one sublist left this is simply added + * to the output list) + */ + while(l2) { + l1 = l2; + for(len1 = 0; l2 && len1 < stride; len1 ++, l2 = l2->next); + len2 = stride; + + /* + * l1 points to first sublist. + * l2 points to second sublist. + * Merge them onto the output list + */ + while(len1 && l2 && len2) { + if(strcmp(l1->name, l2->name) <= 0) { + next = l1; + l1 = l1->next; + len1 --; + } else { + next = l2; + l2 = l2->next; + len2 --; + } + + if(cur) { + cur->next = next; + cur = next; + } else + dir->list = cur = next; + } + /* + * One sublist is now empty, copy the other one onto the + * output list + */ + for(; len1; len1 --, l1 = l1->next) { + if(cur) { + cur->next = l1; + cur = l1; + } else + dir->list = cur = l1; + } + for(; l2 && len2; len2 --, l2 = l2->next) { + if(cur) { + cur->next = l2; + cur = l2; + } else + dir->list = cur = l2; + } + } + cur->next = NULL; + stride = stride << 1; + } while(stride < dir->count); +} + + +void dir_scan6(struct dir_info *dir) +{ + struct dir_ent *dir_ent; + unsigned int byte_count = 0; + + sort_directory(dir); + + for(dir_ent = dir->list; dir_ent; dir_ent = dir_ent->next) { + byte_count += strlen(dir_ent->name) + + sizeof(struct squashfs_dir_entry); + + if(dir_ent->inode->root_entry) + continue; + + alloc_inode_no(dir_ent->inode, 0); + + if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) + dir_scan6(dir_ent->dir); + } + + if((dir->count < 257 && byte_count < SQUASHFS_METADATA_SIZE)) + dir->dir_is_ldir = FALSE; +} + + +/* + * dir_scan6 routines... + * This generates the filesystem metadata and writes it out to the destination + */ +void scan7_init_dir(struct directory *dir) +{ + dir->buff = malloc(SQUASHFS_METADATA_SIZE); + if(dir->buff == NULL) + MEM_ERROR(); + + dir->size = SQUASHFS_METADATA_SIZE; + dir->p = dir->index_count_p = dir->buff; + dir->entry_count = 256; + dir->entry_count_p = NULL; + dir->index = NULL; + dir->i_count = dir->i_size = 0; +} + + +struct dir_ent *scan7_readdir(struct directory *dir, struct dir_info *dir_info, + struct dir_ent *dir_ent) +{ + if (dir_ent == NULL) + dir_ent = dir_info->list; + else + dir_ent = dir_ent->next; + + for(; dir_ent && dir_ent->inode->root_entry; dir_ent = dir_ent->next) + add_dir(dir_ent->inode->inode, dir_ent->inode->inode_number, + dir_ent->name, dir_ent->inode->type, dir); + + return dir_ent; +} + + +void scan7_freedir(struct directory *dir) +{ + if(dir->index) + free(dir->index); + free(dir->buff); +} + + +void dir_scan7(squashfs_inode *inode, struct dir_info *dir_info) +{ + int squashfs_type; + int duplicate_file; + struct directory dir; + struct dir_ent *dir_ent = NULL; + + scan7_init_dir(&dir); + + while((dir_ent = scan7_readdir(&dir, dir_info, dir_ent)) != NULL) { + struct stat *buf = &dir_ent->inode->buf; + + update_info(dir_ent); + + if(dir_ent->inode->inode == SQUASHFS_INVALID_BLK) { + switch(buf->st_mode & S_IFMT) { + case S_IFREG: + squashfs_type = SQUASHFS_FILE_TYPE; + write_file(inode, dir_ent, + &duplicate_file); + INFO("file %s, uncompressed size %lld " + "bytes %s\n", + subpathname(dir_ent), + (long long) buf->st_size, + duplicate_file ? "DUPLICATE" : + ""); + break; + + case S_IFDIR: + squashfs_type = SQUASHFS_DIR_TYPE; + dir_scan7(inode, dir_ent->dir); + break; + + case S_IFLNK: + squashfs_type = SQUASHFS_SYMLINK_TYPE; + create_inode(inode, NULL, dir_ent, + squashfs_type, 0, 0, 0, NULL, + NULL, NULL, 0); + INFO("symbolic link %s inode 0x%llx\n", + subpathname(dir_ent), *inode); + sym_count ++; + break; + + case S_IFCHR: + squashfs_type = SQUASHFS_CHRDEV_TYPE; + create_inode(inode, NULL, dir_ent, + squashfs_type, 0, 0, 0, NULL, + NULL, NULL, 0); + INFO("character device %s inode 0x%llx" + "\n", subpathname(dir_ent), + *inode); + dev_count ++; + break; + + case S_IFBLK: + squashfs_type = SQUASHFS_BLKDEV_TYPE; + create_inode(inode, NULL, dir_ent, + squashfs_type, 0, 0, 0, NULL, + NULL, NULL, 0); + INFO("block device %s inode 0x%llx\n", + subpathname(dir_ent), *inode); + dev_count ++; + break; + + case S_IFIFO: + squashfs_type = SQUASHFS_FIFO_TYPE; + create_inode(inode, NULL, dir_ent, + squashfs_type, 0, 0, 0, NULL, + NULL, NULL, 0); + INFO("fifo %s inode 0x%llx\n", + subpathname(dir_ent), *inode); + fifo_count ++; + break; + + case S_IFSOCK: + squashfs_type = SQUASHFS_SOCKET_TYPE; + create_inode(inode, NULL, dir_ent, + squashfs_type, 0, 0, 0, NULL, + NULL, NULL, 0); + INFO("unix domain socket %s inode " + "0x%llx\n", + subpathname(dir_ent), *inode); + sock_count ++; + break; + + default: + BAD_ERROR("%s unrecognised file type, " + "mode is %x\n", + subpathname(dir_ent), + buf->st_mode); + } + dir_ent->inode->inode = *inode; + dir_ent->inode->type = squashfs_type; + } else { + *inode = dir_ent->inode->inode; + squashfs_type = dir_ent->inode->type; + switch(squashfs_type) { + case SQUASHFS_FILE_TYPE: + if(!sorted) + INFO("file %s, uncompressed " + "size %lld bytes LINK" + "\n", + subpathname(dir_ent), + (long long) + buf->st_size); + break; + case SQUASHFS_SYMLINK_TYPE: + INFO("symbolic link %s inode 0x%llx " + "LINK\n", subpathname(dir_ent), + *inode); + break; + case SQUASHFS_CHRDEV_TYPE: + INFO("character device %s inode 0x%llx " + "LINK\n", subpathname(dir_ent), + *inode); + break; + case SQUASHFS_BLKDEV_TYPE: + INFO("block device %s inode 0x%llx " + "LINK\n", subpathname(dir_ent), + *inode); + break; + case SQUASHFS_FIFO_TYPE: + INFO("fifo %s inode 0x%llx LINK\n", + subpathname(dir_ent), *inode); + break; + case SQUASHFS_SOCKET_TYPE: + INFO("unix domain socket %s inode " + "0x%llx LINK\n", + subpathname(dir_ent), *inode); + break; + } + } + + add_dir(*inode, get_inode_no(dir_ent->inode), dir_ent->name, + squashfs_type, &dir); + } + + write_dir(inode, dir_info, &dir); + INFO("directory %s inode 0x%llx\n", subpathname(dir_info->dir_ent), + *inode); + + scan7_freedir(&dir); +} + + +unsigned int slog(unsigned int block) +{ + int i; + + for(i = 12; i <= 20; i++) + if(block == (1 << i)) + return i; + return 0; +} + + +int old_excluded(char *filename, struct stat *buf) +{ + int i; + + for(i = 0; i < exclude; i++) + if((exclude_paths[i].st_dev == buf->st_dev) && + (exclude_paths[i].st_ino == buf->st_ino)) + return TRUE; + return FALSE; +} + + +#define ADD_ENTRY(buf) \ + if(exclude % EXCLUDE_SIZE == 0) { \ + exclude_paths = realloc(exclude_paths, (exclude + EXCLUDE_SIZE) \ + * sizeof(struct exclude_info)); \ + if(exclude_paths == NULL) \ + MEM_ERROR(); \ + } \ + exclude_paths[exclude].st_dev = buf.st_dev; \ + exclude_paths[exclude++].st_ino = buf.st_ino; +int old_add_exclude(char *path) +{ + int i; + char *filename; + struct stat buf; + + if(path[0] == '/' || strncmp(path, "./", 2) == 0 || + strncmp(path, "../", 3) == 0) { + if(lstat(path, &buf) == -1) { + ERROR_START("Cannot stat exclude dir/file %s because " + "%s", path, strerror(errno)); + ERROR_EXIT(", ignoring\n"); + return TRUE; + } + ADD_ENTRY(buf); + return TRUE; + } + + for(i = 0; i < source; i++) { + int res = asprintf(&filename, "%s/%s", source_path[i], path); + if(res == -1) + BAD_ERROR("asprintf failed in old_add_exclude\n"); + if(lstat(filename, &buf) == -1) { + if(!(errno == ENOENT || errno == ENOTDIR)) { + ERROR_START("Cannot stat exclude dir/file %s " + "because %s", filename, strerror(errno)); + ERROR_EXIT(", ignoring\n"); + } + free(filename); + continue; + } + free(filename); + ADD_ENTRY(buf); + } + return TRUE; +} + + +void add_old_root_entry(char *name, squashfs_inode inode, int inode_number, + int type) +{ + old_root_entry = realloc(old_root_entry, + sizeof(struct old_root_entry_info) * (old_root_entries + 1)); + if(old_root_entry == NULL) + MEM_ERROR(); + + old_root_entry[old_root_entries].name = strdup(name); + old_root_entry[old_root_entries].inode.inode = inode; + old_root_entry[old_root_entries].inode.inode_number = inode_number; + old_root_entry[old_root_entries].inode.type = type; + old_root_entry[old_root_entries++].inode.root_entry = TRUE; +} + + +void initialise_threads(int readq, int fragq, int bwriteq, int fwriteq, + int freelst, char *destination_file) +{ + int i; + sigset_t sigmask, old_mask; + int total_mem = readq; + int reader_size; + int fragment_size; + int fwriter_size; + /* + * bwriter_size is global because it is needed in + * write_file_blocks_dup() + */ + + /* + * Never allow the total size of the queues to be larger than + * physical memory + * + * When adding together the possibly user supplied values, make + * sure they've not been deliberately contrived to overflow an int + */ + if(add_overflow(total_mem, fragq)) + BAD_ERROR("Queue sizes rediculously too large\n"); + total_mem += fragq; + if(add_overflow(total_mem, bwriteq)) + BAD_ERROR("Queue sizes rediculously too large\n"); + total_mem += bwriteq; + if(add_overflow(total_mem, fwriteq)) + BAD_ERROR("Queue sizes rediculously too large\n"); + total_mem += fwriteq; + + check_usable_phys_mem(total_mem); + + /* + * convert from queue size in Mbytes to queue size in + * blocks. + * + * This isn't going to overflow an int unless there exists + * systems with more than 8 Petabytes of RAM! + */ + reader_size = readq << (20 - block_log); + fragment_size = fragq << (20 - block_log); + bwriter_size = bwriteq << (20 - block_log); + fwriter_size = fwriteq << (20 - block_log); + + /* + * setup signal handlers for the main thread, these cleanup + * deleting the destination file, if appending the + * handlers for SIGTERM and SIGINT will be replaced with handlers + * allowing the user to press ^C twice to restore the existing + * filesystem. + * + * SIGUSR1 is an internal signal, which is used by the sub-threads + * to tell the main thread to terminate, deleting the destination file, + * or if necessary restoring the filesystem on appending + */ + signal(SIGTERM, sighandler); + signal(SIGINT, sighandler); + signal(SIGUSR1, sighandler); + + /* block SIGQUIT and SIGHUP, these are handled by the info thread */ + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGHUP); + sigaddset(&sigmask, SIGALRM); + if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == -1) + BAD_ERROR("Failed to set signal mask in intialise_threads\n"); + + /* + * temporarily block these signals, so the created sub-threads + * will ignore them, ensuring the main thread handles them + */ + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGUSR1); + if(pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask) == -1) + BAD_ERROR("Failed to set signal mask in intialise_threads\n"); + + if(processors == -1) { +#ifndef linux + int mib[2]; + size_t len = sizeof(processors); + + mib[0] = CTL_HW; +#ifdef HW_AVAILCPU + mib[1] = HW_AVAILCPU; +#else + mib[1] = HW_NCPU; +#endif + + if(sysctl(mib, 2, &processors, &len, NULL, 0) == -1) { + ERROR_START("Failed to get number of available " + "processors."); + ERROR_EXIT(" Defaulting to 1\n"); + processors = 1; + } +#else + processors = sysconf(_SC_NPROCESSORS_ONLN); +#endif + } + + if(multiply_overflow(processors, 3) || + multiply_overflow(processors * 3, sizeof(pthread_t))) + BAD_ERROR("Processors too large\n"); + + deflator_thread = malloc(processors * 3 * sizeof(pthread_t)); + if(deflator_thread == NULL) + MEM_ERROR(); + + frag_deflator_thread = &deflator_thread[processors]; + frag_thread = &frag_deflator_thread[processors]; + + to_reader = queue_init(1); + to_deflate = queue_init(reader_size); + to_process_frag = queue_init(reader_size); + to_writer = queue_init(bwriter_size + fwriter_size); + from_writer = queue_init(1); + to_frag = queue_init(fragment_size); + locked_fragment = queue_init(fragment_size); + to_main = seq_queue_init(); + reader_buffer = cache_init(block_size, reader_size, 0, 0); + bwriter_buffer = cache_init(block_size, bwriter_size, 1, freelst); + fwriter_buffer = cache_init(block_size, fwriter_size, 1, freelst); + fragment_buffer = cache_init(block_size, fragment_size, 1, 0); + reserve_cache = cache_init(block_size, processors + 1, 1, 0); + pthread_create(&reader_thread, NULL, reader, NULL); + pthread_create(&writer_thread, NULL, writer, NULL); + init_progress_bar(); + init_info(); + + for(i = 0; i < processors; i++) { + if(pthread_create(&deflator_thread[i], NULL, deflator, NULL)) + BAD_ERROR("Failed to create thread\n"); + if(pthread_create(&frag_deflator_thread[i], NULL, frag_deflator, + NULL) != 0) + BAD_ERROR("Failed to create thread\n"); + if(pthread_create(&frag_thread[i], NULL, frag_thrd, + (void *) destination_file) != 0) + BAD_ERROR("Failed to create thread\n"); + } + + main_thread = pthread_self(); + + printf("Parallel mksquashfs: Using %d processor%s\n", processors, + processors == 1 ? "" : "s"); + + /* Restore the signal mask for the main thread */ + if(pthread_sigmask(SIG_SETMASK, &old_mask, NULL) == -1) + BAD_ERROR("Failed to set signal mask in intialise_threads\n"); +} + + +long long write_inode_lookup_table() +{ + int i, inode_number, lookup_bytes = SQUASHFS_LOOKUP_BYTES(inode_count); + void *it; + + if(inode_count == sinode_count) + goto skip_inode_hash_table; + + it = realloc(inode_lookup_table, lookup_bytes); + if(it == NULL) + MEM_ERROR(); + inode_lookup_table = it; + + for(i = 0; i < INODE_HASH_SIZE; i ++) { + struct inode_info *inode; + + for(inode = inode_info[i]; inode; inode = inode->next) { + + inode_number = get_inode_no(inode); + + /* The empty action will produce orphaned inode + * entries in the inode_info[] table. These + * entries because they are orphaned will not be + * allocated an inode number in dir_scan5(), so + * skip any entries with the default dummy inode + * number of 0 */ + if(inode_number == 0) + continue; + + SQUASHFS_SWAP_LONG_LONGS(&inode->inode, + &inode_lookup_table[inode_number - 1], 1); + + } + } + +skip_inode_hash_table: + return generic_write_table(lookup_bytes, inode_lookup_table, 0, NULL, + noI); +} + + +char *get_component(char *target, char **targname) +{ + char *start; + + while(*target == '/') + target ++; + + start = target; + while(*target != '/' && *target != '\0') + target ++; + + *targname = strndup(start, target - start); + + while(*target == '/') + target ++; + + return target; +} + + +void free_path(struct pathname *paths) +{ + int i; + + for(i = 0; i < paths->names; i++) { + if(paths->name[i].paths) + free_path(paths->name[i].paths); + free(paths->name[i].name); + if(paths->name[i].preg) { + regfree(paths->name[i].preg); + free(paths->name[i].preg); + } + } + + free(paths); +} + + +struct pathname *add_path(struct pathname *paths, char *target, char *alltarget) +{ + char *targname; + int i, error; + + target = get_component(target, &targname); + + if(paths == NULL) { + paths = malloc(sizeof(struct pathname)); + if(paths == NULL) + MEM_ERROR(); + + paths->names = 0; + paths->name = NULL; + } + + for(i = 0; i < paths->names; i++) + if(strcmp(paths->name[i].name, targname) == 0) + break; + + if(i == paths->names) { + /* allocate new name entry */ + paths->names ++; + paths->name = realloc(paths->name, (i + 1) * + sizeof(struct path_entry)); + if(paths->name == NULL) + MEM_ERROR(); + paths->name[i].name = targname; + paths->name[i].paths = NULL; + if(use_regex) { + paths->name[i].preg = malloc(sizeof(regex_t)); + if(paths->name[i].preg == NULL) + MEM_ERROR(); + error = regcomp(paths->name[i].preg, targname, + REG_EXTENDED|REG_NOSUB); + if(error) { + char str[1024]; /* overflow safe */ + + regerror(error, paths->name[i].preg, str, 1024); + BAD_ERROR("invalid regex %s in export %s, " + "because %s\n", targname, alltarget, + str); + } + } else + paths->name[i].preg = NULL; + + if(target[0] == '\0') + /* at leaf pathname component */ + paths->name[i].paths = NULL; + else + /* recurse adding child components */ + paths->name[i].paths = add_path(NULL, target, + alltarget); + } else { + /* existing matching entry */ + free(targname); + + if(paths->name[i].paths == NULL) { + /* No sub-directory which means this is the leaf + * component of a pre-existing exclude which subsumes + * the exclude currently being added, in which case stop + * adding components */ + } else if(target[0] == '\0') { + /* at leaf pathname component and child components exist + * from more specific excludes, delete as they're + * subsumed by this exclude */ + free_path(paths->name[i].paths); + paths->name[i].paths = NULL; + } else + /* recurse adding child components */ + add_path(paths->name[i].paths, target, alltarget); + } + + return paths; +} + + +void add_exclude(char *target) +{ + + if(target[0] == '/' || strncmp(target, "./", 2) == 0 || + strncmp(target, "../", 3) == 0) + BAD_ERROR("/, ./ and ../ prefixed excludes not supported with " + "-wildcards or -regex options\n"); + else if(strncmp(target, "... ", 4) == 0) + stickypath = add_path(stickypath, target + 4, target + 4); + else + path = add_path(path, target, target); +} + + +void display_path(int depth, struct pathname *paths) +{ + int i, n; + + if(paths == NULL) + return; + + for(i = 0; i < paths->names; i++) { + for(n = 0; n < depth; n++) + printf("\t"); + printf("%d: %s\n", depth, paths->name[i].name); + display_path(depth + 1, paths->name[i].paths); + } +} + + +void display_path2(struct pathname *paths, char *string) +{ + int i; + char *path; + + if(paths == NULL) { + printf("%s\n", string); + return; + } + + for(i = 0; i < paths->names; i++) { + int res = asprintf(&path, "%s/%s", string, paths->name[i].name); + if(res == -1) + BAD_ERROR("asprintf failed in display_path2\n"); + display_path2(paths->name[i].paths, path); + free(path); + } +} + + +struct pathnames *add_subdir(struct pathnames *paths, struct pathname *path) +{ + int count = paths == NULL ? 0 : paths->count; + + if(count % PATHS_ALLOC_SIZE == 0) { + paths = realloc(paths, sizeof(struct pathnames) + + (count + PATHS_ALLOC_SIZE) * sizeof(struct pathname *)); + if(paths == NULL) + MEM_ERROR(); + } + + paths->path[count] = path; + paths->count = count + 1; + return paths; +} + + +int excluded_match(char *name, struct pathname *path, struct pathnames **new) +{ + int i; + + for(i = 0; i < path->names; i++) { + int match = use_regex ? + regexec(path->name[i].preg, name, (size_t) 0, + NULL, 0) == 0 : + fnmatch(path->name[i].name, name, + FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0; + + if(match) { + if(path->name[i].paths == NULL || new == NULL) + /* match on a leaf component, any subdirectories + * in the filesystem should be excluded */ + return TRUE; + else + /* match on a non-leaf component, add any + * subdirectories to the new set of + * subdirectories to scan for this name */ + *new = add_subdir(*new, path->name[i].paths); + } + } + + return FALSE; +} + + +int excluded(char *name, struct pathnames *paths, struct pathnames **new) +{ + int n; + + if(stickypath && excluded_match(name, stickypath, NULL)) + return TRUE; + + for(n = 0; paths && n < paths->count; n++) { + int res = excluded_match(name, paths->path[n], new); + if(res) { + free(*new); + *new = NULL; + return TRUE; + } + } + + /* + * Either: + * - no matching names found, return empty new search set, or + * - one or more matches with sub-directories found (no leaf matches), + * in which case return new search set. + * + * In either case return FALSE as we don't want to exclude this entry + */ + return FALSE; +} + + +void process_exclude_file(char *argv) +{ + FILE *fd; + char buffer[MAX_LINE + 1]; /* overflow safe */ + char *filename; + + fd = fopen(argv, "r"); + if(fd == NULL) + BAD_ERROR("Failed to open exclude file \"%s\" because %s\n", + argv, strerror(errno)); + + while(fgets(filename = buffer, MAX_LINE + 1, fd) != NULL) { + int len = strlen(filename); + + if(len == MAX_LINE && filename[len - 1] != '\n') + /* line too large */ + BAD_ERROR("Line too long when reading " + "exclude file \"%s\", larger than %d " + "bytes\n", argv, MAX_LINE); + + /* + * Remove '\n' terminator if it exists (the last line + * in the file may not be '\n' terminated) + */ + if(len && filename[len - 1] == '\n') + filename[len - 1] = '\0'; + + /* Skip any leading whitespace */ + while(isspace(*filename)) + filename ++; + + /* if comment line, skip */ + if(*filename == '#') + continue; + + /* + * check for initial backslash, to accommodate + * filenames with leading space or leading # character + */ + if(*filename == '\\') + filename ++; + + /* if line is now empty after skipping characters, skip it */ + if(*filename == '\0') + continue; + + if(old_exclude) + old_add_exclude(filename); + else + add_exclude(filename); + } + + if(ferror(fd)) + BAD_ERROR("Reading exclude file \"%s\" failed because %s\n", + argv, strerror(errno)); + + fclose(fd); +} + + +#define RECOVER_ID "Squashfs recovery file v1.0\n" +#define RECOVER_ID_SIZE 28 + +void write_recovery_data(struct squashfs_super_block *sBlk) +{ + int res, recoverfd, bytes = sBlk->bytes_used - sBlk->inode_table_start; + pid_t pid = getpid(); + char *metadata; + char header[] = RECOVER_ID; + + if(recover == FALSE) { + printf("No recovery data option specified.\n"); + printf("Skipping saving recovery file.\n\n"); + return; + } + + metadata = malloc(bytes); + if(metadata == NULL) + MEM_ERROR(); + + res = read_fs_bytes(fd, sBlk->inode_table_start, bytes, metadata); + if(res == 0) { + ERROR("Failed to read append filesystem metadata\n"); + BAD_ERROR("Filesystem corrupted?\n"); + } + + res = asprintf(&recovery_file, "squashfs_recovery_%s_%d", + getbase(destination_file), pid); + if(res == -1) + MEM_ERROR(); + + recoverfd = open(recovery_file, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU); + if(recoverfd == -1) + BAD_ERROR("Failed to create recovery file, because %s. " + "Aborting\n", strerror(errno)); + + if(write_bytes(recoverfd, header, RECOVER_ID_SIZE) == -1) + BAD_ERROR("Failed to write recovery file, because %s\n", + strerror(errno)); + + if(write_bytes(recoverfd, sBlk, sizeof(struct squashfs_super_block)) == -1) + BAD_ERROR("Failed to write recovery file, because %s\n", + strerror(errno)); + + if(write_bytes(recoverfd, metadata, bytes) == -1) + BAD_ERROR("Failed to write recovery file, because %s\n", + strerror(errno)); + + close(recoverfd); + free(metadata); + + printf("Recovery file \"%s\" written\n", recovery_file); + printf("If Mksquashfs aborts abnormally (i.e. power failure), run\n"); + printf("mksquashfs dummy %s -recover %s\n", destination_file, + recovery_file); + printf("to restore filesystem\n\n"); +} + + +void read_recovery_data(char *recovery_file, char *destination_file) +{ + int fd, recoverfd, bytes; + struct squashfs_super_block orig_sBlk, sBlk; + char *metadata; + int res; + struct stat buf; + char header[] = RECOVER_ID; + char header2[RECOVER_ID_SIZE]; + + recoverfd = open(recovery_file, O_RDONLY); + if(recoverfd == -1) + BAD_ERROR("Failed to open recovery file because %s\n", + strerror(errno)); + + if(stat(destination_file, &buf) == -1) + BAD_ERROR("Failed to stat destination file, because %s\n", + strerror(errno)); + + fd = open(destination_file, O_RDWR); + if(fd == -1) + BAD_ERROR("Failed to open destination file because %s\n", + strerror(errno)); + + res = read_bytes(recoverfd, header2, RECOVER_ID_SIZE); + if(res == -1) + BAD_ERROR("Failed to read recovery file, because %s\n", + strerror(errno)); + if(res < RECOVER_ID_SIZE) + BAD_ERROR("Recovery file appears to be truncated\n"); + if(strncmp(header, header2, RECOVER_ID_SIZE) !=0 ) + BAD_ERROR("Not a recovery file\n"); + + res = read_bytes(recoverfd, &sBlk, sizeof(struct squashfs_super_block)); + if(res == -1) + BAD_ERROR("Failed to read recovery file, because %s\n", + strerror(errno)); + if(res < sizeof(struct squashfs_super_block)) + BAD_ERROR("Recovery file appears to be truncated\n"); + + res = read_fs_bytes(fd, 0, sizeof(struct squashfs_super_block), &orig_sBlk); + if(res == 0) { + ERROR("Failed to read superblock from output filesystem\n"); + BAD_ERROR("Output filesystem is empty!\n"); + } + + if(memcmp(((char *) &sBlk) + 4, ((char *) &orig_sBlk) + 4, + sizeof(struct squashfs_super_block) - 4) != 0) + BAD_ERROR("Recovery file and destination file do not seem to " + "match\n"); + + bytes = sBlk.bytes_used - sBlk.inode_table_start; + + metadata = malloc(bytes); + if(metadata == NULL) + MEM_ERROR(); + + res = read_bytes(recoverfd, metadata, bytes); + if(res == -1) + BAD_ERROR("Failed to read recovery file, because %s\n", + strerror(errno)); + if(res < bytes) + BAD_ERROR("Recovery file appears to be truncated\n"); + + write_destination(fd, 0, sizeof(struct squashfs_super_block), &sBlk); + + write_destination(fd, sBlk.inode_table_start, bytes, metadata); + + close(recoverfd); + close(fd); + + printf("Successfully wrote recovery file \"%s\". Exiting\n", + recovery_file); + + exit(0); +} + + +void write_filesystem_tables(struct squashfs_super_block *sBlk, int nopad) +{ + int i; + + sBlk->fragments = fragments; + sBlk->no_ids = id_count; + sBlk->inode_table_start = write_inodes(); + sBlk->directory_table_start = write_directories(); + sBlk->fragment_table_start = write_fragment_table(); + sBlk->lookup_table_start = exportable ? write_inode_lookup_table() : + SQUASHFS_INVALID_BLK; + sBlk->id_table_start = write_id_table(); + sBlk->xattr_id_table_start = write_xattrs(); + + TRACE("sBlk->inode_table_start 0x%llx\n", sBlk->inode_table_start); + TRACE("sBlk->directory_table_start 0x%llx\n", + sBlk->directory_table_start); + TRACE("sBlk->fragment_table_start 0x%llx\n", sBlk->fragment_table_start); + if(exportable) + TRACE("sBlk->lookup_table_start 0x%llx\n", + sBlk->lookup_table_start); + + sBlk->bytes_used = bytes; + + sBlk->compression = comp->id; + + SQUASHFS_INSWAP_SUPER_BLOCK(sBlk); + write_destination(fd, SQUASHFS_START, sizeof(*sBlk), sBlk); + + if(!nopad && (i = bytes & (4096 - 1))) { + char temp[4096] = {0}; + write_destination(fd, bytes, 4096 - i, temp); + } + + close(fd); + + if(recovery_file) + unlink(recovery_file); + + total_bytes += total_inode_bytes + total_directory_bytes + + sizeof(struct squashfs_super_block) + total_xattr_bytes; + + printf("\n%sSquashfs %d.%d filesystem, %s compressed, data block size" + " %d\n", exportable ? "Exportable " : "", SQUASHFS_MAJOR, + SQUASHFS_MINOR, comp->name, block_size); + printf("\t%s data, %s metadata, %s fragments, %s xattrs\n", + noD ? "uncompressed" : "compressed", noI ? "uncompressed" : + "compressed", no_fragments ? "no" : noF ? "uncompressed" : + "compressed", no_xattrs ? "no" : noX ? "uncompressed" : + "compressed"); + printf("\tduplicates are %sremoved\n", duplicate_checking ? "" : + "not "); + printf("Filesystem size %.2f Kbytes (%.2f Mbytes)\n", bytes / 1024.0, + bytes / (1024.0 * 1024.0)); + printf("\t%.2f%% of uncompressed filesystem size (%.2f Kbytes)\n", + ((float) bytes / total_bytes) * 100.0, total_bytes / 1024.0); + printf("Inode table size %d bytes (%.2f Kbytes)\n", + inode_bytes, inode_bytes / 1024.0); + printf("\t%.2f%% of uncompressed inode table size (%d bytes)\n", + ((float) inode_bytes / total_inode_bytes) * 100.0, + total_inode_bytes); + printf("Directory table size %d bytes (%.2f Kbytes)\n", + directory_bytes, directory_bytes / 1024.0); + printf("\t%.2f%% of uncompressed directory table size (%d bytes)\n", + ((float) directory_bytes / total_directory_bytes) * 100.0, + total_directory_bytes); + if(total_xattr_bytes) { + printf("Xattr table size %d bytes (%.2f Kbytes)\n", + xattr_bytes, xattr_bytes / 1024.0); + printf("\t%.2f%% of uncompressed xattr table size (%d bytes)\n", + ((float) xattr_bytes / total_xattr_bytes) * 100.0, + total_xattr_bytes); + } + if(duplicate_checking) + printf("Number of duplicate files found %d\n", file_count - + dup_files); + else + printf("No duplicate files removed\n"); + printf("Number of inodes %d\n", inode_count); + printf("Number of files %d\n", file_count); + if(!no_fragments) + printf("Number of fragments %d\n", fragments); + printf("Number of symbolic links %d\n", sym_count); + printf("Number of device nodes %d\n", dev_count); + printf("Number of fifo nodes %d\n", fifo_count); + printf("Number of socket nodes %d\n", sock_count); + printf("Number of directories %d\n", dir_count); + printf("Number of ids (unique uids + gids) %d\n", id_count); + printf("Number of uids %d\n", uid_count); + + for(i = 0; i < id_count; i++) { + if(id_table[i]->flags & ISA_UID) { + struct passwd *user = getpwuid(id_table[i]->id); + printf("\t%s (%d)\n", user == NULL ? "unknown" : + user->pw_name, id_table[i]->id); + } + } + + printf("Number of gids %d\n", guid_count); + + for(i = 0; i < id_count; i++) { + if(id_table[i]->flags & ISA_GID) { + struct group *group = getgrgid(id_table[i]->id); + printf("\t%s (%d)\n", group == NULL ? "unknown" : + group->gr_name, id_table[i]->id); + } + } +} + + +int parse_numberll(char *start, long long *res, int size) +{ + char *end; + long long number; + + errno = 0; /* To distinguish success/failure after call */ + + number = strtoll(start, &end, 10); + + /* + * check for strtoll underflow or overflow in conversion, and other + * errors. + */ + if((errno == ERANGE && (number == LLONG_MIN || number == LLONG_MAX)) || + (errno != 0 && number == 0)) + return 0; + + /* reject negative numbers as invalid */ + if(number < 0) + return 0; + + if(size) { + /* + * Check for multiplier and trailing junk. + * But first check that a number exists before the + * multiplier + */ + if(end == start) + return 0; + + switch(end[0]) { + case 'g': + case 'G': + if(multiply_overflowll(number, 1073741824)) + return 0; + number *= 1073741824; + + if(end[1] != '\0') + /* trailing junk after multiplier, but + * allow it to be "bytes" */ + if(strcmp(end + 1, "bytes")) + return 0; + + break; + case 'm': + case 'M': + if(multiply_overflowll(number, 1048576)) + return 0; + number *= 1048576; + + if(end[1] != '\0') + /* trailing junk after multiplier, but + * allow it to be "bytes" */ + if(strcmp(end + 1, "bytes")) + return 0; + + break; + case 'k': + case 'K': + if(multiply_overflowll(number, 1024)) + return 0; + number *= 1024; + + if(end[1] != '\0') + /* trailing junk after multiplier, but + * allow it to be "bytes" */ + if(strcmp(end + 1, "bytes")) + return 0; + + break; + case '\0': + break; + default: + /* trailing junk after number */ + return 0; + } + } else if(end[0] != '\0') + /* trailing junk after number */ + return 0; + + *res = number; + return 1; +} + + +int parse_number(char *start, int *res, int size) +{ + long long number; + + if(!parse_numberll(start, &number, size)) + return 0; + + /* check if long result will overflow signed int */ + if(number > INT_MAX) + return 0; + + *res = (int) number; + return 1; +} + + +int parse_num(char *arg, int *res) +{ + return parse_number(arg, res, 0); +} + + +int get_physical_memory() +{ + int phys_mem; +#ifndef linux + #ifdef HW_MEMSIZE + #define SYSCTL_PHYSMEM HW_MEMSIZE + #elif defined(HW_PHYSMEM64) + #define SYSCTL_PHYSMEM HW_PHYSMEM64 + #else + #define SYSCTL_PHYSMEM HW_PHYSMEM + #endif + + int mib[2]; + uint64_t sysctl_physmem = 0; + size_t sysctl_len = sizeof(sysctl_physmem); + + mib[0] = CTL_HW; + mib[1] = SYSCTL_PHYSMEM; + + if(sysctl(mib, 2, &sysctl_physmem, &sysctl_len, NULL, 0) == 0) { + /* some systems use 32-bit values, work with what we're given */ + if (sysctl_len == 4) + sysctl_physmem = *(uint32_t*)&sysctl_physmem; + phys_mem = sysctl_physmem >> 20; + } else { + ERROR_START("Failed to get amount of available " + "memory."); + ERROR_EXIT(" Defaulting to least viable amount\n"); + phys_mem = SQUASHFS_LOWMEM; + } + #undef SYSCTL_PHYSMEM +#else + /* Long longs are used here because with PAE, a 32-bit + machine can have more than 4GB of physical memory */ + + long long num_pages = sysconf(_SC_PHYS_PAGES); + long long page_size = sysconf(_SC_PAGESIZE); + phys_mem = num_pages * page_size >> 20; + if(num_pages == -1 || page_size == -1) + return 0; + +#endif + + if(phys_mem < SQUASHFS_LOWMEM) + BAD_ERROR("Mksquashfs requires more physical memory than is " + "available!\n"); + + return phys_mem; +} + + +void check_usable_phys_mem(int total_mem) +{ + /* + * We want to allow users to use as much of their physical + * memory as they wish. However, for practical reasons there are + * limits which need to be imposed, to protect users from themselves + * and to prevent people from using Mksquashfs as a DOS attack by using + * all physical memory. Mksquashfs uses memory to cache data from disk + * to optimise performance. It is pointless to ask it to use more + * than 75% of physical memory, as this causes thrashing and it is thus + * self-defeating. + */ + int mem = get_physical_memory(); + + mem = (mem >> 1) + (mem >> 2); /* 75% */ + + if(total_mem > mem && mem) { + ERROR("Total memory requested is more than 75%% of physical " + "memory.\n"); + ERROR("Mksquashfs uses memory to cache data from disk to " + "optimise performance.\n"); + ERROR("It is pointless to ask it to use more than this amount " + "of memory, as this\n"); + ERROR("causes thrashing and it is thus self-defeating.\n"); + BAD_ERROR("Requested memory size too large\n"); + } + + if(sizeof(void *) == 4 && total_mem > 2048) { + /* + * If we're running on a kernel with PAE or on a 64-bit kernel, + * then the 75% physical memory limit can still easily exceed + * the addressable memory by this process. + * + * Due to the typical kernel/user-space split (1GB/3GB, or + * 2GB/2GB), we have to conservatively assume the 32-bit + * processes can only address 2-3GB. So refuse if the user + * tries to allocate more than 2GB. + */ + ERROR("Total memory requested may exceed maximum " + "addressable memory by this process\n"); + BAD_ERROR("Requested memory size too large\n"); + } +} + + +int get_default_phys_mem() +{ + /* + * get_physical_memory() relies on /proc being mounted. + * If it fails, issue a warning, and use + * SQUASHFS_LOWMEM / SQUASHFS_TAKE as default, + * and allow a larger value to be set with -mem. + */ + int mem = get_physical_memory(); + + if(mem == 0) { + mem = SQUASHFS_LOWMEM / SQUASHFS_TAKE; + + ERROR("Warning: Cannot get size of physical memory, probably " + "because /proc is missing.\n"); + ERROR("Warning: Defaulting to minimal use of %d Mbytes, use " + "-mem to set a better value,\n", mem); + ERROR("Warning: or fix /proc.\n"); + } else + mem /= SQUASHFS_TAKE; + + if(sizeof(void *) == 4 && mem > 640) { + /* + * If we're running on a kernel with PAE or on a 64-bit kernel, + * the default memory usage can exceed the addressable + * memory by this process. + * Due to the typical kernel/user-space split (1GB/3GB, or + * 2GB/2GB), we have to conservatively assume the 32-bit + * processes can only address 2-3GB. So limit the default + * usage to 640M, which gives room for other data. + */ + mem = 640; + } + + return mem; +} + + +void calculate_queue_sizes(int mem, int *readq, int *fragq, int *bwriteq, + int *fwriteq) +{ + *readq = mem / SQUASHFS_READQ_MEM; + *bwriteq = mem / SQUASHFS_BWRITEQ_MEM; + *fwriteq = mem / SQUASHFS_FWRITEQ_MEM; + *fragq = mem - *readq - *bwriteq - *fwriteq; +} + + +#define VERSION() \ + printf("mksquashfs version 4.3-git (2014/09/12)\n");\ + printf("copyright (C) 2014 Phillip Lougher "\ + "<phillip@squashfs.org.uk>\n\n"); \ + printf("This program is free software; you can redistribute it and/or"\ + "\n");\ + printf("modify it under the terms of the GNU General Public License"\ + "\n");\ + printf("as published by the Free Software Foundation; either version "\ + "2,\n");\ + printf("or (at your option) any later version.\n\n");\ + printf("This program is distributed in the hope that it will be "\ + "useful,\n");\ + printf("but WITHOUT ANY WARRANTY; without even the implied warranty "\ + "of\n");\ + printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"\ + "\n");\ + printf("GNU General Public License for more details.\n"); +int main(int argc, char *argv[]) +{ + struct stat buf, source_buf; + int res, i; + char *b, *root_name = NULL; + int keep_as_directory = FALSE; + squashfs_inode inode; + int readq; + int fragq; + int bwriteq; + int fwriteq; + int total_mem = get_default_phys_mem(); + int progress = TRUE; + int force_progress = FALSE; + struct file_buffer **fragment = NULL; +/* ANDROID CHANGES START*/ +#ifdef ANDROID + const char *fs_config_file = NULL; +#endif +/* ANDROID CHANGES END */ + + if(argc > 1 && strcmp(argv[1], "-version") == 0) { + VERSION(); + exit(0); + } + + block_log = slog(block_size); + calculate_queue_sizes(total_mem, &readq, &fragq, &bwriteq, &fwriteq); + + for(i = 1; i < argc && argv[i][0] != '-'; i++); + if(i < 3) + goto printOptions; + source_path = argv + 1; + source = i - 2; + + /* + * Scan the command line for -comp xxx option, this is to ensure + * any -X compressor specific options are passed to the + * correct compressor + */ + for(; i < argc; i++) { + struct compressor *prev_comp = comp; + + if(strcmp(argv[i], "-comp") == 0) { + if(++i == argc) { + ERROR("%s: -comp missing compression type\n", + argv[0]); + exit(1); + } + comp = lookup_compressor(argv[i]); + if(!comp->supported) { + ERROR("%s: Compressor \"%s\" is not supported!" + "\n", argv[0], argv[i]); + ERROR("%s: Compressors available:\n", argv[0]); + display_compressors("", COMP_DEFAULT); + exit(1); + } + if(prev_comp != NULL && prev_comp != comp) { + ERROR("%s: -comp multiple conflicting -comp" + " options specified on command line" + ", previously %s, now %s\n", argv[0], + prev_comp->name, comp->name); + exit(1); + } + compressor_opt_parsed = 1; + + } else if(strcmp(argv[i], "-e") == 0) + break; + else if(strcmp(argv[i], "-root-becomes") == 0 || + strcmp(argv[i], "-ef") == 0 || + strcmp(argv[i], "-pf") == 0 || + strcmp(argv[i], "-vaf") == 0 || + strcmp(argv[i], "-comp") == 0) + i++; + } + + /* + * if no -comp option specified lookup default compressor. Note the + * Makefile ensures the default compressor has been built, and so we + * don't need to to check for failure here + */ + if(comp == NULL) + comp = lookup_compressor(COMP_DEFAULT); + + for(i = source + 2; i < argc; i++) { + if(strcmp(argv[i], "-action") == 0 || + strcmp(argv[i], "-a") ==0) { + if(++i == argc) { + ERROR("%s: %s missing action\n", + argv[0], argv[i - 1]); + exit(1); + } + res = parse_action(argv[i], ACTION_LOG_NONE); + if(res == 0) + exit(1); + + } else if(strcmp(argv[i], "-verbose-action") == 0 || + strcmp(argv[i], "-va") ==0) { + if(++i == argc) { + ERROR("%s: %s missing action\n", + argv[0], argv[i - 1]); + exit(1); + } + res = parse_action(argv[i], ACTION_LOG_VERBOSE); + if(res == 0) + exit(1); + + } else if(strcmp(argv[i], "-true-action") == 0 || + strcmp(argv[i], "-ta") ==0) { + if(++i == argc) { + ERROR("%s: %s missing action\n", + argv[0], argv[i - 1]); + exit(1); + } + res = parse_action(argv[i], ACTION_LOG_TRUE); + if(res == 0) + exit(1); + + } else if(strcmp(argv[i], "-false-action") == 0 || + strcmp(argv[i], "-fa") ==0) { + if(++i == argc) { + ERROR("%s: %s missing action\n", + argv[0], argv[i - 1]); + exit(1); + } + res = parse_action(argv[i], ACTION_LOG_FALSE); + if(res == 0) + exit(1); + + } else if(strcmp(argv[i], "-action-file") == 0 || + strcmp(argv[i], "-af") ==0) { + if(++i == argc) { + ERROR("%s: %s missing filename\n", argv[0], + argv[i - 1]); + exit(1); + } + if(read_action_file(argv[i], ACTION_LOG_NONE) == FALSE) + exit(1); + + } else if(strcmp(argv[i], "-verbose-action-file") == 0 || + strcmp(argv[i], "-vaf") ==0) { + if(++i == argc) { + ERROR("%s: %s missing filename\n", argv[0], + argv[i - 1]); + exit(1); + } + if(read_action_file(argv[i], ACTION_LOG_VERBOSE) == FALSE) + exit(1); + + } else if(strcmp(argv[i], "-true-action-file") == 0 || + strcmp(argv[i], "-taf") ==0) { + if(++i == argc) { + ERROR("%s: %s missing filename\n", argv[0], + argv[i - 1]); + exit(1); + } + if(read_action_file(argv[i], ACTION_LOG_TRUE) == FALSE) + exit(1); + + } else if(strcmp(argv[i], "-false-action-file") == 0 || + strcmp(argv[i], "-faf") ==0) { + if(++i == argc) { + ERROR("%s: %s missing filename\n", argv[0], + argv[i - 1]); + exit(1); + } + if(read_action_file(argv[i], ACTION_LOG_FALSE) == FALSE) + exit(1); + + } else if(strcmp(argv[i], "-comp") == 0) + /* parsed previously */ + i++; + + else if(strncmp(argv[i], "-X", 2) == 0) { + int args; + + if(strcmp(argv[i] + 2, "help") == 0) + goto print_compressor_options; + + args = compressor_options(comp, argv + i, argc - i); + if(args < 0) { + if(args == -1) { + ERROR("%s: Unrecognised compressor" + " option %s\n", argv[0], + argv[i]); + if(!compressor_opt_parsed) + ERROR("%s: Did you forget to" + " specify -comp?\n", + argv[0]); +print_compressor_options: + ERROR("%s: selected compressor \"%s\"" + ". Options supported: %s\n", + argv[0], comp->name, + comp->usage ? "" : "none"); + if(comp->usage) + comp->usage(); + } + exit(1); + } + i += args; + + } else if(strcmp(argv[i], "-pf") == 0) { + if(++i == argc) { + ERROR("%s: -pf missing filename\n", argv[0]); + exit(1); + } + if(read_pseudo_file(argv[i]) == FALSE) + exit(1); + } else if(strcmp(argv[i], "-p") == 0) { + if(++i == argc) { + ERROR("%s: -p missing pseudo file definition\n", + argv[0]); + exit(1); + } + if(read_pseudo_def(argv[i]) == FALSE) + exit(1); + } else if(strcmp(argv[i], "-recover") == 0) { + if(++i == argc) { + ERROR("%s: -recover missing recovery file\n", + argv[0]); + exit(1); + } + read_recovery_data(argv[i], argv[source + 1]); + } else if(strcmp(argv[i], "-no-recovery") == 0) + recover = FALSE; + else if(strcmp(argv[i], "-wildcards") == 0) { + old_exclude = FALSE; + use_regex = FALSE; + } else if(strcmp(argv[i], "-regex") == 0) { + old_exclude = FALSE; + use_regex = TRUE; + } else if(strcmp(argv[i], "-no-sparse") == 0) + sparse_files = FALSE; + else if(strcmp(argv[i], "-no-progress") == 0) + progress = FALSE; + else if(strcmp(argv[i], "-progress") == 0) + force_progress = TRUE; + else if(strcmp(argv[i], "-no-exports") == 0) + exportable = FALSE; + else if(strcmp(argv[i], "-processors") == 0) { + if((++i == argc) || !parse_num(argv[i], &processors)) { + ERROR("%s: -processors missing or invalid " + "processor number\n", argv[0]); + exit(1); + } + if(processors < 1) { + ERROR("%s: -processors should be 1 or larger\n", + argv[0]); + exit(1); + } + } else if(strcmp(argv[i], "-read-queue") == 0) { + if((++i == argc) || !parse_num(argv[i], &readq)) { + ERROR("%s: -read-queue missing or invalid " + "queue size\n", argv[0]); + exit(1); + } + if(readq < 1) { + ERROR("%s: -read-queue should be 1 megabyte or " + "larger\n", argv[0]); + exit(1); + } + } else if(strcmp(argv[i], "-write-queue") == 0) { + if((++i == argc) || !parse_num(argv[i], &bwriteq)) { + ERROR("%s: -write-queue missing or invalid " + "queue size\n", argv[0]); + exit(1); + } + if(bwriteq < 2) { + ERROR("%s: -write-queue should be 2 megabytes " + "or larger\n", argv[0]); + exit(1); + } + fwriteq = bwriteq >> 1; + bwriteq -= fwriteq; + } else if(strcmp(argv[i], "-fragment-queue") == 0) { + if((++i == argc) || !parse_num(argv[i], &fragq)) { + ERROR("%s: -fragment-queue missing or invalid " + "queue size\n", argv[0]); + exit(1); + } + if(fragq < 1) { + ERROR("%s: -fragment-queue should be 1 " + "megabyte or larger\n", argv[0]); + exit(1); + } + } else if(strcmp(argv[i], "-mem") == 0) { + long long number; + + if((++i == argc) || + !parse_numberll(argv[i], &number, 1)) { + ERROR("%s: -mem missing or invalid mem size\n", + argv[0]); + exit(1); + } + + /* + * convert from bytes to Mbytes, ensuring the value + * does not overflow a signed int + */ + if(number >= (1LL << 51)) { + ERROR("%s: -mem invalid mem size\n", argv[0]); + exit(1); + } + + total_mem = number / 1048576; + if(total_mem < (SQUASHFS_LOWMEM / SQUASHFS_TAKE)) { + ERROR("%s: -mem should be %d Mbytes or " + "larger\n", argv[0], + SQUASHFS_LOWMEM / SQUASHFS_TAKE); + exit(1); + } + calculate_queue_sizes(total_mem, &readq, &fragq, + &bwriteq, &fwriteq); + } else if(strcmp(argv[i], "-b") == 0) { + if(++i == argc) { + ERROR("%s: -b missing block size\n", argv[0]); + exit(1); + } + if(!parse_number(argv[i], &block_size, 1)) { + ERROR("%s: -b invalid block size\n", argv[0]); + exit(1); + } + if((block_log = slog(block_size)) == 0) { + ERROR("%s: -b block size not power of two or " + "not between 4096 and 1Mbyte\n", + argv[0]); + exit(1); + } + } else if(strcmp(argv[i], "-ef") == 0) { + if(++i == argc) { + ERROR("%s: -ef missing filename\n", argv[0]); + exit(1); + } + } else if(strcmp(argv[i], "-no-duplicates") == 0) + duplicate_checking = FALSE; + + else if(strcmp(argv[i], "-no-fragments") == 0) + no_fragments = TRUE; + + else if(strcmp(argv[i], "-always-use-fragments") == 0) + always_use_fragments = TRUE; + + else if(strcmp(argv[i], "-sort") == 0) { + if(++i == argc) { + ERROR("%s: -sort missing filename\n", argv[0]); + exit(1); + } + } else if(strcmp(argv[i], "-all-root") == 0 || + strcmp(argv[i], "-root-owned") == 0) + global_uid = global_gid = 0; + + else if(strcmp(argv[i], "-force-uid") == 0) { + if(++i == argc) { + ERROR("%s: -force-uid missing uid or user\n", + argv[0]); + exit(1); + } + if((global_uid = strtoll(argv[i], &b, 10)), *b =='\0') { + if(global_uid < 0 || global_uid > + (((long long) 1 << 32) - 1)) { + ERROR("%s: -force-uid uid out of range" + "\n", argv[0]); + exit(1); + } + } else { + struct passwd *uid = getpwnam(argv[i]); + if(uid) + global_uid = uid->pw_uid; + else { + ERROR("%s: -force-uid invalid uid or " + "unknown user\n", argv[0]); + exit(1); + } + } + } else if(strcmp(argv[i], "-force-gid") == 0) { + if(++i == argc) { + ERROR("%s: -force-gid missing gid or group\n", + argv[0]); + exit(1); + } + if((global_gid = strtoll(argv[i], &b, 10)), *b =='\0') { + if(global_gid < 0 || global_gid > + (((long long) 1 << 32) - 1)) { + ERROR("%s: -force-gid gid out of range" + "\n", argv[0]); + exit(1); + } + } else { + struct group *gid = getgrnam(argv[i]); + if(gid) + global_gid = gid->gr_gid; + else { + ERROR("%s: -force-gid invalid gid or " + "unknown group\n", argv[0]); + exit(1); + } + } + } else if(strcmp(argv[i], "-noI") == 0 || + strcmp(argv[i], "-noInodeCompression") == 0) + noI = TRUE; + + else if(strcmp(argv[i], "-noD") == 0 || + strcmp(argv[i], "-noDataCompression") == 0) + noD = TRUE; + + else if(strcmp(argv[i], "-noF") == 0 || + strcmp(argv[i], "-noFragmentCompression") == 0) + noF = TRUE; + + else if(strcmp(argv[i], "-noX") == 0 || + strcmp(argv[i], "-noXattrCompression") == 0) + noX = TRUE; + + else if(strcmp(argv[i], "-no-xattrs") == 0) + no_xattrs = TRUE; + + else if(strcmp(argv[i], "-xattrs") == 0) + no_xattrs = FALSE; + +/* ANDROID CHANGES START*/ +#ifdef ANDROID + else if(strcmp(argv[i], "-context-file") == 0) { + if(++i == argc) { + ERROR("%s: -context-file: missing file name\n", + argv[0]); + exit(1); + } + context_file = argv[i]; + } + else if(strcmp(argv[i], "-fs-config-file") == 0) { + if(++i == argc) { + ERROR("%s: -fs-config-file: missing file name\n", + argv[0]); + exit(1); + } + fs_config_file = argv[i]; + } +#endif +/* ANDROID CHANGES END */ + else if(strcmp(argv[i], "-nopad") == 0) + nopad = TRUE; + + else if(strcmp(argv[i], "-info") == 0) + silent = FALSE; + + else if(strcmp(argv[i], "-e") == 0) + break; + + else if(strcmp(argv[i], "-noappend") == 0) + delete = TRUE; + + else if(strcmp(argv[i], "-keep-as-directory") == 0) + keep_as_directory = TRUE; +/* ANDROID CHANGES START*/ +#ifdef ANDROID + else if(strcmp(argv[i], "-android-fs-config") == 0) + android_config = TRUE; + else if(strcmp(argv[i], "-mount-point") == 0) { + if(++i == argc) { + ERROR("%s: -mount-point: missing mount point name\n", + argv[0]); + exit(1); + } + mount_point = argv[i]; + } + else if(strcmp(argv[i], "-product-out") == 0) { + if(++i == argc) { + ERROR("%s: -product-out: missing path name\n", + argv[0]); + exit(1); + } + target_out_path = argv[i]; + } +#endif +/* ANDROID CHANGES END */ + + else if(strcmp(argv[i], "-exit-on-error") == 0) + exit_on_error = TRUE; + + else if(strcmp(argv[i], "-root-becomes") == 0) { + if(++i == argc) { + ERROR("%s: -root-becomes: missing name\n", + argv[0]); + exit(1); + } + root_name = argv[i]; + } else if(strcmp(argv[i], "-version") == 0) { + VERSION(); + } else { + ERROR("%s: invalid option\n\n", argv[0]); +printOptions: + ERROR("SYNTAX:%s source1 source2 ... dest [options] " + "[-e list of exclude\ndirs/files]\n", argv[0]); + ERROR("\nFilesystem build options:\n"); + ERROR("-comp <comp>\t\tselect <comp> compression\n"); + ERROR("\t\t\tCompressors available:\n"); + display_compressors("\t\t\t", COMP_DEFAULT); + ERROR("-b <block_size>\t\tset data block to " + "<block_size>. Default 128 Kbytes\n"); + ERROR("\t\t\tOptionally a suffix of K or M can be" + " given to specify\n\t\t\tKbytes or Mbytes" + " respectively\n"); + ERROR("-no-exports\t\tdon't make the filesystem " + "exportable via NFS\n"); + ERROR("-no-sparse\t\tdon't detect sparse files\n"); + ERROR("-no-xattrs\t\tdon't store extended attributes" + NOXOPT_STR "\n"); + ERROR("-xattrs\t\t\tstore extended attributes" XOPT_STR + "\n"); +/* ANDROID CHANGES START*/ +#ifdef ANDROID + ERROR("-context-file <file>\tApply selinux security " + "xattrs from context-file instead\n\t\t\t" + "of reading xattrs from file system\n"); + ERROR("-fs-config-file <file>\tAndroid specific " + "filesystem config file\n"); +#endif +/* ANDROID CHANGES END */ + ERROR("-noI\t\t\tdo not compress inode table\n"); + ERROR("-noD\t\t\tdo not compress data blocks\n"); + ERROR("-noF\t\t\tdo not compress fragment blocks\n"); + ERROR("-noX\t\t\tdo not compress extended " + "attributes\n"); + ERROR("-no-fragments\t\tdo not use fragments\n"); + ERROR("-always-use-fragments\tuse fragment blocks for " + "files larger than block size\n"); + ERROR("-no-duplicates\t\tdo not perform duplicate " + "checking\n"); + ERROR("-all-root\t\tmake all files owned by root\n"); + ERROR("-force-uid uid\t\tset all file uids to uid\n"); + ERROR("-force-gid gid\t\tset all file gids to gid\n"); + ERROR("-nopad\t\t\tdo not pad filesystem to a multiple " + "of 4K\n"); + ERROR("-keep-as-directory\tif one source directory is " + "specified, create a root\n"); + ERROR("\t\t\tdirectory containing that directory, " + "rather than the\n"); + ERROR("\t\t\tcontents of the directory\n"); +/* ANDROID CHANGES START*/ +#ifdef ANDROID + ERROR("-android-fs-config\tuse android fs config " + "for mode, uid, and gids of inodes\n"); + ERROR("-mount-point <name>\tNeed to be provided when " + "android-fs-config or context-file\n\t\t\tare " + "enabled and source directory is not mount point\n"); + ERROR("-product-out <path>\tPRODUCT_OUT directory to " + "read device specific FS rules files from\n"); +#endif +/* ANDROID CHANGES END */ + ERROR("\nFilesystem filter options:\n"); + ERROR("-p <pseudo-definition>\tAdd pseudo file " + "definition\n"); + ERROR("-pf <pseudo-file>\tAdd list of pseudo file " + "definitions\n"); + ERROR("-sort <sort_file>\tsort files according to " + "priorities in <sort_file>. One\n"); + ERROR("\t\t\tfile or dir with priority per line. " + "Priority -32768 to\n"); + ERROR("\t\t\t32767, default priority 0\n"); + ERROR("-ef <exclude_file>\tlist of exclude dirs/files." + " One per line\n"); + ERROR("-wildcards\t\tAllow extended shell wildcards " + "(globbing) to be used in\n\t\t\texclude " + "dirs/files\n"); + ERROR("-regex\t\t\tAllow POSIX regular expressions to " + "be used in exclude\n\t\t\tdirs/files\n"); + ERROR("\nFilesystem append options:\n"); + ERROR("-noappend\t\tdo not append to existing " + "filesystem\n"); + ERROR("-root-becomes <name>\twhen appending source " + "files/directories, make the\n"); + ERROR("\t\t\toriginal root become a subdirectory in " + "the new root\n"); + ERROR("\t\t\tcalled <name>, rather than adding the new " + "source items\n"); + ERROR("\t\t\tto the original root\n"); + ERROR("\nMksquashfs runtime options:\n"); + ERROR("-version\t\tprint version, licence and " + "copyright message\n"); + ERROR("-exit-on-error\t\ttreat normally ignored errors " + "as fatal\n"); + ERROR("-recover <name>\t\trecover filesystem data " + "using recovery file <name>\n"); + ERROR("-no-recovery\t\tdon't generate a recovery " + "file\n"); + ERROR("-info\t\t\tprint files written to filesystem\n"); + ERROR("-no-progress\t\tdon't display the progress " + "bar\n"); + ERROR("-progress\t\tdisplay progress bar when using " + "the -info option\n"); + ERROR("-processors <number>\tUse <number> processors." + " By default will use number of\n"); + ERROR("\t\t\tprocessors available\n"); + ERROR("-mem <size>\t\tUse <size> physical memory. " + "Currently set to %dM\n", total_mem); + ERROR("\t\t\tOptionally a suffix of K, M or G can be" + " given to specify\n\t\t\tKbytes, Mbytes or" + " Gbytes respectively\n"); + ERROR("\nMiscellaneous options:\n"); + ERROR("-root-owned\t\talternative name for -all-root" + "\n"); + ERROR("-noInodeCompression\talternative name for -noI" + "\n"); + ERROR("-noDataCompression\talternative name for -noD" + "\n"); + ERROR("-noFragmentCompression\talternative name for " + "-noF\n"); + ERROR("-noXattrCompression\talternative name for " + "-noX\n"); + ERROR("\n-Xhelp\t\t\tprint compressor options for" + " selected compressor\n"); + ERROR("\nCompressors available and compressor specific " + "options:\n"); + display_compressor_usage(COMP_DEFAULT); + exit(1); + } + } + +/* ANDROID CHANGES START*/ +#ifdef ANDROID + if (fs_config_file) { + if (load_canned_fs_config(fs_config_file) < 0) { + fprintf(stderr, "failed to load %s\n", fs_config_file); + exit(1); + } + fs_config_func = canned_fs_config; + } else if (mount_point) { + fs_config_func = fs_config; + } +#endif +/* ANDROID CHANGES END */ + + /* + * Some compressors may need the options to be checked for validity + * once all the options have been processed + */ + res = compressor_options_post(comp, block_size); + if(res) + EXIT_MKSQUASHFS(); + + /* + * If the -info option has been selected then disable the + * progress bar unless it has been explicitly enabled with + * the -progress option + */ + if(!silent) + progress = force_progress; + +#ifdef SQUASHFS_TRACE + /* + * Disable progress bar if full debug tracing is enabled. + * The progress bar in this case just gets in the way of the + * debug trace output + */ + progress = FALSE; +#endif + + for(i = 0; i < source; i++) + if(lstat(source_path[i], &source_buf) == -1) { + fprintf(stderr, "Cannot stat source directory \"%s\" " + "because %s\n", source_path[i], + strerror(errno)); + EXIT_MKSQUASHFS(); + } + + destination_file = argv[source + 1]; + if(stat(argv[source + 1], &buf) == -1) { + if(errno == ENOENT) { /* Does not exist */ + fd = open(argv[source + 1], O_CREAT | O_TRUNC | O_RDWR, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if(fd == -1) { + perror("Could not create destination file"); + exit(1); + } + delete = TRUE; + } else { + perror("Could not stat destination file"); + exit(1); + } + + } else { + if(S_ISBLK(buf.st_mode)) { + if((fd = open(argv[source + 1], O_RDWR)) == -1) { + perror("Could not open block device as " + "destination"); + exit(1); + } + block_device = 1; + + } else if(S_ISREG(buf.st_mode)) { + fd = open(argv[source + 1], (delete ? O_TRUNC : 0) | + O_RDWR); + if(fd == -1) { + perror("Could not open regular file for " + "writing as destination"); + exit(1); + } + } + else { + ERROR("Destination not block device or regular file\n"); + exit(1); + } + + } + + /* + * process the exclude files - must be done afer destination file has + * been possibly created + */ + for(i = source + 2; i < argc; i++) + if(strcmp(argv[i], "-ef") == 0) + /* + * Note presence of filename arg has already + * been checked + */ + process_exclude_file(argv[++i]); + else if(strcmp(argv[i], "-e") == 0) + break; + else if(strcmp(argv[i], "-root-becomes") == 0 || + strcmp(argv[i], "-sort") == 0 || + strcmp(argv[i], "-pf") == 0 || + strcmp(argv[i], "-af") == 0 || + strcmp(argv[i], "-vaf") == 0 || + strcmp(argv[i], "-comp") == 0) + i++; + + if(i != argc) { + if(++i == argc) { + ERROR("%s: -e missing arguments\n", argv[0]); + EXIT_MKSQUASHFS(); + } + while(i < argc) + if(old_exclude) + old_add_exclude(argv[i++]); + else + add_exclude(argv[i++]); + } + + /* process the sort files - must be done afer the exclude files */ + for(i = source + 2; i < argc; i++) + if(strcmp(argv[i], "-sort") == 0) { + int res = read_sort_file(argv[++i], source, + source_path); + if(res == FALSE) + BAD_ERROR("Failed to read sort file\n"); + sorted ++; + } else if(strcmp(argv[i], "-e") == 0) + break; + else if(strcmp(argv[i], "-root-becomes") == 0 || + strcmp(argv[i], "-ef") == 0 || + strcmp(argv[i], "-pf") == 0 || + strcmp(argv[i], "-af") == 0 || + strcmp(argv[i], "-vaf") == 0 || + strcmp(argv[i], "-comp") == 0) + i++; + + if(!delete) { + comp = read_super(fd, &sBlk, argv[source + 1]); + if(comp == NULL) { + ERROR("Failed to read existing filesystem - will not " + "overwrite - ABORTING!\n"); + ERROR("To force Mksquashfs to write to this block " + "device or file use -noappend\n"); + EXIT_MKSQUASHFS(); + } + + block_log = slog(block_size = sBlk.block_size); + noI = SQUASHFS_UNCOMPRESSED_INODES(sBlk.flags); + noD = SQUASHFS_UNCOMPRESSED_DATA(sBlk.flags); + noF = SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk.flags); + noX = SQUASHFS_UNCOMPRESSED_XATTRS(sBlk.flags); + no_fragments = SQUASHFS_NO_FRAGMENTS(sBlk.flags); + always_use_fragments = SQUASHFS_ALWAYS_FRAGMENTS(sBlk.flags); + duplicate_checking = SQUASHFS_DUPLICATES(sBlk.flags); + exportable = SQUASHFS_EXPORTABLE(sBlk.flags); + no_xattrs = SQUASHFS_NO_XATTRS(sBlk.flags); + comp_opts = SQUASHFS_COMP_OPTS(sBlk.flags); + } + + initialise_threads(readq, fragq, bwriteq, fwriteq, delete, + destination_file); + + res = compressor_init(comp, &stream, SQUASHFS_METADATA_SIZE, 0); + if(res) + BAD_ERROR("compressor_init failed\n"); + + if(delete) { + int size; + void *comp_data = compressor_dump_options(comp, block_size, + &size); + + printf("Creating %d.%d filesystem on %s, block size %d.\n", + SQUASHFS_MAJOR, SQUASHFS_MINOR, argv[source + 1], block_size); + + /* + * store any compressor specific options after the superblock, + * and set the COMP_OPT flag to show that the filesystem has + * compressor specfic options + */ + if(comp_data) { + unsigned short c_byte = size | SQUASHFS_COMPRESSED_BIT; + + SQUASHFS_INSWAP_SHORTS(&c_byte, 1); + write_destination(fd, sizeof(struct squashfs_super_block), + sizeof(c_byte), &c_byte); + write_destination(fd, sizeof(struct squashfs_super_block) + + sizeof(c_byte), size, comp_data); + bytes = sizeof(struct squashfs_super_block) + sizeof(c_byte) + + size; + comp_opts = TRUE; + } else + bytes = sizeof(struct squashfs_super_block); + } else { + unsigned int last_directory_block, inode_dir_offset, + inode_dir_file_size, root_inode_size, + inode_dir_start_block, uncompressed_data, + compressed_data, inode_dir_inode_number, + inode_dir_parent_inode; + unsigned int root_inode_start = + SQUASHFS_INODE_BLK(sBlk.root_inode), + root_inode_offset = + SQUASHFS_INODE_OFFSET(sBlk.root_inode); + + if((bytes = read_filesystem(root_name, fd, &sBlk, &inode_table, + &data_cache, &directory_table, + &directory_data_cache, &last_directory_block, + &inode_dir_offset, &inode_dir_file_size, + &root_inode_size, &inode_dir_start_block, + &file_count, &sym_count, &dev_count, &dir_count, + &fifo_count, &sock_count, &total_bytes, + &total_inode_bytes, &total_directory_bytes, + &inode_dir_inode_number, + &inode_dir_parent_inode, add_old_root_entry, + &fragment_table, &inode_lookup_table)) == 0) { + ERROR("Failed to read existing filesystem - will not " + "overwrite - ABORTING!\n"); + ERROR("To force Mksquashfs to write to this block " + "device or file use -noappend\n"); + EXIT_MKSQUASHFS(); + } + if((append_fragments = fragments = sBlk.fragments)) { + fragment_table = realloc((char *) fragment_table, + ((fragments + FRAG_SIZE - 1) & ~(FRAG_SIZE - 1)) + * sizeof(struct squashfs_fragment_entry)); + if(fragment_table == NULL) + BAD_ERROR("Out of memory in save filesystem state\n"); + } + + printf("Appending to existing %d.%d filesystem on %s, block " + "size %d\n", SQUASHFS_MAJOR, SQUASHFS_MINOR, argv[source + 1], + block_size); + printf("All -b, -noI, -noD, -noF, -noX, no-duplicates, no-fragments, " + "-always-use-fragments,\n-exportable and -comp options " + "ignored\n"); + printf("\nIf appending is not wanted, please re-run with " + "-noappend specified!\n\n"); + + compressed_data = (inode_dir_offset + inode_dir_file_size) & + ~(SQUASHFS_METADATA_SIZE - 1); + uncompressed_data = (inode_dir_offset + inode_dir_file_size) & + (SQUASHFS_METADATA_SIZE - 1); + + /* save original filesystem state for restoring ... */ + sfragments = fragments; + sbytes = bytes; + sinode_count = sBlk.inodes; + scache_bytes = root_inode_offset + root_inode_size; + sdirectory_cache_bytes = uncompressed_data; + sdata_cache = malloc(scache_bytes); + if(sdata_cache == NULL) + BAD_ERROR("Out of memory in save filesystem state\n"); + sdirectory_data_cache = malloc(sdirectory_cache_bytes); + if(sdirectory_data_cache == NULL) + BAD_ERROR("Out of memory in save filesystem state\n"); + memcpy(sdata_cache, data_cache, scache_bytes); + memcpy(sdirectory_data_cache, directory_data_cache + + compressed_data, sdirectory_cache_bytes); + sinode_bytes = root_inode_start; + stotal_bytes = total_bytes; + stotal_inode_bytes = total_inode_bytes; + stotal_directory_bytes = total_directory_bytes + + compressed_data; + sfile_count = file_count; + ssym_count = sym_count; + sdev_count = dev_count; + sdir_count = dir_count + 1; + sfifo_count = fifo_count; + ssock_count = sock_count; + sdup_files = dup_files; + sid_count = id_count; + write_recovery_data(&sBlk); + save_xattrs(); + appending = TRUE; + + /* + * set the filesystem state up to be able to append to the + * original filesystem. The filesystem state differs depending + * on whether we're appending to the original root directory, or + * if the original root directory becomes a sub-directory + * (root-becomes specified on command line, here root_name != + * NULL) + */ + inode_bytes = inode_size = root_inode_start; + directory_size = last_directory_block; + cache_size = root_inode_offset + root_inode_size; + directory_cache_size = inode_dir_offset + inode_dir_file_size; + if(root_name) { + sdirectory_bytes = last_directory_block; + sdirectory_compressed_bytes = 0; + root_inode_number = inode_dir_parent_inode; + inode_no = sBlk.inodes + 2; + directory_bytes = last_directory_block; + directory_cache_bytes = uncompressed_data; + memmove(directory_data_cache, directory_data_cache + + compressed_data, uncompressed_data); + cache_bytes = root_inode_offset + root_inode_size; + add_old_root_entry(root_name, sBlk.root_inode, + inode_dir_inode_number, SQUASHFS_DIR_TYPE); + total_directory_bytes += compressed_data; + dir_count ++; + } else { + sdirectory_compressed_bytes = last_directory_block - + inode_dir_start_block; + sdirectory_compressed = + malloc(sdirectory_compressed_bytes); + if(sdirectory_compressed == NULL) + BAD_ERROR("Out of memory in save filesystem " + "state\n"); + memcpy(sdirectory_compressed, directory_table + + inode_dir_start_block, + sdirectory_compressed_bytes); + sdirectory_bytes = inode_dir_start_block; + root_inode_number = inode_dir_inode_number; + inode_no = sBlk.inodes + 1; + directory_bytes = inode_dir_start_block; + directory_cache_bytes = inode_dir_offset; + cache_bytes = root_inode_offset; + } + + inode_count = file_count + dir_count + sym_count + dev_count + + fifo_count + sock_count; + } + + if(path) + paths = add_subdir(paths, path); + + dump_actions(); + dump_pseudos(); + + if(delete && !keep_as_directory && source == 1 && + S_ISDIR(source_buf.st_mode)) + dir_scan(&inode, source_path[0], scan1_readdir, progress); + else if(!keep_as_directory && source == 1 && + S_ISDIR(source_buf.st_mode)) + dir_scan(&inode, source_path[0], scan1_single_readdir, progress); + else + dir_scan(&inode, "", scan1_encomp_readdir, progress); + sBlk.root_inode = inode; + sBlk.inodes = inode_count; + sBlk.s_magic = SQUASHFS_MAGIC; + sBlk.s_major = SQUASHFS_MAJOR; + sBlk.s_minor = SQUASHFS_MINOR; + sBlk.block_size = block_size; + sBlk.block_log = block_log; + sBlk.flags = SQUASHFS_MKFLAGS(noI, noD, noF, noX, no_fragments, + always_use_fragments, duplicate_checking, exportable, + no_xattrs, comp_opts); + sBlk.mkfs_time = time(NULL); + + disable_info(); + + while((fragment = get_frag_action(fragment))) + write_fragment(*fragment); + unlock_fragments(); + pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); + pthread_mutex_lock(&fragment_mutex); + while(fragments_outstanding) { + pthread_mutex_unlock(&fragment_mutex); + sched_yield(); + pthread_mutex_lock(&fragment_mutex); + } + pthread_cleanup_pop(1); + + queue_put(to_writer, NULL); + if(queue_get(from_writer) != 0) + EXIT_MKSQUASHFS(); + + set_progressbar_state(FALSE); + write_filesystem_tables(&sBlk, nopad); + + return 0; +}
diff --git a/squashfs-tools/squashfs-tools/mksquashfs.h b/squashfs-tools/squashfs-tools/mksquashfs.h new file mode 100644 index 0000000..bfbf0bf --- /dev/null +++ b/squashfs-tools/squashfs-tools/mksquashfs.h
@@ -0,0 +1,165 @@ +#ifndef MKSQUASHFS_H +#define MKSQUASHFS_H +/* + * Squashfs + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 + * 2012, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * mksquashfs.h + * + */ +/* ANDROID CHANGES START*/ +#ifdef ANDROID +#include <stdint.h> +#endif +/* ANDROID CHANGES END */ +#include <pthread.h> + +struct dir_info { + char *pathname; + char *subpath; + unsigned int count; + unsigned int directory_count; + int depth; + unsigned int excluded; + char dir_is_ldir; + struct dir_ent *dir_ent; + struct dir_ent *list; + DIR *linuxdir; +}; + +struct dir_ent { + char *name; + char *source_name; + char *nonstandard_pathname; + struct inode_info *inode; + struct dir_info *dir; + struct dir_info *our_dir; + struct dir_ent *next; +/* ANDROID CHANGES START*/ +#ifdef ANDROID + uint64_t capabilities; +#endif +/* ANDROID CHANGES END */ +}; + +struct inode_info { + struct stat buf; + struct inode_info *next; + squashfs_inode inode; + unsigned int inode_number; + unsigned int nlink; + int pseudo_id; + char type; + char read; + char root_entry; + char pseudo_file; + char no_fragments; + char always_use_fragments; + char noD; + char noF; + char symlink[0]; +}; + +/* in memory file info */ +struct file_info { + long long file_size; + long long bytes; + long long start; + unsigned int *block_list; + struct file_info *next; + struct fragment *fragment; + unsigned short checksum; + unsigned short fragment_checksum; + char have_frag_checksum; + char have_checksum; +}; + +/* fragment block data structures */ +struct fragment { + unsigned int index; + int offset; + int size; +}; + +/* in memory uid tables */ +#define ID_ENTRIES 256 +#define ID_HASH(id) (id & (ID_ENTRIES - 1)) +#define ISA_UID 1 +#define ISA_GID 2 + +struct id { + unsigned int id; + int index; + char flags; + struct id *next; +}; + +/* fragment to file mapping used when appending */ +struct append_file { + struct file_info *file; + struct append_file *next; +}; + +#define PSEUDO_FILE_OTHER 1 +#define PSEUDO_FILE_PROCESS 2 + +#define IS_PSEUDO(a) ((a)->pseudo_file) +#define IS_PSEUDO_PROCESS(a) ((a)->pseudo_file & PSEUDO_FILE_PROCESS) +#define IS_PSEUDO_OTHER(a) ((a)->pseudo_file & PSEUDO_FILE_OTHER) + +/* + * Amount of physical memory to use by default, and the default queue + * ratios + */ +#define SQUASHFS_TAKE 4 +#define SQUASHFS_READQ_MEM 4 +#define SQUASHFS_BWRITEQ_MEM 4 +#define SQUASHFS_FWRITEQ_MEM 4 + +/* + * Lowest amount of physical memory considered viable for Mksquashfs + * to run in Mbytes + */ +#define SQUASHFS_LOWMEM 64 + +/* offset of data in compressed metadata blocks (allowing room for + * compressed size */ +#define BLOCK_OFFSET 2 + +extern struct cache *reader_buffer, *fragment_buffer, *reserve_cache; +struct cache *bwriter_buffer, *fwriter_buffer; +extern struct queue *to_reader, *to_deflate, *to_writer, *from_writer, + *to_frag, *locked_fragment, *to_process_frag; +extern struct append_file **file_mapping; +extern struct seq_queue *to_main; +extern pthread_mutex_t fragment_mutex, dup_mutex; +extern struct squashfs_fragment_entry *fragment_table; +extern struct compressor *comp; +extern int block_size; +extern struct file_info *dupl[]; +extern int read_fs_bytes(int, long long, int, void *); +extern void add_file(long long, long long, long long, unsigned int *, int, + unsigned int, int, int); +extern struct id *create_id(unsigned int); +extern unsigned int get_uid(unsigned int); +extern unsigned int get_guid(unsigned int); +extern int read_bytes(int, void *, int); +extern unsigned short get_checksum_mem(char *, int); +#endif
diff --git a/squashfs-tools/squashfs-tools/process_fragments.c b/squashfs-tools/squashfs-tools/process_fragments.c new file mode 100644 index 0000000..bba6f5a --- /dev/null +++ b/squashfs-tools/squashfs-tools/process_fragments.c
@@ -0,0 +1,370 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * process_fragments.c + */ + +#include <pthread.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <stdarg.h> +#include <errno.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "caches-queues-lists.h" +#include "squashfs_fs.h" +#include "mksquashfs.h" +#include "error.h" +#include "progressbar.h" +#include "info.h" +#include "compressor.h" +#include "process_fragments.h" + +#define FALSE 0 +#define TRUE 1 + +extern struct queue *to_process_frag; +extern struct seq_queue *to_main; +extern int sparse_files; + +/* + * Compute 16 bit BSD checksum over the data, and check for sparseness + */ +static int checksum_sparse(struct file_buffer *file_buffer) +{ + unsigned char *b = (unsigned char *) file_buffer->data; + unsigned short chksum = 0; + int bytes = file_buffer->size, sparse = TRUE, value; + + while(bytes --) { + chksum = (chksum & 1) ? (chksum >> 1) | 0x8000 : chksum >> 1; + value = *b++; + if(value) { + sparse = FALSE; + chksum += value; + } + } + + file_buffer->checksum = chksum; + return sparse; +} + + +static int read_filesystem(int fd, long long byte, int bytes, void *buff) +{ + off_t off = byte; + + TRACE("read_filesystem: reading from position 0x%llx, bytes %d\n", + byte, bytes); + + if(lseek(fd, off, SEEK_SET) == -1) { + ERROR("read_filesystem: Lseek on destination failed because %s, " + "offset=0x%llx\n", strerror(errno), off); + return 0; + } else if(read_bytes(fd, buff, bytes) < bytes) { + ERROR("Read on destination failed\n"); + return 0; + } + + return 1; +} + + +static struct file_buffer *get_fragment(struct fragment *fragment, + char *data_buffer, int fd) +{ + struct squashfs_fragment_entry *disk_fragment; + struct file_buffer *buffer, *compressed_buffer; + long long start_block; + int res, size, index = fragment->index; + char locked; + + /* + * Lookup fragment block in cache. + * If the fragment block doesn't exist, then get the compressed version + * from the writer cache or off disk, and decompress it. + * + * This routine has two things which complicate the code: + * + * 1. Multiple threads can simultaneously lookup/create the + * same buffer. This means a buffer needs to be "locked" + * when it is being filled in, to prevent other threads from + * using it when it is not ready. This is because we now do + * fragment duplicate checking in parallel. + * 2. We have two caches which need to be checked for the + * presence of fragment blocks: the normal fragment cache + * and a "reserve" cache. The reserve cache is used to + * prevent an unnecessary pipeline stall when the fragment cache + * is full of fragments waiting to be compressed. + */ + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + pthread_mutex_lock(&dup_mutex); + +again: + buffer = cache_lookup_nowait(fragment_buffer, index, &locked); + if(buffer) { + pthread_mutex_unlock(&dup_mutex); + if(locked) + /* got a buffer being filled in. Wait for it */ + cache_wait_unlock(buffer); + goto finished; + } + + /* not in fragment cache, is it in the reserve cache? */ + buffer = cache_lookup_nowait(reserve_cache, index, &locked); + if(buffer) { + pthread_mutex_unlock(&dup_mutex); + if(locked) + /* got a buffer being filled in. Wait for it */ + cache_wait_unlock(buffer); + goto finished; + } + + /* in neither cache, try to get it from the fragment cache */ + buffer = cache_get_nowait(fragment_buffer, index); + if(!buffer) { + /* + * no room, get it from the reserve cache, this is + * dimensioned so it will always have space (no more than + * processors + 1 can have an outstanding reserve buffer) + */ + buffer = cache_get_nowait(reserve_cache, index); + if(!buffer) { + /* failsafe */ + ERROR("no space in reserve cache\n"); + goto again; + } + } + + pthread_mutex_unlock(&dup_mutex); + + compressed_buffer = cache_lookup(fwriter_buffer, index); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex); + pthread_mutex_lock(&fragment_mutex); + disk_fragment = &fragment_table[index]; + size = SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment->size); + start_block = disk_fragment->start_block; + pthread_cleanup_pop(1); + + if(SQUASHFS_COMPRESSED_BLOCK(disk_fragment->size)) { + int error; + char *data; + + if(compressed_buffer) + data = compressed_buffer->data; + else { + res = read_filesystem(fd, start_block, size, data_buffer); + if(res == 0) { + ERROR("Failed to read fragment from output" + " filesystem\n"); + BAD_ERROR("Output filesystem corrupted?\n"); + } + data = data_buffer; + } + + res = compressor_uncompress(comp, buffer->data, data, size, + block_size, &error); + if(res == -1) + BAD_ERROR("%s uncompress failed with error code %d\n", + comp->name, error); + } else if(compressed_buffer) + memcpy(buffer->data, compressed_buffer->data, size); + else { + res = read_filesystem(fd, start_block, size, buffer->data); + if(res == 0) { + ERROR("Failed to read fragment from output " + "filesystem\n"); + BAD_ERROR("Output filesystem corrupted?\n"); + } + } + + cache_unlock(buffer); + cache_block_put(compressed_buffer); + +finished: + pthread_cleanup_pop(0); + + return buffer; +} + + +struct file_buffer *get_fragment_cksum(struct file_info *file, + char *data_buffer, int fd, unsigned short *checksum) +{ + struct file_buffer *frag_buffer; + struct append_file *append; + int index = file->fragment->index; + + frag_buffer = get_fragment(file->fragment, data_buffer, fd); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + + for(append = file_mapping[index]; append; append = append->next) { + int offset = append->file->fragment->offset; + int size = append->file->fragment->size; + char *data = frag_buffer->data + offset; + unsigned short cksum = get_checksum_mem(data, size); + + if(file == append->file) + *checksum = cksum; + + pthread_mutex_lock(&dup_mutex); + append->file->fragment_checksum = cksum; + append->file->have_frag_checksum = TRUE; + pthread_mutex_unlock(&dup_mutex); + } + + pthread_cleanup_pop(0); + + return frag_buffer; +} + + +void *frag_thrd(void *destination_file) +{ + sigset_t sigmask, old_mask; + char *data_buffer; + int fd; + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGUSR1); + pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask); + + fd = open(destination_file, O_RDONLY); + if(fd == -1) + BAD_ERROR("frag_thrd: can't open destination for reading\n"); + + data_buffer = malloc(SQUASHFS_FILE_MAX_SIZE); + if(data_buffer == NULL) + MEM_ERROR(); + + pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex); + + while(1) { + struct file_buffer *file_buffer = queue_get(to_process_frag); + struct file_buffer *buffer; + int sparse = checksum_sparse(file_buffer); + struct file_info *dupl_ptr; + long long file_size; + unsigned short checksum; + char flag; + int res; + + if(sparse_files && sparse) { + file_buffer->c_byte = 0; + file_buffer->fragment = FALSE; + } else + file_buffer->c_byte = file_buffer->size; + + /* + * Specutively pull into the fragment cache any fragment blocks + * which contain fragments which *this* fragment may be + * be a duplicate. + * + * By ensuring the fragment block is in cache ahead of time + * should eliminate the parallelisation stall when the + * main thread needs to read the fragment block to do a + * duplicate check on it. + * + * If this is a fragment belonging to a larger file + * (with additional blocks) then ignore it. Here we're + * interested in the "low hanging fruit" of files which + * consist of only a fragment + */ + if(file_buffer->file_size != file_buffer->size) { + seq_queue_put(to_main, file_buffer); + continue; + } + + file_size = file_buffer->file_size; + + pthread_mutex_lock(&dup_mutex); + dupl_ptr = dupl[DUP_HASH(file_size)]; + pthread_mutex_unlock(&dup_mutex); + + file_buffer->dupl_start = dupl_ptr; + file_buffer->duplicate = FALSE; + + for(; dupl_ptr; dupl_ptr = dupl_ptr->next) { + if(file_size != dupl_ptr->file_size || + file_size != dupl_ptr->fragment->size) + continue; + + pthread_mutex_lock(&dup_mutex); + flag = dupl_ptr->have_frag_checksum; + checksum = dupl_ptr->fragment_checksum; + pthread_mutex_unlock(&dup_mutex); + + /* + * If we have the checksum and it matches then + * read in the fragment block. + * + * If we *don't* have the checksum, then we are + * appending, and the fragment block is on the + * "old" filesystem. Read it in and checksum + * the entire fragment buffer + */ + if(!flag) { + buffer = get_fragment_cksum(dupl_ptr, + data_buffer, fd, &checksum); + if(checksum != file_buffer->checksum) { + cache_block_put(buffer); + continue; + } + } else if(checksum == file_buffer->checksum) + buffer = get_fragment(dupl_ptr->fragment, + data_buffer, fd); + else + continue; + + res = memcmp(file_buffer->data, buffer->data + + dupl_ptr->fragment->offset, file_size); + cache_block_put(buffer); + if(res == 0) { + struct file_buffer *dup = malloc(sizeof(*dup)); + if(dup == NULL) + MEM_ERROR(); + memcpy(dup, file_buffer, sizeof(*dup)); + cache_block_put(file_buffer); + dup->dupl_start = dupl_ptr; + dup->duplicate = TRUE; + file_buffer = dup; + break; + } + } + + seq_queue_put(to_main, file_buffer); + } + + pthread_cleanup_pop(0); +}
diff --git a/squashfs-tools/squashfs-tools/process_fragments.h b/squashfs-tools/squashfs-tools/process_fragments.h new file mode 100644 index 0000000..6f6bdf2 --- /dev/null +++ b/squashfs-tools/squashfs-tools/process_fragments.h
@@ -0,0 +1,30 @@ +#ifndef PROCESS_FRAGMENTS_H +#define PROCESS_FRAGMENTS_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * process_fragments.h + */ + +#define DUP_HASH(a) (a & 0xffff) + +extern void *frag_thrd(void *); +#endif
diff --git a/squashfs-tools/squashfs-tools/progressbar.c b/squashfs-tools/squashfs-tools/progressbar.c new file mode 100644 index 0000000..987a45b --- /dev/null +++ b/squashfs-tools/squashfs-tools/progressbar.c
@@ -0,0 +1,259 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2012, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * progressbar.c + */ + +#include <pthread.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#include <stdio.h> +#include <math.h> +#include <stdarg.h> +#include <errno.h> +#include <stdlib.h> + +#include "error.h" + +#define FALSE 0 +#define TRUE 1 + +/* flag whether progressbar display is enabled or not */ +int display_progress_bar = FALSE; + +/* flag whether the progress bar is temporarily disbled */ +int temp_disabled = FALSE; + +int rotate = 0; +int cur_uncompressed = 0, estimated_uncompressed = 0; +int columns; + +pthread_t progress_thread; +pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER; + + +static void sigwinch_handler() +{ + struct winsize winsize; + + if(ioctl(1, TIOCGWINSZ, &winsize) == -1) { + if(isatty(STDOUT_FILENO)) + ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 " + "columns\n"); + columns = 80; + } else + columns = winsize.ws_col; +} + + +static void sigalrm_handler() +{ + rotate = (rotate + 1) % 4; +} + + +void inc_progress_bar() +{ + cur_uncompressed ++; +} + + +void dec_progress_bar(int count) +{ + cur_uncompressed -= count; +} + + +void progress_bar_size(int count) +{ + estimated_uncompressed += count; +} + + +static void progress_bar(long long current, long long max, int columns) +{ + char rotate_list[] = { '|', '/', '-', '\\' }; + int max_digits, used, hashes, spaces; + static int tty = -1; + + if(max == 0) + return; + + max_digits = floor(log10(max)) + 1; + used = max_digits * 2 + 11; + hashes = (current * (columns - used)) / max; + spaces = columns - used - hashes; + + if((current > max) || (columns - used < 0)) + return; + + if(tty == -1) + tty = isatty(STDOUT_FILENO); + if(!tty) { + static long long previous = -1; + + /* Updating much more frequently than this results in huge + * log files. */ + if((current % 100) != 0 && current != max) + return; + /* Don't update just to rotate the spinner. */ + if(current == previous) + return; + previous = current; + } + + printf("\r["); + + while (hashes --) + putchar('='); + + putchar(rotate_list[rotate]); + + while(spaces --) + putchar(' '); + + printf("] %*lld/%*lld", max_digits, current, max_digits, max); + printf(" %3lld%%", current * 100 / max); + fflush(stdout); +} + + +void enable_progress_bar() +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex); + pthread_mutex_lock(&progress_mutex); + if(display_progress_bar) + progress_bar(cur_uncompressed, estimated_uncompressed, columns); + temp_disabled = FALSE; + pthread_cleanup_pop(1); +} + + +void disable_progress_bar() +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex); + pthread_mutex_lock(&progress_mutex); + if(display_progress_bar) + printf("\n"); + temp_disabled = TRUE; + pthread_cleanup_pop(1); +} + + +void set_progressbar_state(int state) +{ + pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex); + pthread_mutex_lock(&progress_mutex); + if(display_progress_bar != state) { + if(display_progress_bar && !temp_disabled) { + progress_bar(cur_uncompressed, estimated_uncompressed, + columns); + printf("\n"); + } + display_progress_bar = state; + } + pthread_cleanup_pop(1); +} + + +void *progress_thrd(void *arg) +{ + struct timespec requested_time, remaining; + struct itimerval itimerval; + struct winsize winsize; + + if(ioctl(1, TIOCGWINSZ, &winsize) == -1) { + if(isatty(STDOUT_FILENO)) + ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 " + "columns\n"); + columns = 80; + } else + columns = winsize.ws_col; + signal(SIGWINCH, sigwinch_handler); + signal(SIGALRM, sigalrm_handler); + + itimerval.it_value.tv_sec = 0; + itimerval.it_value.tv_usec = 250000; + itimerval.it_interval.tv_sec = 0; + itimerval.it_interval.tv_usec = 250000; + setitimer(ITIMER_REAL, &itimerval, NULL); + + requested_time.tv_sec = 0; + requested_time.tv_nsec = 250000000; + + while(1) { + int res = nanosleep(&requested_time, &remaining); + + if(res == -1 && errno != EINTR) + BAD_ERROR("nanosleep failed in progress thread\n"); + + pthread_mutex_lock(&progress_mutex); + if(display_progress_bar && !temp_disabled) + progress_bar(cur_uncompressed, estimated_uncompressed, + columns); + pthread_mutex_unlock(&progress_mutex); + } +} + + +void init_progress_bar() +{ + pthread_create(&progress_thread, NULL, progress_thrd, NULL); +} + + +void progressbar_error(char *fmt, ...) +{ + va_list ap; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex); + pthread_mutex_lock(&progress_mutex); + + if(display_progress_bar && !temp_disabled) + fprintf(stderr, "\n"); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + pthread_cleanup_pop(1); +} + + +void progressbar_info(char *fmt, ...) +{ + va_list ap; + + pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex); + pthread_mutex_lock(&progress_mutex); + + if(display_progress_bar && !temp_disabled) + printf("\n"); + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + pthread_cleanup_pop(1); +} +
diff --git a/squashfs-tools/squashfs-tools/progressbar.h b/squashfs-tools/squashfs-tools/progressbar.h new file mode 100644 index 0000000..b33b367 --- /dev/null +++ b/squashfs-tools/squashfs-tools/progressbar.h
@@ -0,0 +1,34 @@ +#ifndef PROGRESSBAR_H +#define PROGRESSBAR_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2012, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * progressbar.h + */ + +extern void inc_progress_bar(); +extern void dec_progress_bar(int count); +extern void progress_bar_size(int count); +extern void enable_progress_bar(); +extern void disable_progress_bar(); +extern void init_progress_bar(); +extern void set_progressbar_state(int); +#endif
diff --git a/squashfs-tools/squashfs-tools/pseudo.c b/squashfs-tools/squashfs-tools/pseudo.c new file mode 100644 index 0000000..e12d399 --- /dev/null +++ b/squashfs-tools/squashfs-tools/pseudo.c
@@ -0,0 +1,529 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2009, 2010, 2012, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * pseudo.c + */ + +#include <pwd.h> +#include <grp.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <ctype.h> + +#include "pseudo.h" +#include "error.h" +#include "progressbar.h" + +#define TRUE 1 +#define FALSE 0 + +extern int read_file(char *filename, char *type, int (parse_line)(char *)); + +struct pseudo_dev **pseudo_file = NULL; +struct pseudo *pseudo = NULL; +int pseudo_count = 0; + +static char *get_component(char *target, char **targname) +{ + char *start; + + while(*target == '/') + target ++; + + start = target; + while(*target != '/' && *target != '\0') + target ++; + + *targname = strndup(start, target - start); + + while(*target == '/') + target ++; + + return target; +} + + +/* + * Add pseudo device target to the set of pseudo devices. Pseudo_dev + * describes the pseudo device attributes. + */ +struct pseudo *add_pseudo(struct pseudo *pseudo, struct pseudo_dev *pseudo_dev, + char *target, char *alltarget) +{ + char *targname; + int i; + + target = get_component(target, &targname); + + if(pseudo == NULL) { + pseudo = malloc(sizeof(struct pseudo)); + if(pseudo == NULL) + MEM_ERROR(); + + pseudo->names = 0; + pseudo->count = 0; + pseudo->name = NULL; + } + + for(i = 0; i < pseudo->names; i++) + if(strcmp(pseudo->name[i].name, targname) == 0) + break; + + if(i == pseudo->names) { + /* allocate new name entry */ + pseudo->names ++; + pseudo->name = realloc(pseudo->name, (i + 1) * + sizeof(struct pseudo_entry)); + if(pseudo->name == NULL) + MEM_ERROR(); + pseudo->name[i].name = targname; + + if(target[0] == '\0') { + /* at leaf pathname component */ + pseudo->name[i].pseudo = NULL; + pseudo->name[i].pathname = strdup(alltarget); + pseudo->name[i].dev = pseudo_dev; + } else { + /* recurse adding child components */ + pseudo->name[i].dev = NULL; + pseudo->name[i].pseudo = add_pseudo(NULL, pseudo_dev, + target, alltarget); + } + } else { + /* existing matching entry */ + free(targname); + + if(pseudo->name[i].pseudo == NULL) { + /* No sub-directory which means this is the leaf + * component of a pre-existing pseudo file. + */ + if(target[0] != '\0') { + /* + * entry must exist as either a 'd' type or + * 'm' type pseudo file + */ + if(pseudo->name[i].dev->type == 'd' || + pseudo->name[i].dev->type == 'm') + /* recurse adding child components */ + pseudo->name[i].pseudo = + add_pseudo(NULL, pseudo_dev, + target, alltarget); + else { + ERROR_START("%s already exists as a " + "non directory.", + pseudo->name[i].name); + ERROR_EXIT(". Ignoring %s!\n", + alltarget); + } + } else if(memcmp(pseudo_dev, pseudo->name[i].dev, + sizeof(struct pseudo_dev)) != 0) { + ERROR_START("%s already exists as a different " + "pseudo definition.", alltarget); + ERROR_EXIT(" Ignoring!\n"); + } else { + ERROR_START("%s already exists as an identical " + "pseudo definition!", alltarget); + ERROR_EXIT(" Ignoring!\n"); + } + } else { + if(target[0] == '\0') { + /* + * sub-directory exists, which means we can only + * add a pseudo file of type 'd' or type 'm' + */ + if(pseudo->name[i].dev == NULL && + (pseudo_dev->type == 'd' || + pseudo_dev->type == 'm')) { + pseudo->name[i].pathname = + strdup(alltarget); + pseudo->name[i].dev = pseudo_dev; + } else { + ERROR_START("%s already exists as a " + "different pseudo definition.", + pseudo->name[i].name); + ERROR_EXIT(" Ignoring %s!\n", + alltarget); + } + } else + /* recurse adding child components */ + add_pseudo(pseudo->name[i].pseudo, pseudo_dev, + target, alltarget); + } + } + + return pseudo; +} + + +/* + * Find subdirectory in pseudo directory referenced by pseudo, matching + * filename. If filename doesn't exist or if filename is a leaf file + * return NULL + */ +struct pseudo *pseudo_subdir(char *filename, struct pseudo *pseudo) +{ + int i; + + if(pseudo == NULL) + return NULL; + + for(i = 0; i < pseudo->names; i++) + if(strcmp(filename, pseudo->name[i].name) == 0) + return pseudo->name[i].pseudo; + + return NULL; +} + + +struct pseudo_entry *pseudo_readdir(struct pseudo *pseudo) +{ + if(pseudo == NULL) + return NULL; + + while(pseudo->count < pseudo->names) { + if(pseudo->name[pseudo->count].dev != NULL) + return &pseudo->name[pseudo->count++]; + else + pseudo->count++; + } + + return NULL; +} + + +int pseudo_exec_file(struct pseudo_dev *dev, int *child) +{ + int res, pipefd[2]; + + res = pipe(pipefd); + if(res == -1) { + ERROR("Executing dynamic pseudo file, pipe failed\n"); + return 0; + } + + *child = fork(); + if(*child == -1) { + ERROR("Executing dynamic pseudo file, fork failed\n"); + goto failed; + } + + if(*child == 0) { + close(pipefd[0]); + close(STDOUT_FILENO); + res = dup(pipefd[1]); + if(res == -1) + exit(EXIT_FAILURE); + + execl("/bin/sh", "sh", "-c", dev->command, (char *) NULL); + exit(EXIT_FAILURE); + } + + close(pipefd[1]); + return pipefd[0]; + +failed: + close(pipefd[0]); + close(pipefd[1]); + return 0; +} + + +void add_pseudo_file(struct pseudo_dev *dev) +{ + pseudo_file = realloc(pseudo_file, (pseudo_count + 1) * + sizeof(struct pseudo_dev *)); + if(pseudo_file == NULL) + MEM_ERROR(); + + dev->pseudo_id = pseudo_count; + pseudo_file[pseudo_count ++] = dev; +} + + +struct pseudo_dev *get_pseudo_file(int pseudo_id) +{ + return pseudo_file[pseudo_id]; +} + + +int read_pseudo_def(char *def) +{ + int n, bytes; + unsigned int major = 0, minor = 0, mode; + char type, *ptr; + char suid[100], sgid[100]; /* overflow safe */ + char *filename, *name; + char *orig_def = def; + long long uid, gid; + struct pseudo_dev *dev; + + /* + * Scan for filename, don't use sscanf() and "%s" because + * that can't handle filenames with spaces + */ + filename = malloc(strlen(def) + 1); + if(filename == NULL) + MEM_ERROR(); + + for(name = filename; !isspace(*def) && *def != '\0';) { + if(*def == '\\') { + def ++; + if (*def == '\0') + break; + } + *name ++ = *def ++; + } + *name = '\0'; + + if(*filename == '\0') { + ERROR("Not enough or invalid arguments in pseudo file " + "definition \"%s\"\n", orig_def); + goto error; + } + + n = sscanf(def, " %c %o %99s %99s %n", &type, &mode, suid, sgid, + &bytes); + def += bytes; + + if(n < 4) { + ERROR("Not enough or invalid arguments in pseudo file " + "definition \"%s\"\n", orig_def); + switch(n) { + case -1: + /* FALLTHROUGH */ + case 0: + ERROR("Read filename, but failed to read or match " + "type\n"); + break; + case 1: + ERROR("Read filename and type, but failed to read or " + "match octal mode\n"); + break; + case 2: + ERROR("Read filename, type and mode, but failed to " + "read or match uid\n"); + break; + default: + ERROR("Read filename, type, mode and uid, but failed " + "to read or match gid\n"); + break; + } + goto error; + } + + switch(type) { + case 'b': + /* FALLTHROUGH */ + case 'c': + n = sscanf(def, "%u %u %n", &major, &minor, &bytes); + def += bytes; + + if(n < 2) { + ERROR("Not enough or invalid arguments in %s device " + "pseudo file definition \"%s\"\n", type == 'b' ? + "block" : "character", orig_def); + if(n < 1) + ERROR("Read filename, type, mode, uid and gid, " + "but failed to read or match major\n"); + else + ERROR("Read filename, type, mode, uid, gid " + "and major, but failed to read or " + "match minor\n"); + goto error; + } + + if(major > 0xfff) { + ERROR("Major %d out of range\n", major); + goto error; + } + + if(minor > 0xfffff) { + ERROR("Minor %d out of range\n", minor); + goto error; + } + /* FALLTHROUGH */ + case 'd': + /* FALLTHROUGH */ + case 'm': + /* + * Check for trailing junk after expected arguments + */ + if(def[0] != '\0') { + ERROR("Unexpected tailing characters in pseudo file " + "definition \"%s\"\n", orig_def); + goto error; + } + break; + case 'f': + if(def[0] == '\0') { + ERROR("Not enough arguments in dynamic file pseudo " + "definition \"%s\"\n", orig_def); + ERROR("Expected command, which can be an executable " + "or a piece of shell script\n"); + goto error; + } + break; + default: + ERROR("Unsupported type %c\n", type); + goto error; + } + + + if(mode > 07777) { + ERROR("Mode %o out of range\n", mode); + goto error; + } + + uid = strtoll(suid, &ptr, 10); + if(*ptr == '\0') { + if(uid < 0 || uid > ((1LL << 32) - 1)) { + ERROR("Uid %s out of range\n", suid); + goto error; + } + } else { + struct passwd *pwuid = getpwnam(suid); + if(pwuid) + uid = pwuid->pw_uid; + else { + ERROR("Uid %s invalid uid or unknown user\n", suid); + goto error; + } + } + + gid = strtoll(sgid, &ptr, 10); + if(*ptr == '\0') { + if(gid < 0 || gid > ((1LL << 32) - 1)) { + ERROR("Gid %s out of range\n", sgid); + goto error; + } + } else { + struct group *grgid = getgrnam(sgid); + if(grgid) + gid = grgid->gr_gid; + else { + ERROR("Gid %s invalid uid or unknown user\n", sgid); + goto error; + } + } + + switch(type) { + case 'b': + mode |= S_IFBLK; + break; + case 'c': + mode |= S_IFCHR; + break; + case 'd': + mode |= S_IFDIR; + break; + case 'f': + mode |= S_IFREG; + break; + } + + dev = malloc(sizeof(struct pseudo_dev)); + if(dev == NULL) + MEM_ERROR(); + + dev->type = type; + dev->mode = mode; + dev->uid = uid; + dev->gid = gid; + dev->major = major; + dev->minor = minor; + if(type == 'f') { + dev->command = strdup(def); + add_pseudo_file(dev); + } + + pseudo = add_pseudo(pseudo, dev, filename, filename); + + free(filename); + return TRUE; + +error: + ERROR("Pseudo definitions should be of format\n"); + ERROR("\tfilename d mode uid gid\n"); + ERROR("\tfilename m mode uid gid\n"); + ERROR("\tfilename b mode uid gid major minor\n"); + ERROR("\tfilename c mode uid gid major minor\n"); + ERROR("\tfilename f mode uid command\n"); + free(filename); + return FALSE; +} + + +int read_pseudo_file(char *filename) +{ + return read_file(filename, "pseudo", read_pseudo_def); +} + + +struct pseudo *get_pseudo() +{ + return pseudo; +} + + +#ifdef SQUASHFS_TRACE +static void dump_pseudo(struct pseudo *pseudo, char *string) +{ + int i, res; + char *path; + + for(i = 0; i < pseudo->names; i++) { + struct pseudo_entry *entry = &pseudo->name[i]; + if(string) { + res = asprintf(&path, "%s/%s", string, entry->name); + if(res == -1) + BAD_ERROR("asprintf failed in dump_pseudo\n"); + } else + path = entry->name; + if(entry->dev) + ERROR("%s %c 0%o %d %d %d %d\n", path, entry->dev->type, + entry->dev->mode & ~S_IFMT, entry->dev->uid, + entry->dev->gid, entry->dev->major, + entry->dev->minor); + if(entry->pseudo) + dump_pseudo(entry->pseudo, path); + if(string) + free(path); + } +} + + +void dump_pseudos() +{ + if (pseudo) + dump_pseudo(pseudo, NULL); +} +#else +void dump_pseudos() +{ +} +#endif
diff --git a/squashfs-tools/squashfs-tools/pseudo.h b/squashfs-tools/squashfs-tools/pseudo.h new file mode 100644 index 0000000..c0d9f6f --- /dev/null +++ b/squashfs-tools/squashfs-tools/pseudo.h
@@ -0,0 +1,58 @@ +#ifndef PSEUDO_H +#define PSEUDO_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2009, 2010, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * pseudo.h + */ +struct pseudo_dev { + char type; + unsigned int mode; + unsigned int uid; + unsigned int gid; + unsigned int major; + unsigned int minor; + int pseudo_id; + char *command; +}; + +struct pseudo_entry { + char *name; + char *pathname; + struct pseudo *pseudo; + struct pseudo_dev *dev; +}; + +struct pseudo { + int names; + int count; + struct pseudo_entry *name; +}; + +extern int read_pseudo_def(char *); +extern int read_pseudo_file(char *); +extern struct pseudo *pseudo_subdir(char *, struct pseudo *); +extern struct pseudo_entry *pseudo_readdir(struct pseudo *); +extern struct pseudo_dev *get_pseudo_file(int); +extern int pseudo_exec_file(struct pseudo_dev *, int *); +extern struct pseudo *get_pseudo(); +extern void dump_pseudos(); +#endif
diff --git a/squashfs-tools/squashfs-tools/read_file.c b/squashfs-tools/squashfs-tools/read_file.c new file mode 100644 index 0000000..22705a0 --- /dev/null +++ b/squashfs-tools/squashfs-tools/read_file.c
@@ -0,0 +1,150 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2012 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * read_file.c + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "error.h" + +#define TRUE 1 +#define FALSE 0 +#define MAX_LINE 16384 + +/* + * Read file, passing each line to parse_line() for + * parsing. + * + * Lines can be split across multiple lines using "\". + * + * Blank lines and comment lines indicated by # are supported. + */ +int read_file(char *filename, char *type, int (parse_line)(char *)) +{ + FILE *fd; + char *def, *err, *line = NULL; + int res, size = 0; + + fd = fopen(filename, "r"); + if(fd == NULL) { + ERROR("Could not open %s device file \"%s\" because %s\n", + type, filename, strerror(errno)); + return FALSE; + } + + while(1) { + int total = 0; + + while(1) { + int len; + + if(total + (MAX_LINE + 1) > size) { + line = realloc(line, size += (MAX_LINE + 1)); + if(line == NULL) + MEM_ERROR(); + } + + err = fgets(line + total, MAX_LINE + 1, fd); + if(err == NULL) + break; + + len = strlen(line + total); + total += len; + + if(len == MAX_LINE && line[total - 1] != '\n') { + /* line too large */ + ERROR("Line too long when reading " + "%s file \"%s\", larger than " + "%d bytes\n", type, filename, MAX_LINE); + goto failed; + } + + /* + * Remove '\n' terminator if it exists (the last line + * in the file may not be '\n' terminated) + */ + if(len && line[total - 1] == '\n') { + line[-- total] = '\0'; + len --; + } + + /* + * If no line continuation then jump out to + * process line. Note, we have to be careful to + * check for "\\" (backslashed backslash) and to + * ensure we don't look at the previous line + */ + if(len == 0 || line[total - 1] != '\\' || (len >= 2 && + strcmp(line + total - 2, "\\\\") == 0)) + break; + else + total --; + } + + if(err == NULL) { + if(ferror(fd)) { + ERROR("Reading %s file \"%s\" failed " + "because %s\n", type, filename, + strerror(errno)); + goto failed; + } + + /* + * At EOF, normally we'll be finished, but, have to + * check for special case where we had "\" line + * continuation and then hit EOF immediately afterwards + */ + if(total == 0) + break; + else + line[total] = '\0'; + } + + /* Skip any leading whitespace */ + for(def = line; isspace(*def); def ++); + + /* if line is now empty after skipping characters, skip it */ + if(*def == '\0') + continue; + + /* if comment line, skip */ + if(*def == '#') + continue; + + res = parse_line(def); + if(res == FALSE) + goto failed; + } + + fclose(fd); + free(line); + return TRUE; + +failed: + fclose(fd); + free(line); + return FALSE; +}
diff --git a/squashfs-tools/squashfs-tools/read_fs.c b/squashfs-tools/squashfs-tools/read_fs.c new file mode 100644 index 0000000..ca84460 --- /dev/null +++ b/squashfs-tools/squashfs-tools/read_fs.c
@@ -0,0 +1,980 @@ +/* + * Read a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + * 2012, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * read_fs.c + */ + +#define TRUE 1 +#define FALSE 0 +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <sys/mman.h> +#include <limits.h> +#include <dirent.h> + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include <endian.h> +#endif + +#include <stdlib.h> + +#include "squashfs_fs.h" +#include "squashfs_swap.h" +#include "compressor.h" +#include "xattr.h" +#include "error.h" +#include "mksquashfs.h" + +int read_block(int fd, long long start, long long *next, int expected, + void *block) +{ + unsigned short c_byte; + int res, compressed; + int outlen = expected ? expected : SQUASHFS_METADATA_SIZE; + + /* Read block size */ + res = read_fs_bytes(fd, start, 2, &c_byte); + if(res == 0) + return 0; + + SQUASHFS_INSWAP_SHORTS(&c_byte, 1); + compressed = SQUASHFS_COMPRESSED(c_byte); + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); + + /* + * The block size should not be larger than + * the uncompressed size (or max uncompressed size if + * expected is 0) + */ + if (c_byte > outlen) + return 0; + + if(compressed) { + char buffer[c_byte]; + int error; + + res = read_fs_bytes(fd, start + 2, c_byte, buffer); + if(res == 0) + return 0; + + res = compressor_uncompress(comp, block, buffer, c_byte, + outlen, &error); + if(res == -1) { + ERROR("%s uncompress failed with error code %d\n", + comp->name, error); + return 0; + } + } else { + res = read_fs_bytes(fd, start + 2, c_byte, block); + if(res == 0) + return 0; + res = c_byte; + } + + if(next) + *next = start + 2 + c_byte; + + /* + * if expected, then check the (uncompressed) return data + * is of the expected size + */ + if(expected && expected != res) + return 0; + else + return res; +} + + +#define NO_BYTES(SIZE) \ + (bytes - (cur_ptr - *inode_table) < (SIZE)) + +#define NO_INODE_BYTES(INODE) NO_BYTES(sizeof(struct INODE)) + +int scan_inode_table(int fd, long long start, long long end, + long long root_inode_start, int root_inode_offset, + struct squashfs_super_block *sBlk, union squashfs_inode_header + *dir_inode, unsigned char **inode_table, unsigned int *root_inode_block, + unsigned int *root_inode_size, long long *uncompressed_file, + unsigned int *uncompressed_directory, int *file_count, int *sym_count, + int *dev_count, int *dir_count, int *fifo_count, int *sock_count, + unsigned int *id_table) +{ + unsigned char *cur_ptr; + int byte, files = 0; + unsigned int directory_start_block, bytes = 0, size = 0; + struct squashfs_base_inode_header base; + + TRACE("scan_inode_table: start 0x%llx, end 0x%llx, root_inode_start " + "0x%llx\n", start, end, root_inode_start); + + *root_inode_block = UINT_MAX; + while(start < end) { + if(start == root_inode_start) { + TRACE("scan_inode_table: read compressed block 0x%llx " + "containing root inode\n", start); + *root_inode_block = bytes; + } + if(size - bytes < SQUASHFS_METADATA_SIZE) { + *inode_table = realloc(*inode_table, size + += SQUASHFS_METADATA_SIZE); + if(*inode_table == NULL) + MEM_ERROR(); + } + TRACE("scan_inode_table: reading block 0x%llx\n", start); + byte = read_block(fd, start, &start, 0, *inode_table + bytes); + if(byte == 0) + goto corrupted; + + bytes += byte; + + /* If this is not the last metadata block in the inode table + * then it should be SQUASHFS_METADATA_SIZE in size. + * Note, we can't use expected in read_block() above for this + * because we don't know if this is the last block until + * after reading. + */ + if(start != end && byte != SQUASHFS_METADATA_SIZE) + goto corrupted; + } + + /* + * We expect to have found the metadata block containing the + * root inode in the above inode_table metadata block scan. If it + * hasn't been found then the filesystem is corrupted + */ + if(*root_inode_block == UINT_MAX) + goto corrupted; + + /* + * The number of bytes available after the root inode medata block + * should be at least the root inode offset + the size of a + * regular directory inode, if not the filesystem is corrupted + * + * +-----------------------+-----------------------+ + * | | directory | + * | | inode | + * +-----------------------+-----------------------+ + * ^ ^ ^ + * *root_inode_block root_inode_offset bytes + */ + if((bytes - *root_inode_block) < (root_inode_offset + + sizeof(struct squashfs_dir_inode_header))) + goto corrupted; + + /* + * Read last inode entry which is the root directory inode, and obtain + * the last directory start block index. This is used when calculating + * the total uncompressed directory size. The directory bytes in the + * last * block will be counted as normal. + * + * Note, the previous check ensures the following calculation won't + * underflow, and we won't access beyond the buffer + */ + *root_inode_size = bytes - (*root_inode_block + root_inode_offset); + bytes = *root_inode_block + root_inode_offset; + SQUASHFS_SWAP_DIR_INODE_HEADER(*inode_table + bytes, &dir_inode->dir); + + if(dir_inode->base.inode_type == SQUASHFS_DIR_TYPE) + directory_start_block = dir_inode->dir.start_block; + else if(dir_inode->base.inode_type == SQUASHFS_LDIR_TYPE) { + if(*root_inode_size < sizeof(struct squashfs_ldir_inode_header)) + /* corrupted filesystem */ + goto corrupted; + SQUASHFS_SWAP_LDIR_INODE_HEADER(*inode_table + bytes, + &dir_inode->ldir); + directory_start_block = dir_inode->ldir.start_block; + } else + /* bad type, corrupted filesystem */ + goto corrupted; + + get_uid(id_table[dir_inode->base.uid]); + get_guid(id_table[dir_inode->base.guid]); + + /* allocate fragment to file mapping table */ + file_mapping = calloc(sBlk->fragments, sizeof(struct append_file *)); + if(file_mapping == NULL) + MEM_ERROR(); + + for(cur_ptr = *inode_table; cur_ptr < *inode_table + bytes; files ++) { + if(NO_INODE_BYTES(squashfs_base_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + SQUASHFS_SWAP_BASE_INODE_HEADER(cur_ptr, &base); + + TRACE("scan_inode_table: processing inode @ byte position " + "0x%x, type 0x%x\n", + (unsigned int) (cur_ptr - *inode_table), + base.inode_type); + + get_uid(id_table[base.uid]); + get_guid(id_table[base.guid]); + + switch(base.inode_type) { + case SQUASHFS_FILE_TYPE: { + struct squashfs_reg_inode_header inode; + int frag_bytes, blocks, i; + long long start, file_bytes = 0; + unsigned int *block_list; + + if(NO_INODE_BYTES(squashfs_reg_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + SQUASHFS_SWAP_REG_INODE_HEADER(cur_ptr, &inode); + + frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ? + 0 : inode.file_size % sBlk->block_size; + blocks = inode.fragment == SQUASHFS_INVALID_FRAG ? + (inode.file_size + sBlk->block_size - 1) >> + sBlk->block_log : inode.file_size >> + sBlk->block_log; + start = inode.start_block; + + TRACE("scan_inode_table: regular file, file_size %d, " + "blocks %d\n", inode.file_size, blocks); + + if(NO_BYTES(blocks * sizeof(unsigned int))) + /* corrupted filesystem */ + goto corrupted; + + block_list = malloc(blocks * sizeof(unsigned int)); + if(block_list == NULL) + MEM_ERROR(); + + cur_ptr += sizeof(inode); + SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks); + + *uncompressed_file += inode.file_size; + (*file_count) ++; + + for(i = 0; i < blocks; i++) + file_bytes += + SQUASHFS_COMPRESSED_SIZE_BLOCK + (block_list[i]); + + if(inode.fragment != SQUASHFS_INVALID_FRAG && + inode.fragment >= sBlk->fragments) { + free(block_list); + goto corrupted; + } + + add_file(start, inode.file_size, file_bytes, + block_list, blocks, inode.fragment, + inode.offset, frag_bytes); + + cur_ptr += blocks * sizeof(unsigned int); + break; + } + case SQUASHFS_LREG_TYPE: { + struct squashfs_lreg_inode_header inode; + int frag_bytes, blocks, i; + long long start, file_bytes = 0; + unsigned int *block_list; + + if(NO_INODE_BYTES(squashfs_lreg_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + SQUASHFS_SWAP_LREG_INODE_HEADER(cur_ptr, &inode); + + frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ? + 0 : inode.file_size % sBlk->block_size; + blocks = inode.fragment == SQUASHFS_INVALID_FRAG ? + (inode.file_size + sBlk->block_size - 1) >> + sBlk->block_log : inode.file_size >> + sBlk->block_log; + start = inode.start_block; + + TRACE("scan_inode_table: extended regular " + "file, file_size %lld, blocks %d\n", + inode.file_size, blocks); + + if(NO_BYTES(blocks * sizeof(unsigned int))) + /* corrupted filesystem */ + goto corrupted; + + block_list = malloc(blocks * sizeof(unsigned int)); + if(block_list == NULL) + MEM_ERROR(); + + cur_ptr += sizeof(inode); + SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks); + + *uncompressed_file += inode.file_size; + (*file_count) ++; + + for(i = 0; i < blocks; i++) + file_bytes += + SQUASHFS_COMPRESSED_SIZE_BLOCK + (block_list[i]); + + if(inode.fragment != SQUASHFS_INVALID_FRAG && + inode.fragment >= sBlk->fragments) { + free(block_list); + goto corrupted; + } + + add_file(start, inode.file_size, file_bytes, + block_list, blocks, inode.fragment, + inode.offset, frag_bytes); + + cur_ptr += blocks * sizeof(unsigned int); + break; + } + case SQUASHFS_SYMLINK_TYPE: + case SQUASHFS_LSYMLINK_TYPE: { + struct squashfs_symlink_inode_header inode; + + if(NO_INODE_BYTES(squashfs_symlink_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + SQUASHFS_SWAP_SYMLINK_INODE_HEADER(cur_ptr, &inode); + + (*sym_count) ++; + + if (inode.inode_type == SQUASHFS_LSYMLINK_TYPE) { + if(NO_BYTES(inode.symlink_size + + sizeof(unsigned int))) + /* corrupted filesystem */ + goto corrupted; + cur_ptr += sizeof(inode) + inode.symlink_size + + sizeof(unsigned int); + } else { + if(NO_BYTES(inode.symlink_size)) + /* corrupted filesystem */ + goto corrupted; + cur_ptr += sizeof(inode) + inode.symlink_size; + } + break; + } + case SQUASHFS_DIR_TYPE: { + struct squashfs_dir_inode_header dir_inode; + + if(NO_INODE_BYTES(squashfs_dir_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + SQUASHFS_SWAP_DIR_INODE_HEADER(cur_ptr, &dir_inode); + + if(dir_inode.start_block < directory_start_block) + *uncompressed_directory += dir_inode.file_size; + + (*dir_count) ++; + cur_ptr += sizeof(struct squashfs_dir_inode_header); + break; + } + case SQUASHFS_LDIR_TYPE: { + struct squashfs_ldir_inode_header dir_inode; + int i; + + if(NO_INODE_BYTES(squashfs_ldir_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + SQUASHFS_SWAP_LDIR_INODE_HEADER(cur_ptr, &dir_inode); + + if(dir_inode.start_block < directory_start_block) + *uncompressed_directory += dir_inode.file_size; + + (*dir_count) ++; + cur_ptr += sizeof(struct squashfs_ldir_inode_header); + + for(i = 0; i < dir_inode.i_count; i++) { + struct squashfs_dir_index index; + + if(NO_BYTES(sizeof(index))) + /* corrupted filesystem */ + goto corrupted; + + SQUASHFS_SWAP_DIR_INDEX(cur_ptr, &index); + + if(NO_BYTES(index.size + 1)) + /* corrupted filesystem */ + goto corrupted; + + cur_ptr += sizeof(index) + index.size + 1; + } + break; + } + case SQUASHFS_BLKDEV_TYPE: + case SQUASHFS_CHRDEV_TYPE: + if(NO_INODE_BYTES(squashfs_dev_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + (*dev_count) ++; + cur_ptr += sizeof(struct squashfs_dev_inode_header); + break; + case SQUASHFS_LBLKDEV_TYPE: + case SQUASHFS_LCHRDEV_TYPE: + if(NO_INODE_BYTES(squashfs_ldev_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + (*dev_count) ++; + cur_ptr += sizeof(struct squashfs_ldev_inode_header); + break; + case SQUASHFS_FIFO_TYPE: + if(NO_INODE_BYTES(squashfs_ipc_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + (*fifo_count) ++; + cur_ptr += sizeof(struct squashfs_ipc_inode_header); + break; + case SQUASHFS_LFIFO_TYPE: + if(NO_INODE_BYTES(squashfs_lipc_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + (*fifo_count) ++; + cur_ptr += sizeof(struct squashfs_lipc_inode_header); + break; + case SQUASHFS_SOCKET_TYPE: + if(NO_INODE_BYTES(squashfs_ipc_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + (*sock_count) ++; + cur_ptr += sizeof(struct squashfs_ipc_inode_header); + break; + case SQUASHFS_LSOCKET_TYPE: + if(NO_INODE_BYTES(squashfs_lipc_inode_header)) + /* corrupted filesystem */ + goto corrupted; + + (*sock_count) ++; + cur_ptr += sizeof(struct squashfs_lipc_inode_header); + break; + default: + ERROR("Unknown inode type %d in scan_inode_table!\n", + base.inode_type); + goto corrupted; + } + } + + printf("Read existing filesystem, %d inodes scanned\n", files); + return TRUE; + +corrupted: + ERROR("scan_inode_table: filesystem corruption detected in " + "scanning metadata\n"); + free(*inode_table); + return FALSE; +} + + +struct compressor *read_super(int fd, struct squashfs_super_block *sBlk, char *source) +{ + int res, bytes = 0; + char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned)); + + res = read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block), + sBlk); + if(res == 0) { + ERROR("Can't find a SQUASHFS superblock on %s\n", + source); + ERROR("Wrong filesystem or filesystem is corrupted!\n"); + goto failed_mount; + } + + SQUASHFS_INSWAP_SUPER_BLOCK(sBlk); + + if(sBlk->s_magic != SQUASHFS_MAGIC) { + if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP) + ERROR("Pre 4.0 big-endian filesystem on %s, appending" + " to this is unsupported\n", source); + else { + ERROR("Can't find a SQUASHFS superblock on %s\n", + source); + ERROR("Wrong filesystem or filesystem is corrupted!\n"); + } + goto failed_mount; + } + + /* Check the MAJOR & MINOR versions */ + if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) { + if(sBlk->s_major < 4) + ERROR("Filesystem on %s is a SQUASHFS %d.%d filesystem." + " Appending\nto SQUASHFS %d.%d filesystems is " + "not supported. Please convert it to a " + "SQUASHFS 4 filesystem\n", source, + sBlk->s_major, + sBlk->s_minor, sBlk->s_major, sBlk->s_minor); + else + ERROR("Filesystem on %s is %d.%d, which is a later " + "filesystem version than I support\n", + source, sBlk->s_major, sBlk->s_minor); + goto failed_mount; + } + + /* Check the compression type */ + comp = lookup_compressor_id(sBlk->compression); + if(!comp->supported) { + ERROR("Filesystem on %s uses %s compression, this is " + "unsupported by this version\n", source, comp->name); + ERROR("Compressors available:\n"); + display_compressors("", ""); + goto failed_mount; + } + + /* + * Read extended superblock information from disk. + * + * Read compressor specific options from disk if present, and pass + * to compressor to set compressor options. + * + * Note, if there's no compressor options present, the compressor + * is still called to set the default options (the defaults may have + * been changed by the user specifying options on the command + * line which need to be over-ridden). + * + * Compressor_extract_options is also used to ensure that + * we know how decompress a filesystem compressed with these + * compression options. + */ + if(SQUASHFS_COMP_OPTS(sBlk->flags)) { + bytes = read_block(fd, sizeof(*sBlk), NULL, 0, buffer); + + if(bytes == 0) { + ERROR("Failed to read compressor options from append " + "filesystem\n"); + ERROR("Filesystem corrupted?\n"); + goto failed_mount; + } + } + + res = compressor_extract_options(comp, sBlk->block_size, buffer, bytes); + if(res == -1) { + ERROR("Compressor failed to set compressor options\n"); + goto failed_mount; + } + + printf("Found a valid %sSQUASHFS superblock on %s.\n", + SQUASHFS_EXPORTABLE(sBlk->flags) ? "exportable " : "", source); + printf("\tCompression used %s\n", comp->name); + printf("\tInodes are %scompressed\n", + SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : ""); + printf("\tData is %scompressed\n", + SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : ""); + printf("\tFragments are %scompressed\n", + SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk->flags) ? "un" : ""); + printf("\tXattrs are %scompressed\n", + SQUASHFS_UNCOMPRESSED_XATTRS(sBlk->flags) ? "un" : ""); + printf("\tFragments are %spresent in the filesystem\n", + SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not " : ""); + printf("\tAlways-use-fragments option is %sspecified\n", + SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not "); + printf("\tDuplicates are %sremoved\n", + SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not "); + printf("\tXattrs are %sstored\n", + SQUASHFS_NO_XATTRS(sBlk->flags) ? "not " : ""); + printf("\tFilesystem size %.2f Kbytes (%.2f Mbytes)\n", + sBlk->bytes_used / 1024.0, sBlk->bytes_used + / (1024.0 * 1024.0)); + printf("\tBlock size %d\n", sBlk->block_size); + printf("\tNumber of fragments %d\n", sBlk->fragments); + printf("\tNumber of inodes %d\n", sBlk->inodes); + printf("\tNumber of ids %d\n", sBlk->no_ids); + TRACE("sBlk->inode_table_start %llx\n", sBlk->inode_table_start); + TRACE("sBlk->directory_table_start %llx\n", + sBlk->directory_table_start); + TRACE("sBlk->id_table_start %llx\n", sBlk->id_table_start); + TRACE("sBlk->fragment_table_start %llx\n", sBlk->fragment_table_start); + TRACE("sBlk->lookup_table_start %llx\n", sBlk->lookup_table_start); + TRACE("sBlk->xattr_id_table_start %llx\n", sBlk->xattr_id_table_start); + printf("\n"); + + return comp; + +failed_mount: + return NULL; +} + + +unsigned char *squashfs_readdir(int fd, int root_entries, + unsigned int directory_start_block, int offset, int size, + unsigned int *last_directory_block, struct squashfs_super_block *sBlk, + void (push_directory_entry)(char *, squashfs_inode, int, int)) +{ + struct squashfs_dir_header dirh; + char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1] + __attribute__ ((aligned)); + struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; + unsigned char *directory_table = NULL; + int byte, bytes = 0, dir_count; + long long start = sBlk->directory_table_start + directory_start_block, + last_start_block = start; + + size += offset; + directory_table = malloc((size + SQUASHFS_METADATA_SIZE * 2 - 1) & + ~(SQUASHFS_METADATA_SIZE - 1)); + if(directory_table == NULL) + MEM_ERROR(); + + while(bytes < size) { + int expected = (size - bytes) >= SQUASHFS_METADATA_SIZE ? + SQUASHFS_METADATA_SIZE : 0; + + TRACE("squashfs_readdir: reading block 0x%llx, bytes read so " + "far %d\n", start, bytes); + + last_start_block = start; + byte = read_block(fd, start, &start, expected, directory_table + bytes); + if(byte == 0) { + ERROR("Failed to read directory\n"); + ERROR("Filesystem corrupted?\n"); + free(directory_table); + return NULL; + } + bytes += byte; + } + + if(!root_entries) + goto all_done; + + bytes = offset; + while(bytes < size) { + SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh); + + dir_count = dirh.count + 1; + TRACE("squashfs_readdir: Read directory header @ byte position " + "0x%x, 0x%x directory entries\n", bytes, dir_count); + bytes += sizeof(dirh); + + while(dir_count--) { + SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire); + bytes += sizeof(*dire); + + memcpy(dire->name, directory_table + bytes, + dire->size + 1); + dire->name[dire->size + 1] = '\0'; + TRACE("squashfs_readdir: pushing directory entry %s, " + "inode %x:%x, type 0x%x\n", dire->name, + dirh.start_block, dire->offset, dire->type); + push_directory_entry(dire->name, + SQUASHFS_MKINODE(dirh.start_block, + dire->offset), dirh.inode_number + + dire->inode_number, dire->type); + bytes += dire->size + 1; + } + } + +all_done: + *last_directory_block = (unsigned int) last_start_block - + sBlk->directory_table_start; + return directory_table; +} + + +unsigned int *read_id_table(int fd, struct squashfs_super_block *sBlk) +{ + int indexes = SQUASHFS_ID_BLOCKS(sBlk->no_ids); + long long index[indexes]; + int bytes = SQUASHFS_ID_BYTES(sBlk->no_ids); + unsigned int *id_table; + int res, i; + + id_table = malloc(bytes); + if(id_table == NULL) + MEM_ERROR(); + + res = read_fs_bytes(fd, sBlk->id_table_start, + SQUASHFS_ID_BLOCK_BYTES(sBlk->no_ids), index); + if(res == 0) { + ERROR("Failed to read id table index\n"); + ERROR("Filesystem corrupted?\n"); + free(id_table); + return NULL; + } + + SQUASHFS_INSWAP_ID_BLOCKS(index, indexes); + + for(i = 0; i < indexes; i++) { + int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE : + bytes & (SQUASHFS_METADATA_SIZE - 1); + int length = read_block(fd, index[i], NULL, expected, + ((unsigned char *) id_table) + + (i * SQUASHFS_METADATA_SIZE)); + TRACE("Read id table block %d, from 0x%llx, length %d\n", i, + index[i], length); + if(length == 0) { + ERROR("Failed to read id table block %d, from 0x%llx, " + "length %d\n", i, index[i], length); + ERROR("Filesystem corrupted?\n"); + free(id_table); + return NULL; + } + } + + SQUASHFS_INSWAP_INTS(id_table, sBlk->no_ids); + + for(i = 0; i < sBlk->no_ids; i++) { + TRACE("Adding id %d to id tables\n", id_table[i]); + create_id(id_table[i]); + } + + return id_table; +} + + +int read_fragment_table(int fd, struct squashfs_super_block *sBlk, + struct squashfs_fragment_entry **fragment_table) +{ + int res, i; + int bytes = SQUASHFS_FRAGMENT_BYTES(sBlk->fragments); + int indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments); + long long fragment_table_index[indexes]; + + TRACE("read_fragment_table: %d fragments, reading %d fragment indexes " + "from 0x%llx\n", sBlk->fragments, indexes, + sBlk->fragment_table_start); + + if(sBlk->fragments == 0) + return 1; + + *fragment_table = malloc(bytes); + if(*fragment_table == NULL) + MEM_ERROR(); + + res = read_fs_bytes(fd, sBlk->fragment_table_start, + SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), + fragment_table_index); + if(res == 0) { + ERROR("Failed to read fragment table index\n"); + ERROR("Filesystem corrupted?\n"); + free(*fragment_table); + return 0; + } + + SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index, indexes); + + for(i = 0; i < indexes; i++) { + int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE : + bytes & (SQUASHFS_METADATA_SIZE - 1); + int length = read_block(fd, fragment_table_index[i], NULL, + expected, ((unsigned char *) *fragment_table) + + (i * SQUASHFS_METADATA_SIZE)); + TRACE("Read fragment table block %d, from 0x%llx, length %d\n", + i, fragment_table_index[i], length); + if(length == 0) { + ERROR("Failed to read fragment table block %d, from " + "0x%llx, length %d\n", i, + fragment_table_index[i], length); + ERROR("Filesystem corrupted?\n"); + free(*fragment_table); + return 0; + } + } + + for(i = 0; i < sBlk->fragments; i++) + SQUASHFS_INSWAP_FRAGMENT_ENTRY(&(*fragment_table)[i]); + + return 1; +} + + +int read_inode_lookup_table(int fd, struct squashfs_super_block *sBlk, + squashfs_inode **inode_lookup_table) +{ + int lookup_bytes = SQUASHFS_LOOKUP_BYTES(sBlk->inodes); + int indexes = SQUASHFS_LOOKUP_BLOCKS(sBlk->inodes); + long long index[indexes]; + int res, i; + + if(sBlk->lookup_table_start == SQUASHFS_INVALID_BLK) + return 1; + + *inode_lookup_table = malloc(lookup_bytes); + if(*inode_lookup_table == NULL) + MEM_ERROR(); + + res = read_fs_bytes(fd, sBlk->lookup_table_start, + SQUASHFS_LOOKUP_BLOCK_BYTES(sBlk->inodes), index); + if(res == 0) { + ERROR("Failed to read inode lookup table index\n"); + ERROR("Filesystem corrupted?\n"); + free(*inode_lookup_table); + return 0; + } + + SQUASHFS_INSWAP_LONG_LONGS(index, indexes); + + for(i = 0; i < indexes; i++) { + int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE : + lookup_bytes & (SQUASHFS_METADATA_SIZE - 1); + int length = read_block(fd, index[i], NULL, expected, + ((unsigned char *) *inode_lookup_table) + + (i * SQUASHFS_METADATA_SIZE)); + TRACE("Read inode lookup table block %d, from 0x%llx, length " + "%d\n", i, index[i], length); + if(length == 0) { + ERROR("Failed to read inode lookup table block %d, " + "from 0x%llx, length %d\n", i, index[i], + length); + ERROR("Filesystem corrupted?\n"); + free(*inode_lookup_table); + return 0; + } + } + + SQUASHFS_INSWAP_LONG_LONGS(*inode_lookup_table, sBlk->inodes); + + return 1; +} + + +long long read_filesystem(char *root_name, int fd, struct squashfs_super_block *sBlk, + char **cinode_table, char **data_cache, char **cdirectory_table, + char **directory_data_cache, unsigned int *last_directory_block, + unsigned int *inode_dir_offset, unsigned int *inode_dir_file_size, + unsigned int *root_inode_size, unsigned int *inode_dir_start_block, + int *file_count, int *sym_count, int *dev_count, int *dir_count, + int *fifo_count, int *sock_count, long long *uncompressed_file, + unsigned int *uncompressed_inode, unsigned int *uncompressed_directory, + unsigned int *inode_dir_inode_number, + unsigned int *inode_dir_parent_inode, + void (push_directory_entry)(char *, squashfs_inode, int, int), + struct squashfs_fragment_entry **fragment_table, + squashfs_inode **inode_lookup_table) +{ + unsigned char *inode_table = NULL, *directory_table = NULL; + long long start = sBlk->inode_table_start; + long long end = sBlk->directory_table_start; + long long root_inode_start = start + + SQUASHFS_INODE_BLK(sBlk->root_inode); + unsigned int root_inode_offset = + SQUASHFS_INODE_OFFSET(sBlk->root_inode); + unsigned int root_inode_block; + union squashfs_inode_header inode; + unsigned int *id_table = NULL; + int res; + + printf("Scanning existing filesystem...\n"); + + if(get_xattrs(fd, sBlk) == 0) + goto error; + + if(read_fragment_table(fd, sBlk, fragment_table) == 0) + goto error; + + if(read_inode_lookup_table(fd, sBlk, inode_lookup_table) == 0) + goto error; + + id_table = read_id_table(fd, sBlk); + if(id_table == NULL) + goto error; + + res = scan_inode_table(fd, start, end, root_inode_start, + root_inode_offset, sBlk, &inode, &inode_table, + &root_inode_block, root_inode_size, uncompressed_file, + uncompressed_directory, file_count, sym_count, dev_count, + dir_count, fifo_count, sock_count, id_table); + if(res == 0) + goto error; + + *uncompressed_inode = root_inode_block; + + if(inode.base.inode_type == SQUASHFS_DIR_TYPE || + inode.base.inode_type == SQUASHFS_LDIR_TYPE) { + if(inode.base.inode_type == SQUASHFS_DIR_TYPE) { + *inode_dir_start_block = inode.dir.start_block; + *inode_dir_offset = inode.dir.offset; + *inode_dir_file_size = inode.dir.file_size - 3; + *inode_dir_inode_number = inode.dir.inode_number; + *inode_dir_parent_inode = inode.dir.parent_inode; + } else { + *inode_dir_start_block = inode.ldir.start_block; + *inode_dir_offset = inode.ldir.offset; + *inode_dir_file_size = inode.ldir.file_size - 3; + *inode_dir_inode_number = inode.ldir.inode_number; + *inode_dir_parent_inode = inode.ldir.parent_inode; + } + + directory_table = squashfs_readdir(fd, !root_name, + *inode_dir_start_block, *inode_dir_offset, + *inode_dir_file_size, last_directory_block, sBlk, + push_directory_entry); + if(directory_table == NULL) + goto error; + + root_inode_start -= start; + *cinode_table = malloc(root_inode_start); + if(*cinode_table == NULL) + MEM_ERROR(); + + res = read_fs_bytes(fd, start, root_inode_start, *cinode_table); + if(res == 0) { + ERROR("Failed to read inode table\n"); + ERROR("Filesystem corrupted?\n"); + goto error; + } + + *cdirectory_table = malloc(*last_directory_block); + if(*cdirectory_table == NULL) + MEM_ERROR(); + + res = read_fs_bytes(fd, sBlk->directory_table_start, + *last_directory_block, *cdirectory_table); + if(res == 0) { + ERROR("Failed to read directory table\n"); + ERROR("Filesystem corrupted?\n"); + goto error; + } + + *data_cache = malloc(root_inode_offset + *root_inode_size); + if(*data_cache == NULL) + MEM_ERROR(); + + memcpy(*data_cache, inode_table + root_inode_block, + root_inode_offset + *root_inode_size); + + *directory_data_cache = malloc(*inode_dir_offset + + *inode_dir_file_size); + if(*directory_data_cache == NULL) + MEM_ERROR(); + + memcpy(*directory_data_cache, directory_table, + *inode_dir_offset + *inode_dir_file_size); + + free(id_table); + free(inode_table); + free(directory_table); + return sBlk->inode_table_start; + } + +error: + free(id_table); + free(inode_table); + free(directory_table); + return 0; +}
diff --git a/squashfs-tools/squashfs-tools/read_fs.h b/squashfs-tools/squashfs-tools/read_fs.h new file mode 100644 index 0000000..9ad32c0 --- /dev/null +++ b/squashfs-tools/squashfs-tools/read_fs.h
@@ -0,0 +1,34 @@ +#ifndef READ_FS_H +#define READ_FS_H +/* + * Squashfs + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * read_fs.h + * + */ +extern struct compressor *read_super(int, struct squashfs_super_block *, + char *); +extern long long read_filesystem(char *, int, struct squashfs_super_block *, +char **, char **, char **, char **, unsigned int *, unsigned int *, +unsigned int *, unsigned int *, unsigned int *, int *, int *, int *, int *, +int *, int *, long long *, unsigned int *, unsigned int *, unsigned int *, +unsigned int *, void (push_directory_entry)(char *, squashfs_inode, int, int), +struct squashfs_fragment_entry **, squashfs_inode **); +#endif
diff --git a/squashfs-tools/squashfs-tools/read_xattrs.c b/squashfs-tools/squashfs-tools/read_xattrs.c new file mode 100644 index 0000000..837d3fb --- /dev/null +++ b/squashfs-tools/squashfs-tools/read_xattrs.c
@@ -0,0 +1,390 @@ +/* + * Read a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2010, 2012, 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * read_xattrs.c + */ + +/* + * Common xattr read code shared between mksquashfs and unsquashfs + */ + +#define TRUE 1 +#define FALSE 0 +#include <stdio.h> +#include <string.h> + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include <endian.h> +#endif + +#include <stdlib.h> + +#include "squashfs_fs.h" +#include "squashfs_swap.h" +#include "xattr.h" +#include "error.h" + +extern int read_fs_bytes(int, long long, int, void *); +extern int read_block(int, long long, long long *, int, void *); + +static struct hash_entry { + long long start; + unsigned int offset; + struct hash_entry *next; +} *hash_table[65536]; + +static struct squashfs_xattr_id *xattr_ids; +static void *xattrs = NULL; +static long long xattr_table_start; + +/* + * Prefix lookup table, storing mapping to/from prefix string and prefix id + */ +struct prefix prefix_table[] = { + { "user.", SQUASHFS_XATTR_USER }, + { "trusted.", SQUASHFS_XATTR_TRUSTED }, + { "security.", SQUASHFS_XATTR_SECURITY }, + { "", -1 } +}; + +/* + * store mapping from location of compressed block in fs -> + * location of uncompressed block in memory + */ +static void save_xattr_block(long long start, int offset) +{ + struct hash_entry *hash_entry = malloc(sizeof(*hash_entry)); + int hash = start & 0xffff; + + TRACE("save_xattr_block: start %lld, offset %d\n", start, offset); + + if(hash_entry == NULL) + MEM_ERROR(); + + hash_entry->start = start; + hash_entry->offset = offset; + hash_entry->next = hash_table[hash]; + hash_table[hash] = hash_entry; +} + + +/* + * map from location of compressed block in fs -> + * location of uncompressed block in memory + */ +static int get_xattr_block(long long start) +{ + int hash = start & 0xffff; + struct hash_entry *hash_entry = hash_table[hash]; + + for(; hash_entry; hash_entry = hash_entry->next) + if(hash_entry->start == start) + break; + + TRACE("get_xattr_block: start %lld, offset %d\n", start, + hash_entry ? hash_entry->offset : -1); + + return hash_entry ? hash_entry->offset : -1; +} + + +/* + * construct the xattr_list entry from the fs xattr, including + * mapping name and prefix into a full name + */ +static int read_xattr_entry(struct xattr_list *xattr, + struct squashfs_xattr_entry *entry, void *name) +{ + int i, len, type = entry->type & XATTR_PREFIX_MASK; + + for(i = 0; prefix_table[i].type != -1; i++) + if(prefix_table[i].type == type) + break; + + if(prefix_table[i].type == -1) { + ERROR("Unrecognised type in read_xattr_entry\n"); + return 0; + } + + len = strlen(prefix_table[i].prefix); + xattr->full_name = malloc(len + entry->size + 1); + if(xattr->full_name == NULL) + MEM_ERROR(); + + memcpy(xattr->full_name, prefix_table[i].prefix, len); + memcpy(xattr->full_name + len, name, entry->size); + xattr->full_name[len + entry->size] = '\0'; + xattr->name = xattr->full_name + len; + xattr->size = entry->size; + xattr->type = type; + + return 1; +} + + +/* + * Read and decompress the xattr id table and the xattr metadata. + * This is cached in memory for later use by get_xattr() + */ +int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk) +{ + int res, bytes, i, indexes, index_bytes, ids; + long long *index, start, end; + struct squashfs_xattr_table id_table; + + TRACE("read_xattrs_from_disk\n"); + + if(sBlk->xattr_id_table_start == SQUASHFS_INVALID_BLK) + return SQUASHFS_INVALID_BLK; + + /* + * Read xattr id table, containing start of xattr metadata and the + * number of xattrs in the file system + */ + res = read_fs_bytes(fd, sBlk->xattr_id_table_start, sizeof(id_table), + &id_table); + if(res == 0) + return 0; + + SQUASHFS_INSWAP_XATTR_TABLE(&id_table); + + /* + * Allocate and read the index to the xattr id table metadata + * blocks + */ + ids = id_table.xattr_ids; + xattr_table_start = id_table.xattr_table_start; + index_bytes = SQUASHFS_XATTR_BLOCK_BYTES(ids); + indexes = SQUASHFS_XATTR_BLOCKS(ids); + index = malloc(index_bytes); + if(index == NULL) + MEM_ERROR(); + + res = read_fs_bytes(fd, sBlk->xattr_id_table_start + sizeof(id_table), + index_bytes, index); + if(res ==0) + goto failed1; + + SQUASHFS_INSWAP_LONG_LONGS(index, indexes); + + /* + * Allocate enough space for the uncompressed xattr id table, and + * read and decompress it + */ + bytes = SQUASHFS_XATTR_BYTES(ids); + xattr_ids = malloc(bytes); + if(xattr_ids == NULL) + MEM_ERROR(); + + for(i = 0; i < indexes; i++) { + int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE : + bytes & (SQUASHFS_METADATA_SIZE - 1); + int length = read_block(fd, index[i], NULL, expected, + ((unsigned char *) xattr_ids) + + (i * SQUASHFS_METADATA_SIZE)); + TRACE("Read xattr id table block %d, from 0x%llx, length " + "%d\n", i, index[i], length); + if(length == 0) { + ERROR("Failed to read xattr id table block %d, " + "from 0x%llx, length %d\n", i, index[i], + length); + goto failed2; + } + } + + /* + * Read and decompress the xattr metadata + * + * Note the first xattr id table metadata block is immediately after + * the last xattr metadata block, so we can use index[0] to work out + * the end of the xattr metadata + */ + start = xattr_table_start; + end = index[0]; + for(i = 0; start < end; i++) { + int length; + xattrs = realloc(xattrs, (i + 1) * SQUASHFS_METADATA_SIZE); + if(xattrs == NULL) + MEM_ERROR(); + + /* store mapping from location of compressed block in fs -> + * location of uncompressed block in memory */ + save_xattr_block(start, i * SQUASHFS_METADATA_SIZE); + + length = read_block(fd, start, &start, 0, + ((unsigned char *) xattrs) + + (i * SQUASHFS_METADATA_SIZE)); + TRACE("Read xattr block %d, length %d\n", i, length); + if(length == 0) { + ERROR("Failed to read xattr block %d\n", i); + goto failed3; + } + + /* + * If this is not the last metadata block in the xattr metadata + * then it should be SQUASHFS_METADATA_SIZE in size. + * Note, we can't use expected in read_block() above for this + * because we don't know if this is the last block until + * after reading. + */ + if(start != end && length != SQUASHFS_METADATA_SIZE) { + ERROR("Xattr block %d should be %d bytes in length, " + "it is %d bytes\n", i, SQUASHFS_METADATA_SIZE, + length); + goto failed3; + } + } + + /* swap if necessary the xattr id entries */ + for(i = 0; i < ids; i++) + SQUASHFS_INSWAP_XATTR_ID(&xattr_ids[i]); + + free(index); + + return ids; + +failed3: + free(xattrs); +failed2: + free(xattr_ids); +failed1: + free(index); + + return 0; +} + + +void free_xattr(struct xattr_list *xattr_list, int count) +{ + int i; + + for(i = 0; i < count; i++) + free(xattr_list[i].full_name); + + free(xattr_list); +} + + +/* + * Construct and return the list of xattr name:value pairs for the passed xattr + * id + * + * There are two users for get_xattr(), Mksquashfs uses it to read the + * xattrs from the filesystem on appending, and Unsquashfs uses it + * to retrieve the xattrs for writing to disk. + * + * Unfortunately, the two users disagree on what to do with unknown + * xattr prefixes, Mksquashfs wants to treat this as fatal otherwise + * this will cause xattrs to be be lost on appending. Unsquashfs + * on the otherhand wants to retrieve the xattrs which are known and + * to ignore the rest, this allows Unsquashfs to cope more gracefully + * with future versions which may have unknown xattrs, as long as the + * general xattr structure is adhered to, Unsquashfs should be able + * to safely ignore unknown xattrs, and to write the ones it knows about, + * this is better than completely refusing to retrieve all the xattrs. + * + * If ignore is TRUE then don't treat unknown xattr prefixes as + * a failure to read the xattr. + */ +struct xattr_list *get_xattr(int i, unsigned int *count, int ignore) +{ + long long start; + struct xattr_list *xattr_list = NULL; + unsigned int offset; + void *xptr; + int j = 0, res = 1; + + TRACE("get_xattr\n"); + + *count = xattr_ids[i].count; + start = SQUASHFS_XATTR_BLK(xattr_ids[i].xattr) + xattr_table_start; + offset = SQUASHFS_XATTR_OFFSET(xattr_ids[i].xattr); + xptr = xattrs + get_xattr_block(start) + offset; + + TRACE("get_xattr: xattr_id %d, count %d, start %lld, offset %d\n", i, + *count, start, offset); + + while(j < *count) { + struct squashfs_xattr_entry entry; + struct squashfs_xattr_val val; + + if(res != 0) { + xattr_list = realloc(xattr_list, (j + 1) * + sizeof(struct xattr_list)); + if(xattr_list == NULL) + MEM_ERROR(); + } + + SQUASHFS_SWAP_XATTR_ENTRY(xptr, &entry); + xptr += sizeof(entry); + + res = read_xattr_entry(&xattr_list[j], &entry, xptr); + if(ignore && res == 0) { + /* unknown prefix, but ignore flag is set */ + (*count) --; + continue; + } + + if(res != 1) + goto failed; + + xptr += entry.size; + + TRACE("get_xattr: xattr %d, type %d, size %d, name %s\n", j, + entry.type, entry.size, xattr_list[j].full_name); + + if(entry.type & SQUASHFS_XATTR_VALUE_OOL) { + long long xattr; + void *ool_xptr; + + xptr += sizeof(val); + SQUASHFS_SWAP_LONG_LONGS(xptr, &xattr, 1); + xptr += sizeof(xattr); + start = SQUASHFS_XATTR_BLK(xattr) + xattr_table_start; + offset = SQUASHFS_XATTR_OFFSET(xattr); + ool_xptr = xattrs + get_xattr_block(start) + offset; + SQUASHFS_SWAP_XATTR_VAL(ool_xptr, &val); + xattr_list[j].value = ool_xptr + sizeof(val); + } else { + SQUASHFS_SWAP_XATTR_VAL(xptr, &val); + xattr_list[j].value = xptr + sizeof(val); + xptr += sizeof(val) + val.vsize; + } + + TRACE("get_xattr: xattr %d, vsize %d\n", j, val.vsize); + + xattr_list[j ++].vsize = val.vsize; + } + + if(*count == 0) + goto failed; + + return xattr_list; + +failed: + free_xattr(xattr_list, j); + + return NULL; +}
diff --git a/squashfs-tools/squashfs-tools/restore.c b/squashfs-tools/squashfs-tools/restore.c new file mode 100644 index 0000000..5e336b3 --- /dev/null +++ b/squashfs-tools/squashfs-tools/restore.c
@@ -0,0 +1,155 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * restore.c + */ + +#include <pthread.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#include <stdio.h> +#include <math.h> +#include <stdarg.h> +#include <errno.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "caches-queues-lists.h" +#include "squashfs_fs.h" +#include "mksquashfs.h" +#include "error.h" +#include "progressbar.h" +#include "info.h" + +#define FALSE 0 +#define TRUE 1 + +extern pthread_t reader_thread, writer_thread, main_thread; +extern pthread_t *deflator_thread, *frag_deflator_thread, *frag_thread; +extern struct queue *to_deflate, *to_writer, *to_frag, *to_process_frag; +extern struct seq_queue *to_main; +extern void restorefs(); +extern int processors; + +static int interrupted = 0; +static pthread_t restore_thread; + +void *restore_thrd(void *arg) +{ + sigset_t sigmask, old_mask; + int i, sig; + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGUSR1); + pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask); + + while(1) { + sigwait(&sigmask, &sig); + + if((sig == SIGINT || sig == SIGTERM) && !interrupted) { + ERROR("Interrupting will restore original " + "filesystem!\n"); + ERROR("Interrupt again to quit\n"); + interrupted = TRUE; + continue; + } + + /* kill main thread/worker threads and restore */ + set_progressbar_state(FALSE); + disable_info(); + + /* first kill the reader thread */ + pthread_cancel(reader_thread); + pthread_join(reader_thread, NULL); + + /* + * then flush the reader to deflator thread(s) output queue. + * The deflator thread(s) will idle + */ + queue_flush(to_deflate); + + /* now kill the deflator thread(s) */ + for(i = 0; i < processors; i++) + pthread_cancel(deflator_thread[i]); + for(i = 0; i < processors; i++) + pthread_join(deflator_thread[i], NULL); + + /* + * then flush the reader to process fragment thread(s) output + * queue. The process fragment thread(s) will idle + */ + queue_flush(to_process_frag); + + /* now kill the process fragment thread(s) */ + for(i = 0; i < processors; i++) + pthread_cancel(frag_thread[i]); + for(i = 0; i < processors; i++) + pthread_join(frag_thread[i], NULL); + + /* + * then flush the reader/deflator/process fragment to main + * thread output queue. The main thread will idle + */ + seq_queue_flush(to_main); + + /* now kill the main thread */ + pthread_cancel(main_thread); + pthread_join(main_thread, NULL); + + /* then flush the main thread to fragment deflator thread(s) + * queue. The fragment deflator thread(s) will idle + */ + queue_flush(to_frag); + + /* now kill the fragment deflator thread(s) */ + for(i = 0; i < processors; i++) + pthread_cancel(frag_deflator_thread[i]); + for(i = 0; i < processors; i++) + pthread_join(frag_deflator_thread[i], NULL); + + /* + * then flush the main thread/fragment deflator thread(s) + * to writer thread queue. The writer thread will idle + */ + queue_flush(to_writer); + + /* now kill the writer thread */ + pthread_cancel(writer_thread); + pthread_join(writer_thread, NULL); + + TRACE("All threads cancelled\n"); + + restorefs(); + } +} + + +pthread_t *init_restore_thread() +{ + pthread_create(&restore_thread, NULL, restore_thrd, NULL); + return &restore_thread; +}
diff --git a/squashfs-tools/squashfs-tools/restore.h b/squashfs-tools/squashfs-tools/restore.h new file mode 100644 index 0000000..35129f0 --- /dev/null +++ b/squashfs-tools/squashfs-tools/restore.h
@@ -0,0 +1,28 @@ +#ifndef RESTORE_H +#define RESTORE_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * restore.h + */ + +extern pthread_t *init_restore_thread(); +#endif
diff --git a/squashfs-tools/squashfs-tools/sort.c b/squashfs-tools/squashfs-tools/sort.c new file mode 100644 index 0000000..89df9e4 --- /dev/null +++ b/squashfs-tools/squashfs-tools/sort.c
@@ -0,0 +1,363 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, + * 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * sort.c + */ + +#define TRUE 1 +#define FALSE 0 +#define MAX_LINE 16384 + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <dirent.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#include "squashfs_fs.h" +#include "mksquashfs.h" +#include "sort.h" +#include "error.h" +#include "progressbar.h" + +int mkisofs_style = -1; + +struct sort_info { + dev_t st_dev; + ino_t st_ino; + int priority; + struct sort_info *next; +}; + +struct sort_info *sort_info_list[65536]; + +struct priority_entry *priority_list[65536]; + +extern int silent; +extern void write_file(squashfs_inode *inode, struct dir_ent *dir_ent, + int *c_size); +extern char *pathname(struct dir_ent *dir_ent); + + +void add_priority_list(struct dir_ent *dir, int priority) +{ + struct priority_entry *new_priority_entry; + + priority += 32768; + new_priority_entry = malloc(sizeof(struct priority_entry)); + if(new_priority_entry == NULL) + MEM_ERROR(); + + new_priority_entry->dir = dir;; + new_priority_entry->next = priority_list[priority]; + priority_list[priority] = new_priority_entry; +} + + +int get_priority(char *filename, struct stat *buf, int priority) +{ + int hash = buf->st_ino & 0xffff; + struct sort_info *s; + + for(s = sort_info_list[hash]; s; s = s->next) + if((s->st_dev == buf->st_dev) && (s->st_ino == buf->st_ino)) { + TRACE("returning priority %d (%s)\n", s->priority, + filename); + return s->priority; + } + TRACE("returning priority %d (%s)\n", priority, filename); + return priority; +} + + +#define ADD_ENTRY(buf, priority) {\ + int hash = buf.st_ino & 0xffff;\ + struct sort_info *s;\ + if((s = malloc(sizeof(struct sort_info))) == NULL) \ + MEM_ERROR(); \ + s->st_dev = buf.st_dev;\ + s->st_ino = buf.st_ino;\ + s->priority = priority;\ + s->next = sort_info_list[hash];\ + sort_info_list[hash] = s;\ + } +int add_sort_list(char *path, int priority, int source, char *source_path[]) +{ + int i, n; + struct stat buf; + + TRACE("add_sort_list: filename %s, priority %d\n", path, priority); + if(strlen(path) > 1 && strcmp(path + strlen(path) - 2, "/*") == 0) + path[strlen(path) - 2] = '\0'; + + TRACE("add_sort_list: filename %s, priority %d\n", path, priority); +re_read: + if(path[0] == '/' || strncmp(path, "./", 2) == 0 || + strncmp(path, "../", 3) == 0 || mkisofs_style == 1) { + if(lstat(path, &buf) == -1) + goto error; + TRACE("adding filename %s, priority %d, st_dev %d, st_ino " + "%lld\n", path, priority, (int) buf.st_dev, + (long long) buf.st_ino); + ADD_ENTRY(buf, priority); + return TRUE; + } + + for(i = 0, n = 0; i < source; i++) { + char *filename; + int res = asprintf(&filename, "%s/%s", source_path[i], path); + if(res == -1) + BAD_ERROR("asprintf failed in add_sort_list\n"); + res = lstat(filename, &buf); + free(filename); + if(res == -1) { + if(!(errno == ENOENT || errno == ENOTDIR)) + goto error; + continue; + } + ADD_ENTRY(buf, priority); + n ++; + } + + if(n == 0 && mkisofs_style == -1 && lstat(path, &buf) != -1) { + ERROR("WARNING: Mkisofs style sortlist detected! This is " + "supported but please\n"); + ERROR("convert to mksquashfs style sortlist! A sortlist entry"); + ERROR(" should be\neither absolute (starting with "); + ERROR("'/') start with './' or '../' (taken to be\nrelative to " + "$PWD), otherwise it "); + ERROR("is assumed the entry is relative to one\nof the source " + "directories, i.e. with "); + ERROR("\"mksquashfs test test.sqsh\",\nthe sortlist "); + ERROR("entry \"file\" is assumed to be inside the directory " + "test.\n\n"); + mkisofs_style = 1; + goto re_read; + } + + mkisofs_style = 0; + + if(n == 1) + return TRUE; + if(n > 1) { + ERROR(" Ambiguous sortlist entry \"%s\"\n\nIt maps to more " + "than one source entry! Please use an absolute path." + "\n", path); + return FALSE; + } + +error: + ERROR_START("Cannot stat sortlist entry \"%s\"\n", path); + ERROR("This is probably because you're using the wrong file\n"); + ERROR("path relative to the source directories."); + ERROR_EXIT(" Ignoring"); + /* + * Historical note + * Failure to stat a sortlist entry is deliberately ignored, even + * though it is an error. Squashfs release 2.2 changed the behaviour + * to treat it as a fatal error, but it was changed back to + * the original behaviour to ignore it in release 2.2-r2 following + * feedback from users at the time. + */ + return TRUE; +} + + +void generate_file_priorities(struct dir_info *dir, int priority, + struct stat *buf) +{ + struct dir_ent *dir_ent = dir->list; + + priority = get_priority(dir->pathname, buf, priority); + + for(; dir_ent; dir_ent = dir_ent->next) { + struct stat *buf = &dir_ent->inode->buf; + if(dir_ent->inode->root_entry) + continue; + + switch(buf->st_mode & S_IFMT) { + case S_IFREG: + add_priority_list(dir_ent, + get_priority(pathname(dir_ent), buf, + priority)); + break; + case S_IFDIR: + generate_file_priorities(dir_ent->dir, + priority, buf); + break; + } + } +} + + +int read_sort_file(char *filename, int source, char *source_path[]) +{ + FILE *fd; + char line_buffer[MAX_LINE + 1]; /* overflow safe */ + char sort_filename[MAX_LINE + 1]; /* overflow safe */ + char *line, *name; + int n, priority, res; + + if((fd = fopen(filename, "r")) == NULL) { + ERROR("Failed to open sort file \"%s\" because %s\n", + filename, strerror(errno)); + return FALSE; + } + + while(fgets(line = line_buffer, MAX_LINE + 1, fd) != NULL) { + int len = strlen(line); + + if(len == MAX_LINE && line[len - 1] != '\n') { + /* line too large */ + ERROR("Line too long when reading " + "sort file \"%s\", larger than %d " + "bytes\n", filename, MAX_LINE); + goto failed; + } + + /* + * Remove '\n' terminator if it exists (the last line + * in the file may not be '\n' terminated) + */ + if(len && line[len - 1] == '\n') + line[len - 1] = '\0'; + + /* Skip any leading whitespace */ + while(isspace(*line)) + line ++; + + /* if comment line, skip */ + if(*line == '#') + continue; + + /* + * Scan for filename, don't use sscanf() and "%s" because + * that can't handle filenames with spaces + */ + for(name = sort_filename; !isspace(*line) && *line != '\0';) { + if(*line == '\\') { + line ++; + if (*line == '\0') + break; + } + *name ++ = *line ++; + } + *name = '\0'; + + /* + * if filename empty, then line was empty of anything but + * whitespace or a backslash character. Skip empy lines + */ + if(sort_filename[0] == '\0') + continue; + + /* + * Scan the rest of the line, we expect a decimal number + * which is the filename priority + */ + errno = 0; + res = sscanf(line, "%d%n", &priority, &n); + + if((res < 1 || errno) && errno != ERANGE) { + if(errno == 0) + /* No error, assume EOL or match failure */ + ERROR("Sort file \"%s\", can't find priority " + "in entry \"%s\", EOL or match " + "failure\n", filename, line_buffer); + else + /* Some other failure not ERANGE */ + ERROR("Sscanf failed reading sort file \"%s\" " + "because %s\n", filename, + strerror(errno)); + goto failed; + } else if((errno == ERANGE) || + (priority < -32768 || priority > 32767)) { + ERROR("Sort file \"%s\", entry \"%s\" has priority " + "outside range of -32767:32768.\n", filename, + line_buffer); + goto failed; + } + + /* Skip any trailing whitespace */ + line += n; + while(isspace(*line)) + line ++; + + if(*line != '\0') { + ERROR("Sort file \"%s\", trailing characters after " + "priority in entry \"%s\"\n", filename, + line_buffer); + goto failed; + } + + res = add_sort_list(sort_filename, priority, source, + source_path); + if(res == FALSE) + goto failed; + } + + if(ferror(fd)) { + ERROR("Reading sort file \"%s\" failed because %s\n", filename, + strerror(errno)); + goto failed; + } + + fclose(fd); + return TRUE; + +failed: + fclose(fd); + return FALSE; +} + + +void sort_files_and_write(struct dir_info *dir) +{ + int i; + struct priority_entry *entry; + squashfs_inode inode; + int duplicate_file; + + for(i = 65535; i >= 0; i--) + for(entry = priority_list[i]; entry; entry = entry->next) { + TRACE("%d: %s\n", i - 32768, pathname(entry->dir)); + if(entry->dir->inode->inode == SQUASHFS_INVALID_BLK) { + write_file(&inode, entry->dir, &duplicate_file); + INFO("file %s, uncompressed size %lld bytes %s" + "\n", pathname(entry->dir), + (long long) + entry->dir->inode->buf.st_size, + duplicate_file ? "DUPLICATE" : ""); + entry->dir->inode->inode = inode; + entry->dir->inode->type = SQUASHFS_FILE_TYPE; + } else + INFO("file %s, uncompressed size %lld bytes " + "LINK\n", pathname(entry->dir), + (long long) + entry->dir->inode->buf.st_size); + } +}
diff --git a/squashfs-tools/squashfs-tools/sort.h b/squashfs-tools/squashfs-tools/sort.h new file mode 100644 index 0000000..98db62c --- /dev/null +++ b/squashfs-tools/squashfs-tools/sort.h
@@ -0,0 +1,37 @@ +#ifndef SORT_H +#define SORT_H + +/* + * Squashfs + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * sort.h + */ + +struct priority_entry { + struct dir_ent *dir; + struct priority_entry *next; +}; + +extern int read_sort_file(char *, int, char *[]); +extern void sort_files_and_write(struct dir_info *); +extern void generate_file_priorities(struct dir_info *, int priority, + struct stat *); +extern struct priority_entry *priority_list[65536]; +#endif
diff --git a/squashfs-tools/squashfs-tools/squashfs_compat.h b/squashfs-tools/squashfs-tools/squashfs_compat.h new file mode 100644 index 0000000..83b0278 --- /dev/null +++ b/squashfs-tools/squashfs-tools/squashfs_compat.h
@@ -0,0 +1,818 @@ +#ifndef SQUASHFS_COMPAT +#define SQUASHFS_COMPAT +/* + * Squashfs + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * squashfs_compat.h + */ + +/* + * definitions for structures on disk - layout 3.x + */ + +#define SQUASHFS_CHECK 2 +#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_CHECK) + +/* Max number of uids and gids */ +#define SQUASHFS_UIDS 256 +#define SQUASHFS_GUIDS 255 + +struct squashfs_super_block_3 { + unsigned int s_magic; + unsigned int inodes; + unsigned int bytes_used_2; + unsigned int uid_start_2; + unsigned int guid_start_2; + unsigned int inode_table_start_2; + unsigned int directory_table_start_2; + unsigned int s_major:16; + unsigned int s_minor:16; + unsigned int block_size_1:16; + unsigned int block_log:16; + unsigned int flags:8; + unsigned int no_uids:8; + unsigned int no_guids:8; + int mkfs_time /* time of filesystem creation */; + squashfs_inode root_inode; + unsigned int block_size; + unsigned int fragments; + unsigned int fragment_table_start_2; + long long bytes_used; + long long uid_start; + long long guid_start; + long long inode_table_start; + long long directory_table_start; + long long fragment_table_start; + long long lookup_table_start; +} __attribute__ ((packed)); + +struct squashfs_dir_index_3 { + unsigned int index; + unsigned int start_block; + unsigned char size; + unsigned char name[0]; +} __attribute__ ((packed)); + +struct squashfs_base_inode_header_3 { + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; +} __attribute__ ((packed)); + +struct squashfs_ipc_inode_header_3 { + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; + unsigned int nlink; +} __attribute__ ((packed)); + +struct squashfs_dev_inode_header_3 { + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; + unsigned int nlink; + unsigned short rdev; +} __attribute__ ((packed)); + +struct squashfs_symlink_inode_header_3 { + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; + unsigned int nlink; + unsigned short symlink_size; + char symlink[0]; +} __attribute__ ((packed)); + +struct squashfs_reg_inode_header_3 { + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; + squashfs_block start_block; + unsigned int fragment; + unsigned int offset; + unsigned int file_size; + unsigned short block_list[0]; +} __attribute__ ((packed)); + +struct squashfs_lreg_inode_header_3 { + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; + unsigned int nlink; + squashfs_block start_block; + unsigned int fragment; + unsigned int offset; + long long file_size; + unsigned short block_list[0]; +} __attribute__ ((packed)); + +struct squashfs_dir_inode_header_3 { + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; + unsigned int nlink; + unsigned int file_size:19; + unsigned int offset:13; + unsigned int start_block; + unsigned int parent_inode; +} __attribute__ ((packed)); + +struct squashfs_ldir_inode_header_3 { + unsigned int inode_type:4; + unsigned int mode:12; + unsigned int uid:8; + unsigned int guid:8; + int mtime; + unsigned int inode_number; + unsigned int nlink; + unsigned int file_size:27; + unsigned int offset:13; + unsigned int start_block; + unsigned int i_count:16; + unsigned int parent_inode; + struct squashfs_dir_index_3 index[0]; +} __attribute__ ((packed)); + +union squashfs_inode_header_3 { + struct squashfs_base_inode_header_3 base; + struct squashfs_dev_inode_header_3 dev; + struct squashfs_symlink_inode_header_3 symlink; + struct squashfs_reg_inode_header_3 reg; + struct squashfs_lreg_inode_header_3 lreg; + struct squashfs_dir_inode_header_3 dir; + struct squashfs_ldir_inode_header_3 ldir; + struct squashfs_ipc_inode_header_3 ipc; +}; + +struct squashfs_dir_entry_3 { + unsigned int offset:13; + unsigned int type:3; + unsigned int size:8; + int inode_number:16; + char name[0]; +} __attribute__ ((packed)); + +struct squashfs_dir_header_3 { + unsigned int count:8; + unsigned int start_block; + unsigned int inode_number; +} __attribute__ ((packed)); + +struct squashfs_fragment_entry_3 { + long long start_block; + unsigned int size; + unsigned int pending; +} __attribute__ ((packed)); + + +typedef struct squashfs_super_block_3 squashfs_super_block_3; +typedef struct squashfs_dir_index_3 squashfs_dir_index_3; +typedef struct squashfs_base_inode_header_3 squashfs_base_inode_header_3; +typedef struct squashfs_ipc_inode_header_3 squashfs_ipc_inode_header_3; +typedef struct squashfs_dev_inode_header_3 squashfs_dev_inode_header_3; +typedef struct squashfs_symlink_inode_header_3 squashfs_symlink_inode_header_3; +typedef struct squashfs_reg_inode_header_3 squashfs_reg_inode_header_3; +typedef struct squashfs_lreg_inode_header_3 squashfs_lreg_inode_header_3; +typedef struct squashfs_dir_inode_header_3 squashfs_dir_inode_header_3; +typedef struct squashfs_ldir_inode_header_3 squashfs_ldir_inode_header_3; +typedef struct squashfs_dir_entry_3 squashfs_dir_entry_3; +typedef struct squashfs_dir_header_3 squashfs_dir_header_3; +typedef struct squashfs_fragment_entry_3 squashfs_fragment_entry_3; + +/* + * macros to convert each packed bitfield structure from little endian to big + * endian and vice versa. These are needed when creating or using a filesystem + * on a machine with different byte ordering to the target architecture. + * + */ + +#define SQUASHFS_SWAP_START \ + int bits;\ + int b_pos;\ + unsigned long long val;\ + unsigned char *s;\ + unsigned char *d; + +#define SQUASHFS_SWAP_SUPER_BLOCK_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block_3));\ + SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\ + SQUASHFS_SWAP((s)->inodes, d, 32, 32);\ + SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\ + SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\ + SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\ + SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\ + SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\ + SQUASHFS_SWAP((s)->s_major, d, 224, 16);\ + SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\ + SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\ + SQUASHFS_SWAP((s)->block_log, d, 272, 16);\ + SQUASHFS_SWAP((s)->flags, d, 288, 8);\ + SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\ + SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\ + SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\ + SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\ + SQUASHFS_SWAP((s)->block_size, d, 408, 32);\ + SQUASHFS_SWAP((s)->fragments, d, 440, 32);\ + SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\ + SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\ + SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\ + SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\ + SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\ + SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\ + SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\ + SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\ +} + +#define SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, n)\ + SQUASHFS_MEMSET(s, d, n);\ + SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ + SQUASHFS_SWAP((s)->mode, d, 4, 12);\ + SQUASHFS_SWAP((s)->uid, d, 16, 8);\ + SQUASHFS_SWAP((s)->guid, d, 24, 8);\ + SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ + SQUASHFS_SWAP((s)->inode_number, d, 64, 32); + +#define SQUASHFS_SWAP_BASE_INODE_HEADER_3(s, d, n) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, n)\ +} + +#define SQUASHFS_SWAP_IPC_INODE_HEADER_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \ + sizeof(struct squashfs_ipc_inode_header_3))\ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ +} + +#define SQUASHFS_SWAP_DEV_INODE_HEADER_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \ + sizeof(struct squashfs_dev_inode_header_3)); \ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ + SQUASHFS_SWAP((s)->rdev, d, 128, 16);\ +} + +#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \ + sizeof(struct squashfs_symlink_inode_header_3));\ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ + SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\ +} + +#define SQUASHFS_SWAP_REG_INODE_HEADER_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \ + sizeof(struct squashfs_reg_inode_header_3));\ + SQUASHFS_SWAP((s)->start_block, d, 96, 64);\ + SQUASHFS_SWAP((s)->fragment, d, 160, 32);\ + SQUASHFS_SWAP((s)->offset, d, 192, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 224, 32);\ +} + +#define SQUASHFS_SWAP_LREG_INODE_HEADER_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \ + sizeof(struct squashfs_lreg_inode_header_3));\ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 128, 64);\ + SQUASHFS_SWAP((s)->fragment, d, 192, 32);\ + SQUASHFS_SWAP((s)->offset, d, 224, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 256, 64);\ +} + +#define SQUASHFS_SWAP_DIR_INODE_HEADER_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \ + sizeof(struct squashfs_dir_inode_header_3));\ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 128, 19);\ + SQUASHFS_SWAP((s)->offset, d, 147, 13);\ + SQUASHFS_SWAP((s)->start_block, d, 160, 32);\ + SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\ +} + +#define SQUASHFS_SWAP_LDIR_INODE_HEADER_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \ + sizeof(struct squashfs_ldir_inode_header_3));\ + SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 128, 27);\ + SQUASHFS_SWAP((s)->offset, d, 155, 13);\ + SQUASHFS_SWAP((s)->start_block, d, 168, 32);\ + SQUASHFS_SWAP((s)->i_count, d, 200, 16);\ + SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\ +} + +#define SQUASHFS_SWAP_DIR_INDEX_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_3));\ + SQUASHFS_SWAP((s)->index, d, 0, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 32, 32);\ + SQUASHFS_SWAP((s)->size, d, 64, 8);\ +} + +#define SQUASHFS_SWAP_DIR_HEADER_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_3));\ + SQUASHFS_SWAP((s)->count, d, 0, 8);\ + SQUASHFS_SWAP((s)->start_block, d, 8, 32);\ + SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\ +} + +#define SQUASHFS_SWAP_DIR_ENTRY_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_3));\ + SQUASHFS_SWAP((s)->offset, d, 0, 13);\ + SQUASHFS_SWAP((s)->type, d, 13, 3);\ + SQUASHFS_SWAP((s)->size, d, 16, 8);\ + SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\ +} + +#define SQUASHFS_SWAP_INODE_T_3(s, d) SQUASHFS_SWAP_LONG_LONGS_3(s, d, 1) + +#define SQUASHFS_SWAP_SHORTS_3(s, d, n) {\ + int entry;\ + int bit_position;\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, n * 2);\ + for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ + 16)\ + SQUASHFS_SWAP(s[entry], d, bit_position, 16);\ +} + +#define SQUASHFS_SWAP_INTS_3(s, d, n) {\ + int entry;\ + int bit_position;\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, n * 4);\ + for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ + 32)\ + SQUASHFS_SWAP(s[entry], d, bit_position, 32);\ +} + +#define SQUASHFS_SWAP_LONG_LONGS_3(s, d, n) {\ + int entry;\ + int bit_position;\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, n * 8);\ + for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ + 64)\ + SQUASHFS_SWAP(s[entry], d, bit_position, 64);\ +} + +#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\ + int entry;\ + int bit_position;\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, n * bits / 8);\ + for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ + bits)\ + SQUASHFS_SWAP(s[entry], d, bit_position, bits);\ +} + +#define SQUASHFS_SWAP_FRAGMENT_INDEXES_3(s, d, n) SQUASHFS_SWAP_LONG_LONGS_3(s, d, n) +#define SQUASHFS_SWAP_LOOKUP_BLOCKS_3(s, d, n) SQUASHFS_SWAP_LONG_LONGS_3(s, d, n) + +#define SQUASHFS_SWAP_FRAGMENT_ENTRY_3(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_3));\ + SQUASHFS_SWAP((s)->start_block, d, 0, 64);\ + SQUASHFS_SWAP((s)->size, d, 64, 32);\ +} + +/* fragment and fragment table defines */ +#define SQUASHFS_FRAGMENT_BYTES_3(A) ((A) * sizeof(struct squashfs_fragment_entry_3)) + +#define SQUASHFS_FRAGMENT_INDEX_3(A) (SQUASHFS_FRAGMENT_BYTES_3(A) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEX_OFFSET_3(A) (SQUASHFS_FRAGMENT_BYTES_3(A) % \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEXES_3(A) ((SQUASHFS_FRAGMENT_BYTES_3(A) + \ + SQUASHFS_METADATA_SIZE - 1) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEX_BYTES_3(A) (SQUASHFS_FRAGMENT_INDEXES_3(A) *\ + sizeof(long long)) + +/* + * definitions for structures on disk - layout 1.x + */ +#define SQUASHFS_TYPES 5 +#define SQUASHFS_IPC_TYPE 0 + +struct squashfs_base_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ +} __attribute__ ((packed)); + +struct squashfs_ipc_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ + unsigned int type:4; + unsigned int offset:4; +} __attribute__ ((packed)); + +struct squashfs_dev_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ + unsigned short rdev; +} __attribute__ ((packed)); + +struct squashfs_symlink_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ + unsigned short symlink_size; + char symlink[0]; +} __attribute__ ((packed)); + +struct squashfs_reg_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ + int mtime; + unsigned int start_block; + unsigned int file_size:32; + unsigned short block_list[0]; +} __attribute__ ((packed)); + +struct squashfs_dir_inode_header_1 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:4; /* index into uid table */ + unsigned int guid:4; /* index into guid table */ + unsigned int file_size:19; + unsigned int offset:13; + int mtime; + unsigned int start_block:24; +} __attribute__ ((packed)); + +union squashfs_inode_header_1 { + struct squashfs_base_inode_header_1 base; + struct squashfs_dev_inode_header_1 dev; + struct squashfs_symlink_inode_header_1 symlink; + struct squashfs_reg_inode_header_1 reg; + struct squashfs_dir_inode_header_1 dir; + struct squashfs_ipc_inode_header_1 ipc; +}; + +typedef struct squashfs_dir_index_1 squashfs_dir_index_1; +typedef struct squashfs_base_inode_header_1 squashfs_base_inode_header_1; +typedef struct squashfs_ipc_inode_header_1 squashfs_ipc_inode_header_1; +typedef struct squashfs_dev_inode_header_1 squashfs_dev_inode_header_1; +typedef struct squashfs_symlink_inode_header_1 squashfs_symlink_inode_header_1; +typedef struct squashfs_reg_inode_header_1 squashfs_reg_inode_header_1; +typedef struct squashfs_dir_inode_header_1 squashfs_dir_inode_header_1; + +#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \ + SQUASHFS_MEMSET(s, d, n);\ + SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ + SQUASHFS_SWAP((s)->mode, d, 4, 12);\ + SQUASHFS_SWAP((s)->uid, d, 16, 4);\ + SQUASHFS_SWAP((s)->guid, d, 20, 4); + +#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\ +} + +#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ + sizeof(struct squashfs_ipc_inode_header_1));\ + SQUASHFS_SWAP((s)->type, d, 24, 4);\ + SQUASHFS_SWAP((s)->offset, d, 28, 4);\ +} + +#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ + sizeof(struct squashfs_dev_inode_header_1));\ + SQUASHFS_SWAP((s)->rdev, d, 24, 16);\ +} + +#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ + sizeof(struct squashfs_symlink_inode_header_1));\ + SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\ +} + +#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ + sizeof(struct squashfs_reg_inode_header_1));\ + SQUASHFS_SWAP((s)->mtime, d, 24, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 56, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 88, 32);\ +} + +#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ + sizeof(struct squashfs_dir_inode_header_1));\ + SQUASHFS_SWAP((s)->file_size, d, 24, 19);\ + SQUASHFS_SWAP((s)->offset, d, 43, 13);\ + SQUASHFS_SWAP((s)->mtime, d, 56, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 88, 24);\ +} + +/* + * definitions for structures on disk - layout 2.x + */ +struct squashfs_dir_index_2 { + unsigned int index:27; + unsigned int start_block:29; + unsigned char size; + unsigned char name[0]; +} __attribute__ ((packed)); + +struct squashfs_base_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ +} __attribute__ ((packed)); + +struct squashfs_ipc_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ +} __attribute__ ((packed)); + +struct squashfs_dev_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ + unsigned short rdev; +} __attribute__ ((packed)); + +struct squashfs_symlink_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ + unsigned short symlink_size; + char symlink[0]; +} __attribute__ ((packed)); + +struct squashfs_reg_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ + int mtime; + unsigned int start_block; + unsigned int fragment; + unsigned int offset; + unsigned int file_size:32; + unsigned short block_list[0]; +} __attribute__ ((packed)); + +struct squashfs_dir_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ + unsigned int file_size:19; + unsigned int offset:13; + int mtime; + unsigned int start_block:24; +} __attribute__ ((packed)); + +struct squashfs_ldir_inode_header_2 { + unsigned int inode_type:4; + unsigned int mode:12; /* protection */ + unsigned int uid:8; /* index into uid table */ + unsigned int guid:8; /* index into guid table */ + unsigned int file_size:27; + unsigned int offset:13; + int mtime; + unsigned int start_block:24; + unsigned int i_count:16; + struct squashfs_dir_index_2 index[0]; +} __attribute__ ((packed)); + +union squashfs_inode_header_2 { + struct squashfs_base_inode_header_2 base; + struct squashfs_dev_inode_header_2 dev; + struct squashfs_symlink_inode_header_2 symlink; + struct squashfs_reg_inode_header_2 reg; + struct squashfs_dir_inode_header_2 dir; + struct squashfs_ldir_inode_header_2 ldir; + struct squashfs_ipc_inode_header_2 ipc; +}; + +struct squashfs_dir_header_2 { + unsigned int count:8; + unsigned int start_block:24; +} __attribute__ ((packed)); + +struct squashfs_dir_entry_2 { + unsigned int offset:13; + unsigned int type:3; + unsigned int size:8; + char name[0]; +} __attribute__ ((packed)); + +struct squashfs_fragment_entry_2 { + unsigned int start_block; + unsigned int size; +} __attribute__ ((packed)); + +typedef struct squashfs_dir_index_2 squashfs_dir_index_2; +typedef struct squashfs_base_inode_header_2 squashfs_base_inode_header_2; +typedef struct squashfs_ipc_inode_header_2 squashfs_ipc_inode_header_2; +typedef struct squashfs_dev_inode_header_2 squashfs_dev_inode_header_2; +typedef struct squashfs_symlink_inode_header_2 squashfs_symlink_inode_header_2; +typedef struct squashfs_reg_inode_header_2 squashfs_reg_inode_header_2; +typedef struct squashfs_lreg_inode_header_2 squashfs_lreg_inode_header_2; +typedef struct squashfs_dir_inode_header_2 squashfs_dir_inode_header_2; +typedef struct squashfs_ldir_inode_header_2 squashfs_ldir_inode_header_2; +typedef struct squashfs_dir_entry_2 squashfs_dir_entry_2; +typedef struct squashfs_dir_header_2 squashfs_dir_header_2; +typedef struct squashfs_fragment_entry_2 squashfs_fragment_entry_2; + +#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ + SQUASHFS_MEMSET(s, d, n);\ + SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ + SQUASHFS_SWAP((s)->mode, d, 4, 12);\ + SQUASHFS_SWAP((s)->uid, d, 16, 8);\ + SQUASHFS_SWAP((s)->guid, d, 24, 8);\ + +#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ +} + +#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \ + SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2)) + +#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ + sizeof(struct squashfs_dev_inode_header_2)); \ + SQUASHFS_SWAP((s)->rdev, d, 32, 16);\ +} + +#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ + sizeof(struct squashfs_symlink_inode_header_2));\ + SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\ +} + +#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ + sizeof(struct squashfs_reg_inode_header_2));\ + SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 64, 32);\ + SQUASHFS_SWAP((s)->fragment, d, 96, 32);\ + SQUASHFS_SWAP((s)->offset, d, 128, 32);\ + SQUASHFS_SWAP((s)->file_size, d, 160, 32);\ +} + +#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ + sizeof(struct squashfs_dir_inode_header_2));\ + SQUASHFS_SWAP((s)->file_size, d, 32, 19);\ + SQUASHFS_SWAP((s)->offset, d, 51, 13);\ + SQUASHFS_SWAP((s)->mtime, d, 64, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 96, 24);\ +} + +#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ + sizeof(struct squashfs_ldir_inode_header_2));\ + SQUASHFS_SWAP((s)->file_size, d, 32, 27);\ + SQUASHFS_SWAP((s)->offset, d, 59, 13);\ + SQUASHFS_SWAP((s)->mtime, d, 72, 32);\ + SQUASHFS_SWAP((s)->start_block, d, 104, 24);\ + SQUASHFS_SWAP((s)->i_count, d, 128, 16);\ +} + +#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\ + SQUASHFS_SWAP((s)->index, d, 0, 27);\ + SQUASHFS_SWAP((s)->start_block, d, 27, 29);\ + SQUASHFS_SWAP((s)->size, d, 56, 8);\ +} +#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\ + SQUASHFS_SWAP((s)->count, d, 0, 8);\ + SQUASHFS_SWAP((s)->start_block, d, 8, 24);\ +} + +#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\ + SQUASHFS_SWAP((s)->offset, d, 0, 13);\ + SQUASHFS_SWAP((s)->type, d, 13, 3);\ + SQUASHFS_SWAP((s)->size, d, 16, 8);\ +} + +#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\ + SQUASHFS_SWAP_START\ + SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\ + SQUASHFS_SWAP((s)->start_block, d, 0, 32);\ + SQUASHFS_SWAP((s)->size, d, 32, 32);\ +} + +#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS_3(s, d, n) + +/* fragment and fragment table defines */ +#define SQUASHFS_FRAGMENT_BYTES_2(A) ((A) * sizeof(struct squashfs_fragment_entry_2)) + +#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \ + SQUASHFS_METADATA_SIZE - 1) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\ + sizeof(int)) +/* + * macros used to swap each structure entry, taking into account + * bitfields and different bitfield placing conventions on differing architectures + */ +#if __BYTE_ORDER == __BIG_ENDIAN + /* convert from big endian to little endian */ +#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, tbits, b_pos) +#else + /* convert from little endian to big 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
diff --git a/squashfs-tools/squashfs-tools/squashfs_fs.h b/squashfs-tools/squashfs-tools/squashfs_fs.h new file mode 100644 index 0000000..791fe12 --- /dev/null +++ b/squashfs-tools/squashfs-tools/squashfs_fs.h
@@ -0,0 +1,491 @@ +#ifndef SQUASHFS_FS +#define SQUASHFS_FS +/* + * Squashfs + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, + * 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * squashfs_fs.h + */ + +#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE +#define SQUASHFS_MAJOR 4 +#define SQUASHFS_MINOR 0 +#define SQUASHFS_MAGIC 0x73717368 +#define SQUASHFS_MAGIC_SWAP 0x68737173 +#define SQUASHFS_START 0 + +/* size of metadata (inode and directory) blocks */ +#define SQUASHFS_METADATA_SIZE 8192 +#define SQUASHFS_METADATA_LOG 13 + +/* default size of data blocks */ +#define SQUASHFS_FILE_SIZE 131072 + +#define SQUASHFS_FILE_MAX_SIZE 1048576 +#define SQUASHFS_FILE_MAX_LOG 20 + +/* Max number of uids and gids */ +#define SQUASHFS_IDS 65536 + +/* Max length of filename (not 255) */ +#define SQUASHFS_NAME_LEN 256 + +#define SQUASHFS_INVALID ((long long) 0xffffffffffff) +#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff) +#define SQUASHFS_INVALID_XATTR ((unsigned int) 0xffffffff) +#define SQUASHFS_INVALID_BLK ((long long) -1) +#define SQUASHFS_USED_BLK ((long long) -2) + +/* Filesystem flags */ +#define SQUASHFS_NOI 0 +#define SQUASHFS_NOD 1 +#define SQUASHFS_CHECK 2 +#define SQUASHFS_NOF 3 +#define SQUASHFS_NO_FRAG 4 +#define SQUASHFS_ALWAYS_FRAG 5 +#define SQUASHFS_DUPLICATE 6 +#define SQUASHFS_EXPORT 7 +#define SQUASHFS_NOX 8 +#define SQUASHFS_NO_XATTR 9 +#define SQUASHFS_COMP_OPT 10 + +#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1) + +#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_NOI) + +#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_NOD) + +#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_NOF) + +#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_NO_FRAG) + +#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_ALWAYS_FRAG) + +#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_DUPLICATE) + +#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_EXPORT) + +#define SQUASHFS_UNCOMPRESSED_XATTRS(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_NOX) + +#define SQUASHFS_NO_XATTRS(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_NO_XATTR) + +#define SQUASHFS_COMP_OPTS(flags) SQUASHFS_BIT(flags, \ + SQUASHFS_COMP_OPT) + +#define SQUASHFS_MKFLAGS(noi, nod, nof, nox, no_frag, always_frag, \ + duplicate_checking, exportable, no_xattr, comp_opt) (noi | \ + (nod << 1) | (nof << 3) | (no_frag << 4) | \ + (always_frag << 5) | (duplicate_checking << 6) | \ + (exportable << 7) | (nox << 8) | (no_xattr << 9) | \ + (comp_opt << 10)) + +/* Max number of types and file types */ +#define SQUASHFS_DIR_TYPE 1 +#define SQUASHFS_FILE_TYPE 2 +#define SQUASHFS_SYMLINK_TYPE 3 +#define SQUASHFS_BLKDEV_TYPE 4 +#define SQUASHFS_CHRDEV_TYPE 5 +#define SQUASHFS_FIFO_TYPE 6 +#define SQUASHFS_SOCKET_TYPE 7 +#define SQUASHFS_LDIR_TYPE 8 +#define SQUASHFS_LREG_TYPE 9 +#define SQUASHFS_LSYMLINK_TYPE 10 +#define SQUASHFS_LBLKDEV_TYPE 11 +#define SQUASHFS_LCHRDEV_TYPE 12 +#define SQUASHFS_LFIFO_TYPE 13 +#define SQUASHFS_LSOCKET_TYPE 14 + +/* Xattr types */ +#define SQUASHFS_XATTR_USER 0 +#define SQUASHFS_XATTR_TRUSTED 1 +#define SQUASHFS_XATTR_SECURITY 2 +#define SQUASHFS_XATTR_VALUE_OOL 256 +#define SQUASHFS_XATTR_PREFIX_MASK 0xff + +/* Flag whether block is compressed or uncompressed, bit is set if block is + * uncompressed */ +#define SQUASHFS_COMPRESSED_BIT (1 << 15) + +#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \ + (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT) + +#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT)) + +#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) + +#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \ + ~SQUASHFS_COMPRESSED_BIT_BLOCK) + +#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) + +/* + * Inode number ops. Inodes consist of a compressed block number, and an + * uncompressed offset within that block + */ +#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16)) + +#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff)) + +#define SQUASHFS_MKINODE(A, B) ((squashfs_inode)(((squashfs_inode) (A)\ + << 16) + (B))) + +/* Compute 32 bit VFS inode number from squashfs inode number */ +#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \ + ((b) >> 2) + 1)) + +/* Translate between VFS mode and squashfs mode */ +#define SQUASHFS_MODE(a) ((a) & 0xfff) + +/* fragment and fragment table defines */ +#define SQUASHFS_FRAGMENT_BYTES(A) ((A) * \ + sizeof(struct squashfs_fragment_entry)) + +#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \ + SQUASHFS_METADATA_SIZE - 1) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\ + sizeof(long long)) + +/* inode lookup table defines */ +#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(squashfs_inode)) + +#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \ + SQUASHFS_METADATA_SIZE - 1) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\ + sizeof(long long)) + +/* uid lookup table defines */ +#define SQUASHFS_ID_BYTES(A) ((A) * sizeof(unsigned int)) + +#define SQUASHFS_ID_BLOCK(A) (SQUASHFS_ID_BYTES(A) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_ID_BLOCK_OFFSET(A) (SQUASHFS_ID_BYTES(A) % \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_ID_BLOCKS(A) ((SQUASHFS_ID_BYTES(A) + \ + SQUASHFS_METADATA_SIZE - 1) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\ + sizeof(long long)) + +/* xattr id lookup table defines */ +#define SQUASHFS_XATTR_BYTES(A) ((A) * sizeof(struct squashfs_xattr_id)) + +#define SQUASHFS_XATTR_BLOCK(A) (SQUASHFS_XATTR_BYTES(A) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_XATTR_BLOCK_OFFSET(A) (SQUASHFS_XATTR_BYTES(A) % \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_XATTR_BLOCKS(A) ((SQUASHFS_XATTR_BYTES(A) + \ + SQUASHFS_METADATA_SIZE - 1) / \ + SQUASHFS_METADATA_SIZE) + +#define SQUASHFS_XATTR_BLOCK_BYTES(A) (SQUASHFS_XATTR_BLOCKS(A) *\ + sizeof(long long)) + +#define SQUASHFS_XATTR_BLK(A) ((unsigned int) ((A) >> 16)) + +#define SQUASHFS_XATTR_OFFSET(A) ((unsigned int) ((A) & 0xffff)) + +/* cached data constants for filesystem */ +#define SQUASHFS_CACHED_BLKS 8 + +#define SQUASHFS_MAX_FILE_SIZE_LOG 64 + +#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \ + (SQUASHFS_MAX_FILE_SIZE_LOG - 2)) + +#define SQUASHFS_MARKER_BYTE 0xff + +/* meta index cache */ +#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) +#define SQUASHFS_META_ENTRIES 31 +#define SQUASHFS_META_NUMBER 8 +#define SQUASHFS_SLOTS 4 + +struct meta_entry { + long long data_block; + unsigned int index_block; + unsigned short offset; + unsigned short pad; +}; + +struct meta_index { + unsigned int inode_number; + unsigned int offset; + unsigned short entries; + unsigned short skip; + unsigned short locked; + unsigned short pad; + struct meta_entry meta_entry[SQUASHFS_META_ENTRIES]; +}; + + +/* + * definitions for structures on disk + */ + +typedef long long squashfs_block; +typedef long long squashfs_inode; + +#define ZLIB_COMPRESSION 1 +#define LZMA_COMPRESSION 2 +#define LZO_COMPRESSION 3 +#define XZ_COMPRESSION 4 +#define LZ4_COMPRESSION 5 + +struct squashfs_super_block { + unsigned int s_magic; + unsigned int inodes; + int mkfs_time /* time of filesystem creation */; + unsigned int block_size; + unsigned int fragments; + unsigned short compression; + unsigned short block_log; + unsigned short flags; + unsigned short no_ids; + unsigned short s_major; + unsigned short s_minor; + squashfs_inode root_inode; + long long bytes_used; + long long id_table_start; + long long xattr_id_table_start; + long long inode_table_start; + long long directory_table_start; + long long fragment_table_start; + long long lookup_table_start; +}; + +struct squashfs_dir_index { + unsigned int index; + unsigned int start_block; + unsigned int size; + unsigned char name[0]; +}; + +struct squashfs_base_inode_header { + unsigned short inode_type; + unsigned short mode; + unsigned short uid; + unsigned short guid; + int mtime; + unsigned int inode_number; +}; + +struct squashfs_ipc_inode_header { + unsigned short inode_type; + unsigned short mode; + unsigned short uid; + unsigned short guid; + int mtime; + unsigned int inode_number; + unsigned int nlink; +}; + +struct squashfs_lipc_inode_header { + unsigned short inode_type; + unsigned short mode; + unsigned short uid; + unsigned short guid; + int mtime; + unsigned int inode_number; + unsigned int nlink; + unsigned int xattr; +}; + +struct squashfs_dev_inode_header { + unsigned short inode_type; + unsigned short mode; + unsigned short uid; + unsigned short guid; + int mtime; + unsigned int inode_number; + unsigned int nlink; + unsigned int rdev; +}; + +struct squashfs_ldev_inode_header { + unsigned short inode_type; + unsigned short mode; + unsigned short uid; + unsigned short guid; + int mtime; + unsigned int inode_number; + unsigned int nlink; + unsigned int rdev; + unsigned int xattr; +}; + +struct squashfs_symlink_inode_header { + unsigned short inode_type; + unsigned short mode; + unsigned short uid; + unsigned short guid; + int mtime; + unsigned int inode_number; + unsigned int nlink; + unsigned int symlink_size; + char symlink[0]; +}; + +struct squashfs_reg_inode_header { + unsigned short inode_type; + unsigned short mode; + unsigned short uid; + unsigned short guid; + int mtime; + unsigned int inode_number; + unsigned int start_block; + unsigned int fragment; + unsigned int offset; + unsigned int file_size; + unsigned int block_list[0]; +}; + +struct squashfs_lreg_inode_header { + unsigned short inode_type; + unsigned short mode; + unsigned short uid; + unsigned short guid; + int mtime; + unsigned int inode_number; + squashfs_block start_block; + long long file_size; + long long sparse; + unsigned int nlink; + unsigned int fragment; + unsigned int offset; + unsigned int xattr; + unsigned int block_list[0]; +}; + +struct squashfs_dir_inode_header { + unsigned short inode_type; + unsigned short mode; + unsigned short uid; + unsigned short guid; + int mtime; + unsigned int inode_number; + unsigned int start_block; + unsigned int nlink; + unsigned short file_size; + unsigned short offset; + unsigned int parent_inode; +}; + +struct squashfs_ldir_inode_header { + unsigned short inode_type; + unsigned short mode; + unsigned short uid; + unsigned short guid; + int mtime; + unsigned int inode_number; + unsigned int nlink; + unsigned int file_size; + unsigned int start_block; + unsigned int parent_inode; + unsigned short i_count; + unsigned short offset; + unsigned int xattr; + struct squashfs_dir_index index[0]; +}; + +union squashfs_inode_header { + struct squashfs_base_inode_header base; + struct squashfs_dev_inode_header dev; + struct squashfs_ldev_inode_header ldev; + struct squashfs_symlink_inode_header symlink; + struct squashfs_reg_inode_header reg; + struct squashfs_lreg_inode_header lreg; + struct squashfs_dir_inode_header dir; + struct squashfs_ldir_inode_header ldir; + struct squashfs_ipc_inode_header ipc; + struct squashfs_lipc_inode_header lipc; +}; + +struct squashfs_dir_entry { + unsigned short offset; + short inode_number; + unsigned short type; + unsigned short size; + char name[0]; +}; + +struct squashfs_dir_header { + unsigned int count; + unsigned int start_block; + unsigned int inode_number; +}; + +struct squashfs_fragment_entry { + long long start_block; + unsigned int size; + unsigned int unused; +}; + +struct squashfs_xattr_entry { + unsigned short type; + unsigned short size; +}; + +struct squashfs_xattr_val { + unsigned int vsize; +}; + +struct squashfs_xattr_id { + long long xattr; + unsigned int count; + unsigned int size; +}; + +struct squashfs_xattr_table { + long long xattr_table_start; + unsigned int xattr_ids; + unsigned int unused; +}; + +#endif
diff --git a/squashfs-tools/squashfs-tools/squashfs_swap.h b/squashfs-tools/squashfs-tools/squashfs_swap.h new file mode 100644 index 0000000..f1becfc --- /dev/null +++ b/squashfs-tools/squashfs-tools/squashfs_swap.h
@@ -0,0 +1,424 @@ +#ifndef SQUASHFS_SWAP_H +#define SQUASHFS_SWAP_H +/* + * Squashfs + * + * Copyright (c) 2008, 2009, 2010, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * squashfs_swap.h + */ + +/* + * macros to convert each stucture from big endian to little endian + */ + +#if __BYTE_ORDER == __BIG_ENDIAN +#include <stddef.h> +extern void swap_le16(void *, void *); +extern void swap_le32(void *, void *); +extern void swap_le64(void *, void *); +extern void swap_le16_num(void *, void *, int); +extern void swap_le32_num(void *, void *, int); +extern void swap_le64_num(void *, void *, int); +extern unsigned short inswap_le16(unsigned short); +extern unsigned int inswap_le32(unsigned int); +extern long long inswap_le64(long long); +extern void inswap_le16_num(unsigned short *, int); +extern void inswap_le32_num(unsigned int *, int); +extern void inswap_le64_num(long long *, int); + +#define _SQUASHFS_SWAP_SUPER_BLOCK(s, d, SWAP_FUNC) {\ + SWAP_FUNC(32, s, d, s_magic, struct squashfs_super_block);\ + SWAP_FUNC(32, s, d, inodes, struct squashfs_super_block);\ + SWAP_FUNC##S(32, s, d, mkfs_time, struct squashfs_super_block);\ + SWAP_FUNC(32, s, d, block_size, struct squashfs_super_block);\ + SWAP_FUNC(32, s, d, fragments, struct squashfs_super_block);\ + SWAP_FUNC(16, s, d, compression, struct squashfs_super_block);\ + SWAP_FUNC(16, s, d, block_log, struct squashfs_super_block);\ + SWAP_FUNC(16, s, d, flags, struct squashfs_super_block);\ + SWAP_FUNC(16, s, d, no_ids, struct squashfs_super_block);\ + SWAP_FUNC(16, s, d, s_major, struct squashfs_super_block);\ + SWAP_FUNC(16, s, d, s_minor, struct squashfs_super_block);\ + SWAP_FUNC(64, s, d, root_inode, struct squashfs_super_block);\ + SWAP_FUNC(64, s, d, bytes_used, struct squashfs_super_block);\ + SWAP_FUNC(64, s, d, id_table_start, struct squashfs_super_block);\ + SWAP_FUNC(64, s, d, xattr_id_table_start, struct squashfs_super_block);\ + SWAP_FUNC(64, s, d, inode_table_start, struct squashfs_super_block);\ + SWAP_FUNC(64, s, d, directory_table_start, struct squashfs_super_block);\ + SWAP_FUNC(64, s, d, fragment_table_start, struct squashfs_super_block);\ + SWAP_FUNC(64, s, d, lookup_table_start, struct squashfs_super_block);\ +} + +#define _SQUASHFS_SWAP_DIR_INDEX(s, d, SWAP_FUNC) {\ + SWAP_FUNC(32, s, d, index, struct squashfs_dir_index);\ + SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_index);\ + SWAP_FUNC(32, s, d, size, struct squashfs_dir_index);\ +} + +#define _SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, inode_type, struct squashfs_base_inode_header);\ + SWAP_FUNC(16, s, d, mode, struct squashfs_base_inode_header);\ + SWAP_FUNC(16, s, d, uid, struct squashfs_base_inode_header);\ + SWAP_FUNC(16, s, d, guid, struct squashfs_base_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_base_inode_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_base_inode_header);\ +} + +#define _SQUASHFS_SWAP_IPC_INODE_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, inode_type, struct squashfs_ipc_inode_header);\ + SWAP_FUNC(16, s, d, mode, struct squashfs_ipc_inode_header);\ + SWAP_FUNC(16, s, d, uid, struct squashfs_ipc_inode_header);\ + SWAP_FUNC(16, s, d, guid, struct squashfs_ipc_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_ipc_inode_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_ipc_inode_header);\ + SWAP_FUNC(32, s, d, nlink, struct squashfs_ipc_inode_header);\ +} + +#define _SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, inode_type, struct squashfs_lipc_inode_header);\ + SWAP_FUNC(16, s, d, mode, struct squashfs_lipc_inode_header);\ + SWAP_FUNC(16, s, d, uid, struct squashfs_lipc_inode_header);\ + SWAP_FUNC(16, s, d, guid, struct squashfs_lipc_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_lipc_inode_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_lipc_inode_header);\ + SWAP_FUNC(32, s, d, nlink, struct squashfs_lipc_inode_header);\ + SWAP_FUNC(32, s, d, xattr, struct squashfs_lipc_inode_header);\ +} + +#define _SQUASHFS_SWAP_DEV_INODE_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, inode_type, struct squashfs_dev_inode_header);\ + SWAP_FUNC(16, s, d, mode, struct squashfs_dev_inode_header);\ + SWAP_FUNC(16, s, d, uid, struct squashfs_dev_inode_header);\ + SWAP_FUNC(16, s, d, guid, struct squashfs_dev_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_dev_inode_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_dev_inode_header);\ + SWAP_FUNC(32, s, d, nlink, struct squashfs_dev_inode_header);\ + SWAP_FUNC(32, s, d, rdev, struct squashfs_dev_inode_header);\ +} + +#define _SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, inode_type, struct squashfs_ldev_inode_header);\ + SWAP_FUNC(16, s, d, mode, struct squashfs_ldev_inode_header);\ + SWAP_FUNC(16, s, d, uid, struct squashfs_ldev_inode_header);\ + SWAP_FUNC(16, s, d, guid, struct squashfs_ldev_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_ldev_inode_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_ldev_inode_header);\ + SWAP_FUNC(32, s, d, nlink, struct squashfs_ldev_inode_header);\ + SWAP_FUNC(32, s, d, rdev, struct squashfs_ldev_inode_header);\ + SWAP_FUNC(32, s, d, xattr, struct squashfs_ldev_inode_header);\ +} + +#define _SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, inode_type, struct squashfs_symlink_inode_header);\ + SWAP_FUNC(16, s, d, mode, struct squashfs_symlink_inode_header);\ + SWAP_FUNC(16, s, d, uid, struct squashfs_symlink_inode_header);\ + SWAP_FUNC(16, s, d, guid, struct squashfs_symlink_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_symlink_inode_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_symlink_inode_header);\ + SWAP_FUNC(32, s, d, nlink, struct squashfs_symlink_inode_header);\ + SWAP_FUNC(32, s, d, symlink_size, struct squashfs_symlink_inode_header);\ +} + +#define _SQUASHFS_SWAP_REG_INODE_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, inode_type, struct squashfs_reg_inode_header);\ + SWAP_FUNC(16, s, d, mode, struct squashfs_reg_inode_header);\ + SWAP_FUNC(16, s, d, uid, struct squashfs_reg_inode_header);\ + SWAP_FUNC(16, s, d, guid, struct squashfs_reg_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_reg_inode_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_reg_inode_header);\ + SWAP_FUNC(32, s, d, start_block, struct squashfs_reg_inode_header);\ + SWAP_FUNC(32, s, d, fragment, struct squashfs_reg_inode_header);\ + SWAP_FUNC(32, s, d, offset, struct squashfs_reg_inode_header);\ + SWAP_FUNC(32, s, d, file_size, struct squashfs_reg_inode_header);\ +} + +#define _SQUASHFS_SWAP_LREG_INODE_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, inode_type, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(16, s, d, mode, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(16, s, d, uid, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(16, s, d, guid, struct squashfs_lreg_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(64, s, d, start_block, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(64, s, d, file_size, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(64, s, d, sparse, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(32, s, d, nlink, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(32, s, d, fragment, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(32, s, d, offset, struct squashfs_lreg_inode_header);\ + SWAP_FUNC(32, s, d, xattr, struct squashfs_lreg_inode_header);\ +} + +#define _SQUASHFS_SWAP_DIR_INODE_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, inode_type, struct squashfs_dir_inode_header);\ + SWAP_FUNC(16, s, d, mode, struct squashfs_dir_inode_header);\ + SWAP_FUNC(16, s, d, uid, struct squashfs_dir_inode_header);\ + SWAP_FUNC(16, s, d, guid, struct squashfs_dir_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_dir_inode_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_dir_inode_header);\ + SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_inode_header);\ + SWAP_FUNC(32, s, d, nlink, struct squashfs_dir_inode_header);\ + SWAP_FUNC(16, s, d, file_size, struct squashfs_dir_inode_header);\ + SWAP_FUNC(16, s, d, offset, struct squashfs_dir_inode_header);\ + SWAP_FUNC(32, s, d, parent_inode, struct squashfs_dir_inode_header);\ +} + +#define _SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, inode_type, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(16, s, d, mode, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(16, s, d, uid, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(16, s, d, guid, struct squashfs_ldir_inode_header);\ + SWAP_FUNC##S(32, s, d, mtime, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(32, s, d, nlink, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(32, s, d, file_size, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(32, s, d, start_block, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(32, s, d, parent_inode, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(16, s, d, i_count, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(16, s, d, offset, struct squashfs_ldir_inode_header);\ + SWAP_FUNC(32, s, d, xattr, struct squashfs_ldir_inode_header);\ +} + +#define _SQUASHFS_SWAP_DIR_ENTRY(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, offset, struct squashfs_dir_entry);\ + SWAP_FUNC##S(16, s, d, inode_number, struct squashfs_dir_entry);\ + SWAP_FUNC(16, s, d, type, struct squashfs_dir_entry);\ + SWAP_FUNC(16, s, d, size, struct squashfs_dir_entry);\ +} + +#define _SQUASHFS_SWAP_DIR_HEADER(s, d, SWAP_FUNC) {\ + SWAP_FUNC(32, s, d, count, struct squashfs_dir_header);\ + SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_header);\ + SWAP_FUNC(32, s, d, inode_number, struct squashfs_dir_header);\ +} + +#define _SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d, SWAP_FUNC) {\ + SWAP_FUNC(64, s, d, start_block, struct squashfs_fragment_entry);\ + SWAP_FUNC(32, s, d, size, struct squashfs_fragment_entry);\ +} + +#define _SQUASHFS_SWAP_XATTR_ENTRY(s, d, SWAP_FUNC) {\ + SWAP_FUNC(16, s, d, type, struct squashfs_xattr_entry);\ + SWAP_FUNC(16, s, d, size, struct squashfs_xattr_entry);\ +} + +#define _SQUASHFS_SWAP_XATTR_VAL(s, d, SWAP_FUNC) {\ + SWAP_FUNC(32, s, d, vsize, struct squashfs_xattr_val);\ +} + +#define _SQUASHFS_SWAP_XATTR_ID(s, d, SWAP_FUNC) {\ + SWAP_FUNC(64, s, d, xattr, struct squashfs_xattr_id);\ + SWAP_FUNC(32, s, d, count, struct squashfs_xattr_id);\ + SWAP_FUNC(32, s, d, size, struct squashfs_xattr_id);\ +} + +#define _SQUASHFS_SWAP_XATTR_TABLE(s, d, SWAP_FUNC) {\ + SWAP_FUNC(64, s, d, xattr_table_start, struct squashfs_xattr_table);\ + SWAP_FUNC(32, s, d, xattr_ids, struct squashfs_xattr_table);\ +} + +/* big endian architecture copy and swap macros */ +#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) \ + _SQUASHFS_SWAP_SUPER_BLOCK(s, d, SWAP_LE) +#define SQUASHFS_SWAP_DIR_INDEX(s, d) \ + _SQUASHFS_SWAP_DIR_INDEX(s, d, SWAP_LE) +#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d) \ + _SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) \ + _SQUASHFS_SWAP_IPC_INODE_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d) \ + _SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) \ + _SQUASHFS_SWAP_DEV_INODE_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d) \ + _SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) \ + _SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) \ + _SQUASHFS_SWAP_REG_INODE_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) \ + _SQUASHFS_SWAP_LREG_INODE_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) \ + _SQUASHFS_SWAP_DIR_INODE_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) \ + _SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_DIR_ENTRY(s, d) \ + _SQUASHFS_SWAP_DIR_ENTRY(s, d, SWAP_LE) +#define SQUASHFS_SWAP_DIR_HEADER(s, d) \ + _SQUASHFS_SWAP_DIR_HEADER(s, d, SWAP_LE) +#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) \ + _SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d, SWAP_LE) +#define SQUASHFS_SWAP_XATTR_ENTRY(s, d) \ + _SQUASHFS_SWAP_XATTR_ENTRY(s, d, SWAP_LE) +#define SQUASHFS_SWAP_XATTR_VAL(s, d) \ + _SQUASHFS_SWAP_XATTR_VAL(s, d, SWAP_LE) +#define SQUASHFS_SWAP_XATTR_ID(s, d) \ + _SQUASHFS_SWAP_XATTR_ID(s, d, SWAP_LE) +#define SQUASHFS_SWAP_XATTR_TABLE(s, d) \ + _SQUASHFS_SWAP_XATTR_TABLE(s, d, SWAP_LE) +#define SWAP_LE(bits, s, d, field, type) \ + SWAP_LE##bits(((void *)(s)) + offsetof(type, field), \ + ((void *)(d)) + offsetof(type, field)) +#define SWAP_LES(bits, s, d, field, type) \ + SWAP_LE(bits, s, d, field, type) +#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1) +#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) \ + SQUASHFS_SWAP_LONG_LONGS(s, d, n) +#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) +#define SQUASHFS_SWAP_ID_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) + +#define SQUASHFS_SWAP_SHORTS(s, d, n) swap_le16_num(s, d, n) +#define SQUASHFS_SWAP_INTS(s, d, n) swap_le32_num(s, d, n) +#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) swap_le64_num(s, d, n) + +#define SWAP_LE16(s, d) swap_le16(s, d) +#define SWAP_LE32(s, d) swap_le32(s, d) +#define SWAP_LE64(s, d) swap_le64(s, d) + +/* big endian architecture swap in-place macros */ +#define SQUASHFS_INSWAP_SUPER_BLOCK(s) \ + _SQUASHFS_SWAP_SUPER_BLOCK(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_DIR_INDEX(s) \ + _SQUASHFS_SWAP_DIR_INDEX(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_BASE_INODE_HEADER(s) \ + _SQUASHFS_SWAP_BASE_INODE_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_IPC_INODE_HEADER(s) \ + _SQUASHFS_SWAP_IPC_INODE_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_LIPC_INODE_HEADER(s) \ + _SQUASHFS_SWAP_LIPC_INODE_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_DEV_INODE_HEADER(s) \ + _SQUASHFS_SWAP_DEV_INODE_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_LDEV_INODE_HEADER(s) \ + _SQUASHFS_SWAP_LDEV_INODE_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_SYMLINK_INODE_HEADER(s) \ + _SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_REG_INODE_HEADER(s) \ + _SQUASHFS_SWAP_REG_INODE_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_LREG_INODE_HEADER(s) \ + _SQUASHFS_SWAP_LREG_INODE_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_DIR_INODE_HEADER(s) \ + _SQUASHFS_SWAP_DIR_INODE_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_LDIR_INODE_HEADER(s) \ + _SQUASHFS_SWAP_LDIR_INODE_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_DIR_ENTRY(s) \ + _SQUASHFS_SWAP_DIR_ENTRY(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_DIR_HEADER(s) \ + _SQUASHFS_SWAP_DIR_HEADER(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_FRAGMENT_ENTRY(s) \ + _SQUASHFS_SWAP_FRAGMENT_ENTRY(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_XATTR_ENTRY(s) \ + _SQUASHFS_SWAP_XATTR_ENTRY(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_XATTR_VAL(s) \ + _SQUASHFS_SWAP_XATTR_VAL(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_XATTR_ID(s) \ + _SQUASHFS_SWAP_XATTR_ID(s, s, INSWAP_LE) +#define SQUASHFS_INSWAP_XATTR_TABLE(s) \ + _SQUASHFS_SWAP_XATTR_TABLE(s, s, INSWAP_LE) +#define INSWAP_LE(bits, s, d, field, type) \ + (s)->field = inswap_le##bits((s)->field) +#define INSWAP_LES(bits, s, d, field, type) \ + (s)->field = INSWAP_LES##bits((s)->field) +#define INSWAP_LES16(num) (short) inswap_le16((unsigned short) (num)) +#define INSWAP_LES32(num) (int) inswap_le32((unsigned int) (num)) +#define SQUASHFS_INSWAP_INODE_T(s) s = inswap_le64(s) +#define SQUASHFS_INSWAP_FRAGMENT_INDEXES(s, n) inswap_le64_num(s, n) +#define SQUASHFS_INSWAP_LOOKUP_BLOCKS(s, n) inswap_le64_num(s, n) +#define SQUASHFS_INSWAP_ID_BLOCKS(s, n) inswap_le64_num(s, n) +#define SQUASHFS_INSWAP_SHORTS(s, n) inswap_le16_num(s, n) +#define SQUASHFS_INSWAP_INTS(s, n) inswap_le32_num(s, n) +#define SQUASHFS_INSWAP_LONG_LONGS(s, n) inswap_le64_num(s, n) +#else +/* little endian architecture, just copy */ +#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_super_block)) +#define SQUASHFS_SWAP_DIR_INDEX(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_index)) +#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_base_inode_header)) +#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ipc_inode_header)) +#define SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_lipc_inode_header)) +#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dev_inode_header)) +#define SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ldev_inode_header)) +#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_symlink_inode_header)) +#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_reg_inode_header)) +#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_lreg_inode_header)) +#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_inode_header)) +#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ldir_inode_header)) +#define SQUASHFS_SWAP_DIR_ENTRY(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_entry)) +#define SQUASHFS_SWAP_DIR_HEADER(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_header)) +#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_fragment_entry)) +#define SQUASHFS_SWAP_XATTR_ENTRY(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_entry)) +#define SQUASHFS_SWAP_XATTR_VAL(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_val)) +#define SQUASHFS_SWAP_XATTR_ID(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_id)) +#define SQUASHFS_SWAP_XATTR_TABLE(s, d) \ + SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_table)) +#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1) +#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) \ + SQUASHFS_SWAP_LONG_LONGS(s, d, n) +#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) +#define SQUASHFS_SWAP_ID_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) + +#define SQUASHFS_MEMCPY(s, d, n) memcpy(d, s, n) +#define SQUASHFS_SWAP_SHORTS(s, d, n) memcpy(d, s, n * sizeof(short)) +#define SQUASHFS_SWAP_INTS(s, d, n) memcpy(d, s, n * sizeof(int)) +#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) \ + memcpy(d, s, n * sizeof(long long)) + +/* little endian architecture, data already in place so do nothing */ +#define SQUASHFS_INSWAP_SUPER_BLOCK(s) +#define SQUASHFS_INSWAP_DIR_INDEX(s) +#define SQUASHFS_INSWAP_BASE_INODE_HEADER(s) +#define SQUASHFS_INSWAP_IPC_INODE_HEADER(s) +#define SQUASHFS_INSWAP_LIPC_INODE_HEADER(s) +#define SQUASHFS_INSWAP_DEV_INODE_HEADER(s) +#define SQUASHFS_INSWAP_LDEV_INODE_HEADER(s) +#define SQUASHFS_INSWAP_SYMLINK_INODE_HEADER(s) +#define SQUASHFS_INSWAP_REG_INODE_HEADER(s) +#define SQUASHFS_INSWAP_LREG_INODE_HEADER(s) +#define SQUASHFS_INSWAP_DIR_INODE_HEADER(s) +#define SQUASHFS_INSWAP_LDIR_INODE_HEADER(s) +#define SQUASHFS_INSWAP_DIR_ENTRY(s) +#define SQUASHFS_INSWAP_DIR_HEADER(s) +#define SQUASHFS_INSWAP_FRAGMENT_ENTRY(s) +#define SQUASHFS_INSWAP_XATTR_ENTRY(s) +#define SQUASHFS_INSWAP_XATTR_VAL(s) +#define SQUASHFS_INSWAP_XATTR_ID(s) +#define SQUASHFS_INSWAP_XATTR_TABLE(s) +#define SQUASHFS_INSWAP_INODE_T(s) +#define SQUASHFS_INSWAP_FRAGMENT_INDEXES(s, n) +#define SQUASHFS_INSWAP_LOOKUP_BLOCKS(s, n) +#define SQUASHFS_INSWAP_ID_BLOCKS(s, n) +#define SQUASHFS_INSWAP_SHORTS(s, n) +#define SQUASHFS_INSWAP_INTS(s, n) +#define SQUASHFS_INSWAP_LONG_LONGS(s, n) +#endif +#endif
diff --git a/squashfs-tools/squashfs-tools/swap.c b/squashfs-tools/squashfs-tools/swap.c new file mode 100644 index 0000000..45c8cbc --- /dev/null +++ b/squashfs-tools/squashfs-tools/swap.c
@@ -0,0 +1,123 @@ +/* + * Copyright (c) 2009, 2010 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * swap.c + */ + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include <endian.h> +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +void swap_le16(void *src, void *dest) +{ + unsigned char *s = src; + unsigned char *d = dest; + + d[0] = s[1]; + d[1] = s[0]; +} + + +void swap_le32(void *src, void *dest) +{ + unsigned char *s = src; + unsigned char *d = dest; + + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; +} + + +void swap_le64(void *src, void *dest) +{ + unsigned char *s = src; + unsigned char *d = dest; + + d[0] = s[7]; + d[1] = s[6]; + d[2] = s[5]; + d[3] = s[4]; + d[4] = s[3]; + d[5] = s[2]; + d[6] = s[1]; + d[7] = s[0]; +} + + +unsigned short inswap_le16(unsigned short num) +{ + return (num >> 8) | + ((num & 0xff) << 8); +} + + +unsigned int inswap_le32(unsigned int num) +{ + return (num >> 24) | + ((num & 0xff0000) >> 8) | + ((num & 0xff00) << 8) | + ((num & 0xff) << 24); +} + + +long long inswap_le64(long long n) +{ + unsigned long long num = n; + + return (num >> 56) | + ((num & 0xff000000000000LL) >> 40) | + ((num & 0xff0000000000LL) >> 24) | + ((num & 0xff00000000LL) >> 8) | + ((num & 0xff000000) << 8) | + ((num & 0xff0000) << 24) | + ((num & 0xff00) << 40) | + ((num & 0xff) << 56); +} + + +#define SWAP_LE_NUM(BITS) \ +void swap_le##BITS##_num(void *s, void *d, int n) \ +{\ + int i;\ + for(i = 0; i < n; i++, s += BITS / 8, d += BITS / 8)\ + swap_le##BITS(s, d);\ +} + +SWAP_LE_NUM(16) +SWAP_LE_NUM(32) +SWAP_LE_NUM(64) + +#define INSWAP_LE_NUM(BITS, TYPE) \ +void inswap_le##BITS##_num(TYPE *s, int n) \ +{\ + int i;\ + for(i = 0; i < n; i++)\ + s[i] = inswap_le##BITS(s[i]);\ +} + +INSWAP_LE_NUM(16, unsigned short) +INSWAP_LE_NUM(32, unsigned int) +INSWAP_LE_NUM(64, long long) +#endif
diff --git a/squashfs-tools/squashfs-tools/unsquash-1.c b/squashfs-tools/squashfs-tools/unsquash-1.c new file mode 100644 index 0000000..c2e7d38 --- /dev/null +++ b/squashfs-tools/squashfs-tools/unsquash-1.c
@@ -0,0 +1,357 @@ +/* + * Unsquash a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2009, 2010, 2011, 2012 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquash-1.c + */ + +#include "unsquashfs.h" +#include "squashfs_compat.h" + +void read_block_list_1(unsigned int *block_list, char *block_ptr, int blocks) +{ + unsigned short block_size; + int i; + + TRACE("read_block_list: blocks %d\n", blocks); + + for(i = 0; i < blocks; i++, block_ptr += 2) { + if(swap) { + unsigned short sblock_size; + memcpy(&sblock_size, block_ptr, sizeof(unsigned short)); + SQUASHFS_SWAP_SHORTS_3((&block_size), &sblock_size, 1); + } else + memcpy(&block_size, block_ptr, sizeof(unsigned short)); + block_list[i] = SQUASHFS_COMPRESSED_SIZE(block_size) | + (SQUASHFS_COMPRESSED(block_size) ? 0 : + SQUASHFS_COMPRESSED_BIT_BLOCK); + } +} + + +int read_fragment_table_1(long long *directory_table_end) +{ + TRACE("read_fragment_table\n"); + *directory_table_end = sBlk.s.fragment_table_start; + return TRUE; +} + + +struct inode *read_inode_1(unsigned int start_block, unsigned int offset) +{ + static union squashfs_inode_header_1 header; + long long start = sBlk.s.inode_table_start + start_block; + int bytes = lookup_entry(inode_table_hash, start); + char *block_ptr = inode_table + bytes + offset; + static struct inode i; + + TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset); + + if(bytes == -1) + EXIT_UNSQUASH("read_inode: inode table block %lld not found\n", + start); + + if(swap) { + squashfs_base_inode_header_1 sinode; + memcpy(&sinode, block_ptr, sizeof(header.base)); + SQUASHFS_SWAP_BASE_INODE_HEADER_1(&header.base, &sinode, + sizeof(squashfs_base_inode_header_1)); + } else + memcpy(&header.base, block_ptr, sizeof(header.base)); + + i.uid = (uid_t) uid_table[(header.base.inode_type - 1) / + SQUASHFS_TYPES * 16 + header.base.uid]; + if(header.base.inode_type == SQUASHFS_IPC_TYPE) { + squashfs_ipc_inode_header_1 *inodep = &header.ipc; + + if(swap) { + squashfs_ipc_inode_header_1 sinodep; + memcpy(&sinodep, block_ptr, sizeof(sinodep)); + SQUASHFS_SWAP_IPC_INODE_HEADER_1(inodep, &sinodep); + } else + memcpy(inodep, block_ptr, sizeof(*inodep)); + + if(inodep->type == SQUASHFS_SOCKET_TYPE) { + i.mode = S_IFSOCK | header.base.mode; + i.type = SQUASHFS_SOCKET_TYPE; + } else { + i.mode = S_IFIFO | header.base.mode; + i.type = SQUASHFS_FIFO_TYPE; + } + i.uid = (uid_t) uid_table[inodep->offset * 16 + inodep->uid]; + } else { + i.mode = lookup_type[(header.base.inode_type - 1) % + SQUASHFS_TYPES + 1] | header.base.mode; + i.type = (header.base.inode_type - 1) % SQUASHFS_TYPES + 1; + } + + i.xattr = SQUASHFS_INVALID_XATTR; + i.gid = header.base.guid == 15 ? i.uid : + (uid_t) guid_table[header.base.guid]; + i.time = sBlk.s.mkfs_time; + i.inode_number = inode_number ++; + + switch(i.type) { + case SQUASHFS_DIR_TYPE: { + squashfs_dir_inode_header_1 *inode = &header.dir; + + if(swap) { + squashfs_dir_inode_header_1 sinode; + memcpy(&sinode, block_ptr, sizeof(header.dir)); + SQUASHFS_SWAP_DIR_INODE_HEADER_1(inode, + &sinode); + } else + memcpy(inode, block_ptr, sizeof(header.dir)); + + i.data = inode->file_size; + i.offset = inode->offset; + i.start = inode->start_block; + i.time = inode->mtime; + break; + } + case SQUASHFS_FILE_TYPE: { + squashfs_reg_inode_header_1 *inode = &header.reg; + + if(swap) { + squashfs_reg_inode_header_1 sinode; + memcpy(&sinode, block_ptr, sizeof(sinode)); + SQUASHFS_SWAP_REG_INODE_HEADER_1(inode, + &sinode); + } else + memcpy(inode, block_ptr, sizeof(*inode)); + + i.data = inode->file_size; + i.time = inode->mtime; + i.blocks = (i.data + sBlk.s.block_size - 1) >> + sBlk.s.block_log; + i.start = inode->start_block; + i.block_ptr = block_ptr + sizeof(*inode); + i.fragment = 0; + i.frag_bytes = 0; + i.offset = 0; + i.sparse = 0; + break; + } + case SQUASHFS_SYMLINK_TYPE: { + squashfs_symlink_inode_header_1 *inodep = + &header.symlink; + + if(swap) { + squashfs_symlink_inode_header_1 sinodep; + memcpy(&sinodep, block_ptr, sizeof(sinodep)); + SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(inodep, + &sinodep); + } else + memcpy(inodep, block_ptr, sizeof(*inodep)); + + i.symlink = malloc(inodep->symlink_size + 1); + if(i.symlink == NULL) + EXIT_UNSQUASH("read_inode: failed to malloc " + "symlink data\n"); + strncpy(i.symlink, block_ptr + + sizeof(squashfs_symlink_inode_header_1), + inodep->symlink_size); + i.symlink[inodep->symlink_size] = '\0'; + i.data = inodep->symlink_size; + break; + } + case SQUASHFS_BLKDEV_TYPE: + case SQUASHFS_CHRDEV_TYPE: { + squashfs_dev_inode_header_1 *inodep = &header.dev; + + if(swap) { + squashfs_dev_inode_header_1 sinodep; + memcpy(&sinodep, block_ptr, sizeof(sinodep)); + SQUASHFS_SWAP_DEV_INODE_HEADER_1(inodep, + &sinodep); + } else + memcpy(inodep, block_ptr, sizeof(*inodep)); + + i.data = inodep->rdev; + break; + } + case SQUASHFS_FIFO_TYPE: + case SQUASHFS_SOCKET_TYPE: { + i.data = 0; + break; + } + default: + EXIT_UNSQUASH("Unknown inode type %d in " + " read_inode_header_1!\n", + header.base.inode_type); + } + return &i; +} + + +struct dir *squashfs_opendir_1(unsigned int block_start, unsigned int offset, + struct inode **i) +{ + squashfs_dir_header_2 dirh; + char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1] + __attribute__((aligned)); + squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer; + long long start; + int bytes; + int dir_count, size; + struct dir_ent *new_dir; + struct dir *dir; + + TRACE("squashfs_opendir: inode start block %d, offset %d\n", + block_start, offset); + + *i = s_ops.read_inode(block_start, offset); + + dir = malloc(sizeof(struct dir)); + if(dir == NULL) + EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); + + dir->dir_count = 0; + dir->cur_entry = 0; + dir->mode = (*i)->mode; + dir->uid = (*i)->uid; + dir->guid = (*i)->gid; + dir->mtime = (*i)->time; + dir->xattr = (*i)->xattr; + dir->dirs = NULL; + + if ((*i)->data == 0) + /* + * if the directory is empty, skip the unnecessary + * lookup_entry, this fixes the corner case with + * completely empty filesystems where lookup_entry correctly + * returning -1 is incorrectly treated as an error + */ + return dir; + + start = sBlk.s.directory_table_start + (*i)->start; + bytes = lookup_entry(directory_table_hash, start); + if(bytes == -1) + EXIT_UNSQUASH("squashfs_opendir: directory block %d not " + "found!\n", block_start); + + bytes += (*i)->offset; + size = (*i)->data + bytes; + + while(bytes < size) { + if(swap) { + squashfs_dir_header_2 sdirh; + memcpy(&sdirh, directory_table + bytes, sizeof(sdirh)); + SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); + } else + memcpy(&dirh, directory_table + bytes, sizeof(dirh)); + + dir_count = dirh.count + 1; + TRACE("squashfs_opendir: Read directory header @ byte position " + "%d, %d directory entries\n", bytes, dir_count); + bytes += sizeof(dirh); + + /* dir_count should never be larger than 256 */ + if(dir_count > 256) + goto corrupted; + + while(dir_count--) { + if(swap) { + squashfs_dir_entry_2 sdire; + memcpy(&sdire, directory_table + bytes, + sizeof(sdire)); + SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); + } else + memcpy(dire, directory_table + bytes, + sizeof(*dire)); + bytes += sizeof(*dire); + + /* size should never be larger than SQUASHFS_NAME_LEN */ + if(dire->size > SQUASHFS_NAME_LEN) + goto corrupted; + + memcpy(dire->name, directory_table + bytes, + dire->size + 1); + dire->name[dire->size + 1] = '\0'; + TRACE("squashfs_opendir: directory entry %s, inode " + "%d:%d, type %d\n", dire->name, + dirh.start_block, dire->offset, dire->type); + if((dir->dir_count % DIR_ENT_SIZE) == 0) { + new_dir = realloc(dir->dirs, (dir->dir_count + + DIR_ENT_SIZE) * sizeof(struct dir_ent)); + if(new_dir == NULL) + EXIT_UNSQUASH("squashfs_opendir: " + "realloc failed!\n"); + dir->dirs = new_dir; + } + strcpy(dir->dirs[dir->dir_count].name, dire->name); + dir->dirs[dir->dir_count].start_block = + dirh.start_block; + dir->dirs[dir->dir_count].offset = dire->offset; + dir->dirs[dir->dir_count].type = dire->type; + dir->dir_count ++; + bytes += dire->size + 1; + } + } + + return dir; + +corrupted: + free(dir->dirs); + free(dir); + return NULL; +} + + +int read_uids_guids_1() +{ + int res; + + TRACE("read_uids_guids: no_uids %d, no_guids %d\n", sBlk.no_uids, + sBlk.no_guids); + + uid_table = malloc((sBlk.no_uids + sBlk.no_guids) * + sizeof(unsigned int)); + if(uid_table == NULL) { + ERROR("read_uids_guids: failed to allocate uid/gid table\n"); + return FALSE; + } + + guid_table = uid_table + sBlk.no_uids; + + if(swap) { + unsigned int suid_table[sBlk.no_uids + sBlk.no_guids]; + + res = read_fs_bytes(fd, sBlk.uid_start, (sBlk.no_uids + + sBlk.no_guids) * sizeof(unsigned int), suid_table); + if(res == FALSE) { + ERROR("read_uids_guids: failed to read uid/gid table" + "\n"); + return FALSE; + } + SQUASHFS_SWAP_INTS_3(uid_table, suid_table, + sBlk.no_uids + sBlk.no_guids); + } else { + res = read_fs_bytes(fd, sBlk.uid_start, (sBlk.no_uids + + sBlk.no_guids) * sizeof(unsigned int), uid_table); + if(res == FALSE) { + ERROR("read_uids_guids: failed to read uid/gid table" + "\n"); + return FALSE; + } + } + + return TRUE; +}
diff --git a/squashfs-tools/squashfs-tools/unsquash-2.c b/squashfs-tools/squashfs-tools/unsquash-2.c new file mode 100644 index 0000000..0f2bfe8 --- /dev/null +++ b/squashfs-tools/squashfs-tools/unsquash-2.c
@@ -0,0 +1,270 @@ +/* + * Unsquash a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2009, 2010, 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquash-2.c + */ + +#include "unsquashfs.h" +#include "squashfs_compat.h" + +static squashfs_fragment_entry_2 *fragment_table; + +void read_block_list_2(unsigned int *block_list, char *block_ptr, int blocks) +{ + TRACE("read_block_list: blocks %d\n", blocks); + + if(swap) { + unsigned int sblock_list[blocks]; + memcpy(sblock_list, block_ptr, blocks * sizeof(unsigned int)); + SQUASHFS_SWAP_INTS_3(block_list, sblock_list, blocks); + } else + memcpy(block_list, block_ptr, blocks * sizeof(unsigned int)); +} + + +int read_fragment_table_2(long long *directory_table_end) +{ + int res, i; + int bytes = SQUASHFS_FRAGMENT_BYTES_2(sBlk.s.fragments); + int indexes = SQUASHFS_FRAGMENT_INDEXES_2(sBlk.s.fragments); + unsigned int fragment_table_index[indexes]; + + TRACE("read_fragment_table: %d fragments, reading %d fragment indexes " + "from 0x%llx\n", sBlk.s.fragments, indexes, + sBlk.s.fragment_table_start); + + if(sBlk.s.fragments == 0) { + *directory_table_end = sBlk.s.fragment_table_start; + return TRUE; + } + + fragment_table = malloc(bytes); + if(fragment_table == NULL) + EXIT_UNSQUASH("read_fragment_table: failed to allocate " + "fragment table\n"); + + if(swap) { + unsigned int sfragment_table_index[indexes]; + + res = read_fs_bytes(fd, sBlk.s.fragment_table_start, + SQUASHFS_FRAGMENT_INDEX_BYTES_2(sBlk.s.fragments), + sfragment_table_index); + if(res == FALSE) { + ERROR("read_fragment_table: failed to read fragment " + "table index\n"); + return FALSE; + } + SQUASHFS_SWAP_FRAGMENT_INDEXES_2(fragment_table_index, + sfragment_table_index, indexes); + } else { + res = read_fs_bytes(fd, sBlk.s.fragment_table_start, + SQUASHFS_FRAGMENT_INDEX_BYTES_2(sBlk.s.fragments), + fragment_table_index); + if(res == FALSE) { + ERROR("read_fragment_table: failed to read fragment " + "table index\n"); + return FALSE; + } + } + + for(i = 0; i < indexes; i++) { + int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE : + bytes & (SQUASHFS_METADATA_SIZE - 1); + int length = read_block(fd, fragment_table_index[i], NULL, + expected, ((char *) fragment_table) + (i * + SQUASHFS_METADATA_SIZE)); + TRACE("Read fragment table block %d, from 0x%x, length %d\n", i, + fragment_table_index[i], length); + if(length == FALSE) { + ERROR("read_fragment_table: failed to read fragment " + "table block\n"); + return FALSE; + } + } + + if(swap) { + squashfs_fragment_entry_2 sfragment; + for(i = 0; i < sBlk.s.fragments; i++) { + SQUASHFS_SWAP_FRAGMENT_ENTRY_2((&sfragment), + (&fragment_table[i])); + memcpy((char *) &fragment_table[i], (char *) &sfragment, + sizeof(squashfs_fragment_entry_2)); + } + } + + *directory_table_end = fragment_table_index[0]; + return TRUE; +} + + +void read_fragment_2(unsigned int fragment, long long *start_block, int *size) +{ + TRACE("read_fragment: reading fragment %d\n", fragment); + + squashfs_fragment_entry_2 *fragment_entry = &fragment_table[fragment]; + *start_block = fragment_entry->start_block; + *size = fragment_entry->size; +} + + +struct inode *read_inode_2(unsigned int start_block, unsigned int offset) +{ + static union squashfs_inode_header_2 header; + long long start = sBlk.s.inode_table_start + start_block; + int bytes = lookup_entry(inode_table_hash, start); + char *block_ptr = inode_table + bytes + offset; + static struct inode i; + + TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset); + + if(bytes == -1) + EXIT_UNSQUASH("read_inode: inode table block %lld not found\n", + start); + + if(swap) { + squashfs_base_inode_header_2 sinode; + memcpy(&sinode, block_ptr, sizeof(header.base)); + SQUASHFS_SWAP_BASE_INODE_HEADER_2(&header.base, &sinode, + sizeof(squashfs_base_inode_header_2)); + } else + memcpy(&header.base, block_ptr, sizeof(header.base)); + + i.xattr = SQUASHFS_INVALID_XATTR; + i.uid = (uid_t) uid_table[header.base.uid]; + i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid : + (uid_t) guid_table[header.base.guid]; + i.mode = lookup_type[header.base.inode_type] | header.base.mode; + i.type = header.base.inode_type; + i.time = sBlk.s.mkfs_time; + i.inode_number = inode_number++; + + switch(header.base.inode_type) { + case SQUASHFS_DIR_TYPE: { + squashfs_dir_inode_header_2 *inode = &header.dir; + + if(swap) { + squashfs_dir_inode_header_2 sinode; + memcpy(&sinode, block_ptr, sizeof(header.dir)); + SQUASHFS_SWAP_DIR_INODE_HEADER_2(&header.dir, + &sinode); + } else + memcpy(&header.dir, block_ptr, + sizeof(header.dir)); + + i.data = inode->file_size; + i.offset = inode->offset; + i.start = inode->start_block; + i.time = inode->mtime; + break; + } + case SQUASHFS_LDIR_TYPE: { + squashfs_ldir_inode_header_2 *inode = &header.ldir; + + if(swap) { + squashfs_ldir_inode_header_2 sinode; + memcpy(&sinode, block_ptr, sizeof(header.ldir)); + SQUASHFS_SWAP_LDIR_INODE_HEADER_2(&header.ldir, + &sinode); + } else + memcpy(&header.ldir, block_ptr, + sizeof(header.ldir)); + + i.data = inode->file_size; + i.offset = inode->offset; + i.start = inode->start_block; + i.time = inode->mtime; + break; + } + case SQUASHFS_FILE_TYPE: { + squashfs_reg_inode_header_2 *inode = &header.reg; + + if(swap) { + squashfs_reg_inode_header_2 sinode; + memcpy(&sinode, block_ptr, sizeof(sinode)); + SQUASHFS_SWAP_REG_INODE_HEADER_2(inode, + &sinode); + } else + memcpy(inode, block_ptr, sizeof(*inode)); + + i.data = inode->file_size; + i.time = inode->mtime; + i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG + ? 0 : inode->file_size % sBlk.s.block_size; + i.fragment = inode->fragment; + i.offset = inode->offset; + i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? + (i.data + sBlk.s.block_size - 1) >> + sBlk.s.block_log : i.data >> + sBlk.s.block_log; + i.start = inode->start_block; + i.sparse = 0; + i.block_ptr = block_ptr + sizeof(*inode); + break; + } + case SQUASHFS_SYMLINK_TYPE: { + squashfs_symlink_inode_header_2 *inodep = + &header.symlink; + + if(swap) { + squashfs_symlink_inode_header_2 sinodep; + memcpy(&sinodep, block_ptr, sizeof(sinodep)); + SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep, + &sinodep); + } else + memcpy(inodep, block_ptr, sizeof(*inodep)); + + i.symlink = malloc(inodep->symlink_size + 1); + if(i.symlink == NULL) + EXIT_UNSQUASH("read_inode: failed to malloc " + "symlink data\n"); + strncpy(i.symlink, block_ptr + + sizeof(squashfs_symlink_inode_header_2), + inodep->symlink_size); + i.symlink[inodep->symlink_size] = '\0'; + i.data = inodep->symlink_size; + break; + } + case SQUASHFS_BLKDEV_TYPE: + case SQUASHFS_CHRDEV_TYPE: { + squashfs_dev_inode_header_2 *inodep = &header.dev; + + if(swap) { + squashfs_dev_inode_header_2 sinodep; + memcpy(&sinodep, block_ptr, sizeof(sinodep)); + SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, + &sinodep); + } else + memcpy(inodep, block_ptr, sizeof(*inodep)); + + i.data = inodep->rdev; + break; + } + case SQUASHFS_FIFO_TYPE: + case SQUASHFS_SOCKET_TYPE: + i.data = 0; + break; + default: + EXIT_UNSQUASH("Unknown inode type %d in " + "read_inode_header_2!\n", + header.base.inode_type); + } + return &i; +}
diff --git a/squashfs-tools/squashfs-tools/unsquash-3.c b/squashfs-tools/squashfs-tools/unsquash-3.c new file mode 100644 index 0000000..d5af57a --- /dev/null +++ b/squashfs-tools/squashfs-tools/unsquash-3.c
@@ -0,0 +1,393 @@ +/* + * Unsquash a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2009, 2010, 2011, 2012, 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquash-3.c + */ + +#include "unsquashfs.h" +#include "squashfs_compat.h" + +static squashfs_fragment_entry_3 *fragment_table; + +int read_fragment_table_3(long long *directory_table_end) +{ + int res, i; + int bytes = SQUASHFS_FRAGMENT_BYTES_3(sBlk.s.fragments); + int indexes = SQUASHFS_FRAGMENT_INDEXES_3(sBlk.s.fragments); + long long fragment_table_index[indexes]; + + TRACE("read_fragment_table: %d fragments, reading %d fragment indexes " + "from 0x%llx\n", sBlk.s.fragments, indexes, + sBlk.s.fragment_table_start); + + if(sBlk.s.fragments == 0) { + *directory_table_end = sBlk.s.fragment_table_start; + return TRUE; + } + + fragment_table = malloc(bytes); + if(fragment_table == NULL) + EXIT_UNSQUASH("read_fragment_table: failed to allocate " + "fragment table\n"); + + if(swap) { + long long sfragment_table_index[indexes]; + + res = read_fs_bytes(fd, sBlk.s.fragment_table_start, + SQUASHFS_FRAGMENT_INDEX_BYTES_3(sBlk.s.fragments), + sfragment_table_index); + if(res == FALSE) { + ERROR("read_fragment_table: failed to read fragment " + "table index\n"); + return FALSE; + } + SQUASHFS_SWAP_FRAGMENT_INDEXES_3(fragment_table_index, + sfragment_table_index, indexes); + } else { + res = read_fs_bytes(fd, sBlk.s.fragment_table_start, + SQUASHFS_FRAGMENT_INDEX_BYTES_3(sBlk.s.fragments), + fragment_table_index); + if(res == FALSE) { + ERROR("read_fragment_table: failed to read fragment " + "table index\n"); + return FALSE; + } + } + + for(i = 0; i < indexes; i++) { + int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE : + bytes & (SQUASHFS_METADATA_SIZE - 1); + int length = read_block(fd, fragment_table_index[i], NULL, + expected, ((char *) fragment_table) + (i * + SQUASHFS_METADATA_SIZE)); + TRACE("Read fragment table block %d, from 0x%llx, length %d\n", + i, fragment_table_index[i], length); + if(length == FALSE) { + ERROR("read_fragment_table: failed to read fragment " + "table block\n"); + return FALSE; + } + } + + if(swap) { + squashfs_fragment_entry_3 sfragment; + for(i = 0; i < sBlk.s.fragments; i++) { + SQUASHFS_SWAP_FRAGMENT_ENTRY_3((&sfragment), + (&fragment_table[i])); + memcpy((char *) &fragment_table[i], (char *) &sfragment, + sizeof(squashfs_fragment_entry_3)); + } + } + + *directory_table_end = fragment_table_index[0]; + return TRUE; +} + + +void read_fragment_3(unsigned int fragment, long long *start_block, int *size) +{ + TRACE("read_fragment: reading fragment %d\n", fragment); + + squashfs_fragment_entry_3 *fragment_entry = &fragment_table[fragment]; + *start_block = fragment_entry->start_block; + *size = fragment_entry->size; +} + + +struct inode *read_inode_3(unsigned int start_block, unsigned int offset) +{ + static union squashfs_inode_header_3 header; + long long start = sBlk.s.inode_table_start + start_block; + int bytes = lookup_entry(inode_table_hash, start); + char *block_ptr = inode_table + bytes + offset; + static struct inode i; + + TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset); + + if(bytes == -1) + EXIT_UNSQUASH("read_inode: inode table block %lld not found\n", + start); + + if(swap) { + squashfs_base_inode_header_3 sinode; + memcpy(&sinode, block_ptr, sizeof(header.base)); + SQUASHFS_SWAP_BASE_INODE_HEADER_3(&header.base, &sinode, + sizeof(squashfs_base_inode_header_3)); + } else + memcpy(&header.base, block_ptr, sizeof(header.base)); + + i.xattr = SQUASHFS_INVALID_XATTR; + i.uid = (uid_t) uid_table[header.base.uid]; + i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid : + (uid_t) guid_table[header.base.guid]; + i.mode = lookup_type[header.base.inode_type] | header.base.mode; + i.type = header.base.inode_type; + i.time = header.base.mtime; + i.inode_number = header.base.inode_number; + + switch(header.base.inode_type) { + case SQUASHFS_DIR_TYPE: { + squashfs_dir_inode_header_3 *inode = &header.dir; + + if(swap) { + squashfs_dir_inode_header_3 sinode; + memcpy(&sinode, block_ptr, sizeof(header.dir)); + SQUASHFS_SWAP_DIR_INODE_HEADER_3(&header.dir, + &sinode); + } else + memcpy(&header.dir, block_ptr, + sizeof(header.dir)); + + i.data = inode->file_size; + i.offset = inode->offset; + i.start = inode->start_block; + break; + } + case SQUASHFS_LDIR_TYPE: { + squashfs_ldir_inode_header_3 *inode = &header.ldir; + + if(swap) { + squashfs_ldir_inode_header_3 sinode; + memcpy(&sinode, block_ptr, sizeof(header.ldir)); + SQUASHFS_SWAP_LDIR_INODE_HEADER_3(&header.ldir, + &sinode); + } else + memcpy(&header.ldir, block_ptr, + sizeof(header.ldir)); + + i.data = inode->file_size; + i.offset = inode->offset; + i.start = inode->start_block; + break; + } + case SQUASHFS_FILE_TYPE: { + squashfs_reg_inode_header_3 *inode = &header.reg; + + if(swap) { + squashfs_reg_inode_header_3 sinode; + memcpy(&sinode, block_ptr, sizeof(sinode)); + SQUASHFS_SWAP_REG_INODE_HEADER_3(inode, + &sinode); + } else + memcpy(inode, block_ptr, sizeof(*inode)); + + i.data = inode->file_size; + i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG + ? 0 : inode->file_size % sBlk.s.block_size; + i.fragment = inode->fragment; + i.offset = inode->offset; + i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? + (i.data + sBlk.s.block_size - 1) >> + sBlk.s.block_log : + i.data >> sBlk.s.block_log; + i.start = inode->start_block; + i.sparse = 1; + i.block_ptr = block_ptr + sizeof(*inode); + break; + } + case SQUASHFS_LREG_TYPE: { + squashfs_lreg_inode_header_3 *inode = &header.lreg; + + if(swap) { + squashfs_lreg_inode_header_3 sinode; + memcpy(&sinode, block_ptr, sizeof(sinode)); + SQUASHFS_SWAP_LREG_INODE_HEADER_3(inode, + &sinode); + } else + memcpy(inode, block_ptr, sizeof(*inode)); + + i.data = inode->file_size; + i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG + ? 0 : inode->file_size % sBlk.s.block_size; + i.fragment = inode->fragment; + i.offset = inode->offset; + i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? + (inode->file_size + sBlk.s.block_size - 1) >> + sBlk.s.block_log : + inode->file_size >> sBlk.s.block_log; + i.start = inode->start_block; + i.sparse = 1; + i.block_ptr = block_ptr + sizeof(*inode); + break; + } + case SQUASHFS_SYMLINK_TYPE: { + squashfs_symlink_inode_header_3 *inodep = + &header.symlink; + + if(swap) { + squashfs_symlink_inode_header_3 sinodep; + memcpy(&sinodep, block_ptr, sizeof(sinodep)); + SQUASHFS_SWAP_SYMLINK_INODE_HEADER_3(inodep, + &sinodep); + } else + memcpy(inodep, block_ptr, sizeof(*inodep)); + + i.symlink = malloc(inodep->symlink_size + 1); + if(i.symlink == NULL) + EXIT_UNSQUASH("read_inode: failed to malloc " + "symlink data\n"); + strncpy(i.symlink, block_ptr + + sizeof(squashfs_symlink_inode_header_3), + inodep->symlink_size); + i.symlink[inodep->symlink_size] = '\0'; + i.data = inodep->symlink_size; + break; + } + case SQUASHFS_BLKDEV_TYPE: + case SQUASHFS_CHRDEV_TYPE: { + squashfs_dev_inode_header_3 *inodep = &header.dev; + + if(swap) { + squashfs_dev_inode_header_3 sinodep; + memcpy(&sinodep, block_ptr, sizeof(sinodep)); + SQUASHFS_SWAP_DEV_INODE_HEADER_3(inodep, + &sinodep); + } else + memcpy(inodep, block_ptr, sizeof(*inodep)); + + i.data = inodep->rdev; + break; + } + case SQUASHFS_FIFO_TYPE: + case SQUASHFS_SOCKET_TYPE: + i.data = 0; + break; + default: + EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n", + header.base.inode_type); + } + return &i; +} + + +struct dir *squashfs_opendir_3(unsigned int block_start, unsigned int offset, + struct inode **i) +{ + squashfs_dir_header_3 dirh; + char buffer[sizeof(squashfs_dir_entry_3) + SQUASHFS_NAME_LEN + 1] + __attribute__((aligned)); + squashfs_dir_entry_3 *dire = (squashfs_dir_entry_3 *) buffer; + long long start; + int bytes; + int dir_count, size; + struct dir_ent *new_dir; + struct dir *dir; + + TRACE("squashfs_opendir: inode start block %d, offset %d\n", + block_start, offset); + + *i = s_ops.read_inode(block_start, offset); + + dir = malloc(sizeof(struct dir)); + if(dir == NULL) + EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); + + dir->dir_count = 0; + dir->cur_entry = 0; + dir->mode = (*i)->mode; + dir->uid = (*i)->uid; + dir->guid = (*i)->gid; + dir->mtime = (*i)->time; + dir->xattr = (*i)->xattr; + dir->dirs = NULL; + + if ((*i)->data == 3) + /* + * if the directory is empty, skip the unnecessary + * lookup_entry, this fixes the corner case with + * completely empty filesystems where lookup_entry correctly + * returning -1 is incorrectly treated as an error + */ + return dir; + + start = sBlk.s.directory_table_start + (*i)->start; + bytes = lookup_entry(directory_table_hash, start); + + if(bytes == -1) + EXIT_UNSQUASH("squashfs_opendir: directory block %d not " + "found!\n", block_start); + + bytes += (*i)->offset; + size = (*i)->data + bytes - 3; + + while(bytes < size) { + if(swap) { + squashfs_dir_header_3 sdirh; + memcpy(&sdirh, directory_table + bytes, sizeof(sdirh)); + SQUASHFS_SWAP_DIR_HEADER_3(&dirh, &sdirh); + } else + memcpy(&dirh, directory_table + bytes, sizeof(dirh)); + + dir_count = dirh.count + 1; + TRACE("squashfs_opendir: Read directory header @ byte position " + "%d, %d directory entries\n", bytes, dir_count); + bytes += sizeof(dirh); + + /* dir_count should never be larger than 256 */ + if(dir_count > 256) + goto corrupted; + + while(dir_count--) { + if(swap) { + squashfs_dir_entry_3 sdire; + memcpy(&sdire, directory_table + bytes, + sizeof(sdire)); + SQUASHFS_SWAP_DIR_ENTRY_3(dire, &sdire); + } else + memcpy(dire, directory_table + bytes, + sizeof(*dire)); + bytes += sizeof(*dire); + + /* size should never be larger than SQUASHFS_NAME_LEN */ + if(dire->size > SQUASHFS_NAME_LEN) + goto corrupted; + + memcpy(dire->name, directory_table + bytes, + dire->size + 1); + dire->name[dire->size + 1] = '\0'; + TRACE("squashfs_opendir: directory entry %s, inode " + "%d:%d, type %d\n", dire->name, + dirh.start_block, dire->offset, dire->type); + if((dir->dir_count % DIR_ENT_SIZE) == 0) { + new_dir = realloc(dir->dirs, (dir->dir_count + + DIR_ENT_SIZE) * sizeof(struct dir_ent)); + if(new_dir == NULL) + EXIT_UNSQUASH("squashfs_opendir: " + "realloc failed!\n"); + dir->dirs = new_dir; + } + strcpy(dir->dirs[dir->dir_count].name, dire->name); + dir->dirs[dir->dir_count].start_block = + dirh.start_block; + dir->dirs[dir->dir_count].offset = dire->offset; + dir->dirs[dir->dir_count].type = dire->type; + dir->dir_count ++; + bytes += dire->size + 1; + } + } + + return dir; + +corrupted: + free(dir->dirs); + free(dir); + return NULL; +}
diff --git a/squashfs-tools/squashfs-tools/unsquash-4.c b/squashfs-tools/squashfs-tools/unsquash-4.c new file mode 100644 index 0000000..ecdaac7 --- /dev/null +++ b/squashfs-tools/squashfs-tools/unsquash-4.c
@@ -0,0 +1,392 @@ +/* + * Unsquash a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2009, 2010, 2011, 2012, 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquash-4.c + */ + +#include "unsquashfs.h" +#include "squashfs_swap.h" + +static struct squashfs_fragment_entry *fragment_table; +static unsigned int *id_table; + +int read_fragment_table_4(long long *directory_table_end) +{ + int res, i; + int bytes = SQUASHFS_FRAGMENT_BYTES(sBlk.s.fragments); + int indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk.s.fragments); + long long fragment_table_index[indexes]; + + TRACE("read_fragment_table: %d fragments, reading %d fragment indexes " + "from 0x%llx\n", sBlk.s.fragments, indexes, + sBlk.s.fragment_table_start); + + if(sBlk.s.fragments == 0) { + *directory_table_end = sBlk.s.fragment_table_start; + return TRUE; + } + + fragment_table = malloc(bytes); + if(fragment_table == NULL) + EXIT_UNSQUASH("read_fragment_table: failed to allocate " + "fragment table\n"); + + res = read_fs_bytes(fd, sBlk.s.fragment_table_start, + SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk.s.fragments), + fragment_table_index); + if(res == FALSE) { + ERROR("read_fragment_table: failed to read fragment table " + "index\n"); + return FALSE; + } + SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index, indexes); + + for(i = 0; i < indexes; i++) { + int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE : + bytes & (SQUASHFS_METADATA_SIZE - 1); + int length = read_block(fd, fragment_table_index[i], NULL, + expected, ((char *) fragment_table) + (i * + SQUASHFS_METADATA_SIZE)); + TRACE("Read fragment table block %d, from 0x%llx, length %d\n", + i, fragment_table_index[i], length); + if(length == FALSE) { + ERROR("read_fragment_table: failed to read fragment " + "table index\n"); + return FALSE; + } + } + + for(i = 0; i < sBlk.s.fragments; i++) + SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]); + + *directory_table_end = fragment_table_index[0]; + return TRUE; +} + + +void read_fragment_4(unsigned int fragment, long long *start_block, int *size) +{ + TRACE("read_fragment: reading fragment %d\n", fragment); + + struct squashfs_fragment_entry *fragment_entry; + + fragment_entry = &fragment_table[fragment]; + *start_block = fragment_entry->start_block; + *size = fragment_entry->size; +} + + +struct inode *read_inode_4(unsigned int start_block, unsigned int offset) +{ + static union squashfs_inode_header header; + long long start = sBlk.s.inode_table_start + start_block; + int bytes = lookup_entry(inode_table_hash, start); + char *block_ptr = inode_table + bytes + offset; + static struct inode i; + + TRACE("read_inode: reading inode [%d:%d]\n", start_block, offset); + + if(bytes == -1) + EXIT_UNSQUASH("read_inode: inode table block %lld not found\n", + start); + + SQUASHFS_SWAP_BASE_INODE_HEADER(block_ptr, &header.base); + + i.uid = (uid_t) id_table[header.base.uid]; + i.gid = (uid_t) id_table[header.base.guid]; + i.mode = lookup_type[header.base.inode_type] | header.base.mode; + i.type = header.base.inode_type; + i.time = header.base.mtime; + i.inode_number = header.base.inode_number; + + switch(header.base.inode_type) { + case SQUASHFS_DIR_TYPE: { + struct squashfs_dir_inode_header *inode = &header.dir; + + SQUASHFS_SWAP_DIR_INODE_HEADER(block_ptr, inode); + + i.data = inode->file_size; + i.offset = inode->offset; + i.start = inode->start_block; + i.xattr = SQUASHFS_INVALID_XATTR; + break; + } + case SQUASHFS_LDIR_TYPE: { + struct squashfs_ldir_inode_header *inode = &header.ldir; + + SQUASHFS_SWAP_LDIR_INODE_HEADER(block_ptr, inode); + + i.data = inode->file_size; + i.offset = inode->offset; + i.start = inode->start_block; + i.xattr = inode->xattr; + break; + } + case SQUASHFS_FILE_TYPE: { + struct squashfs_reg_inode_header *inode = &header.reg; + + SQUASHFS_SWAP_REG_INODE_HEADER(block_ptr, inode); + + i.data = inode->file_size; + i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG + ? 0 : inode->file_size % sBlk.s.block_size; + i.fragment = inode->fragment; + i.offset = inode->offset; + i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? + (i.data + sBlk.s.block_size - 1) >> + sBlk.s.block_log : + i.data >> sBlk.s.block_log; + i.start = inode->start_block; + i.sparse = 0; + i.block_ptr = block_ptr + sizeof(*inode); + i.xattr = SQUASHFS_INVALID_XATTR; + break; + } + case SQUASHFS_LREG_TYPE: { + struct squashfs_lreg_inode_header *inode = &header.lreg; + + SQUASHFS_SWAP_LREG_INODE_HEADER(block_ptr, inode); + + i.data = inode->file_size; + i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG + ? 0 : inode->file_size % sBlk.s.block_size; + i.fragment = inode->fragment; + i.offset = inode->offset; + i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ? + (inode->file_size + sBlk.s.block_size - 1) >> + sBlk.s.block_log : + inode->file_size >> sBlk.s.block_log; + i.start = inode->start_block; + i.sparse = inode->sparse != 0; + i.block_ptr = block_ptr + sizeof(*inode); + i.xattr = inode->xattr; + break; + } + case SQUASHFS_SYMLINK_TYPE: + case SQUASHFS_LSYMLINK_TYPE: { + struct squashfs_symlink_inode_header *inode = &header.symlink; + + SQUASHFS_SWAP_SYMLINK_INODE_HEADER(block_ptr, inode); + + i.symlink = malloc(inode->symlink_size + 1); + if(i.symlink == NULL) + EXIT_UNSQUASH("read_inode: failed to malloc " + "symlink data\n"); + strncpy(i.symlink, block_ptr + + sizeof(struct squashfs_symlink_inode_header), + inode->symlink_size); + i.symlink[inode->symlink_size] = '\0'; + i.data = inode->symlink_size; + + if(header.base.inode_type == SQUASHFS_LSYMLINK_TYPE) + SQUASHFS_SWAP_INTS(block_ptr + + sizeof(struct squashfs_symlink_inode_header) + + inode->symlink_size, &i.xattr, 1); + else + i.xattr = SQUASHFS_INVALID_XATTR; + break; + } + case SQUASHFS_BLKDEV_TYPE: + case SQUASHFS_CHRDEV_TYPE: { + struct squashfs_dev_inode_header *inode = &header.dev; + + SQUASHFS_SWAP_DEV_INODE_HEADER(block_ptr, inode); + + i.data = inode->rdev; + i.xattr = SQUASHFS_INVALID_XATTR; + break; + } + case SQUASHFS_LBLKDEV_TYPE: + case SQUASHFS_LCHRDEV_TYPE: { + struct squashfs_ldev_inode_header *inode = &header.ldev; + + SQUASHFS_SWAP_LDEV_INODE_HEADER(block_ptr, inode); + + i.data = inode->rdev; + i.xattr = inode->xattr; + break; + } + case SQUASHFS_FIFO_TYPE: + case SQUASHFS_SOCKET_TYPE: + i.data = 0; + i.xattr = SQUASHFS_INVALID_XATTR; + break; + case SQUASHFS_LFIFO_TYPE: + case SQUASHFS_LSOCKET_TYPE: { + struct squashfs_lipc_inode_header *inode = &header.lipc; + + SQUASHFS_SWAP_LIPC_INODE_HEADER(block_ptr, inode); + + i.data = 0; + i.xattr = inode->xattr; + break; + } + default: + EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n", + header.base.inode_type); + } + return &i; +} + + +struct dir *squashfs_opendir_4(unsigned int block_start, unsigned int offset, + struct inode **i) +{ + struct squashfs_dir_header dirh; + char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1] + __attribute__((aligned)); + struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; + long long start; + int bytes; + int dir_count, size; + struct dir_ent *new_dir; + struct dir *dir; + + TRACE("squashfs_opendir: inode start block %d, offset %d\n", + block_start, offset); + + *i = s_ops.read_inode(block_start, offset); + + dir = malloc(sizeof(struct dir)); + if(dir == NULL) + EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); + + dir->dir_count = 0; + dir->cur_entry = 0; + dir->mode = (*i)->mode; + dir->uid = (*i)->uid; + dir->guid = (*i)->gid; + dir->mtime = (*i)->time; + dir->xattr = (*i)->xattr; + dir->dirs = NULL; + + if ((*i)->data == 3) + /* + * if the directory is empty, skip the unnecessary + * lookup_entry, this fixes the corner case with + * completely empty filesystems where lookup_entry correctly + * returning -1 is incorrectly treated as an error + */ + return dir; + + start = sBlk.s.directory_table_start + (*i)->start; + bytes = lookup_entry(directory_table_hash, start); + + if(bytes == -1) + EXIT_UNSQUASH("squashfs_opendir: directory block %d not " + "found!\n", block_start); + + bytes += (*i)->offset; + size = (*i)->data + bytes - 3; + + while(bytes < size) { + SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh); + + dir_count = dirh.count + 1; + TRACE("squashfs_opendir: Read directory header @ byte position " + "%d, %d directory entries\n", bytes, dir_count); + bytes += sizeof(dirh); + + /* dir_count should never be larger than 256 */ + if(dir_count > 256) + goto corrupted; + + while(dir_count--) { + SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire); + + bytes += sizeof(*dire); + + /* size should never be larger than SQUASHFS_NAME_LEN */ + if(dire->size > SQUASHFS_NAME_LEN) + goto corrupted; + + memcpy(dire->name, directory_table + bytes, + dire->size + 1); + dire->name[dire->size + 1] = '\0'; + TRACE("squashfs_opendir: directory entry %s, inode " + "%d:%d, type %d\n", dire->name, + dirh.start_block, dire->offset, dire->type); + if((dir->dir_count % DIR_ENT_SIZE) == 0) { + new_dir = realloc(dir->dirs, (dir->dir_count + + DIR_ENT_SIZE) * sizeof(struct dir_ent)); + if(new_dir == NULL) + EXIT_UNSQUASH("squashfs_opendir: " + "realloc failed!\n"); + dir->dirs = new_dir; + } + strcpy(dir->dirs[dir->dir_count].name, dire->name); + dir->dirs[dir->dir_count].start_block = + dirh.start_block; + dir->dirs[dir->dir_count].offset = dire->offset; + dir->dirs[dir->dir_count].type = dire->type; + dir->dir_count ++; + bytes += dire->size + 1; + } + } + + return dir; + +corrupted: + free(dir->dirs); + free(dir); + return NULL; +} + + +int read_uids_guids_4() +{ + int res, i; + int bytes = SQUASHFS_ID_BYTES(sBlk.s.no_ids); + int indexes = SQUASHFS_ID_BLOCKS(sBlk.s.no_ids); + long long id_index_table[indexes]; + + TRACE("read_uids_guids: no_ids %d\n", sBlk.s.no_ids); + + id_table = malloc(bytes); + if(id_table == NULL) { + ERROR("read_uids_guids: failed to allocate id table\n"); + return FALSE; + } + + res = read_fs_bytes(fd, sBlk.s.id_table_start, + SQUASHFS_ID_BLOCK_BYTES(sBlk.s.no_ids), id_index_table); + if(res == FALSE) { + ERROR("read_uids_guids: failed to read id index table\n"); + return FALSE; + } + SQUASHFS_INSWAP_ID_BLOCKS(id_index_table, indexes); + + for(i = 0; i < indexes; i++) { + int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE : + bytes & (SQUASHFS_METADATA_SIZE - 1); + res = read_block(fd, id_index_table[i], NULL, expected, + ((char *) id_table) + i * SQUASHFS_METADATA_SIZE); + if(res == FALSE) { + ERROR("read_uids_guids: failed to read id table block" + "\n"); + return FALSE; + } + } + + SQUASHFS_INSWAP_INTS(id_table, sBlk.s.no_ids); + + return TRUE; +}
diff --git a/squashfs-tools/squashfs-tools/unsquashfs.c b/squashfs-tools/squashfs-tools/unsquashfs.c new file mode 100644 index 0000000..7f46968 --- /dev/null +++ b/squashfs-tools/squashfs-tools/unsquashfs.c
@@ -0,0 +1,2820 @@ +/* + * Unsquash a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, + * 2012, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquashfs.c + */ + +#include "unsquashfs.h" +#include "squashfs_swap.h" +#include "squashfs_compat.h" +#include "compressor.h" +#include "xattr.h" +#include "unsquashfs_info.h" +#include "stdarg.h" + +#ifndef linux +#include <sys/sysctl.h> +#else +#include <sys/sysinfo.h> +#endif + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <limits.h> +#include <ctype.h> + +struct cache *fragment_cache, *data_cache; +struct queue *to_reader, *to_inflate, *to_writer, *from_writer; +pthread_t *thread, *inflator_thread; +pthread_mutex_t fragment_mutex; + +/* user options that control parallelisation */ +int processors = -1; + +struct super_block sBlk; +squashfs_operations s_ops; +struct compressor *comp; + +int bytes = 0, swap, file_count = 0, dir_count = 0, sym_count = 0, + dev_count = 0, fifo_count = 0; +char *inode_table = NULL, *directory_table = NULL; +struct hash_table_entry *inode_table_hash[65536], *directory_table_hash[65536]; +int fd; +unsigned int *uid_table, *guid_table; +unsigned int cached_frag = SQUASHFS_INVALID_FRAG; +char *fragment_data; +char *file_data; +char *data; +unsigned int block_size; +unsigned int block_log; +int lsonly = FALSE, info = FALSE, force = FALSE, short_ls = TRUE; +int use_regex = FALSE; +char **created_inode; +int root_process; +int columns; +int rotate = 0; +pthread_mutex_t screen_mutex; +int progress = TRUE, progress_enabled = FALSE; +unsigned int total_blocks = 0, total_files = 0, total_inodes = 0; +unsigned int cur_blocks = 0; +int inode_number = 1; +int no_xattrs = XATTR_DEF; +int user_xattrs = FALSE; + +int lookup_type[] = { + 0, + S_IFDIR, + S_IFREG, + S_IFLNK, + S_IFBLK, + S_IFCHR, + S_IFIFO, + S_IFSOCK, + S_IFDIR, + S_IFREG, + S_IFLNK, + S_IFBLK, + S_IFCHR, + S_IFIFO, + S_IFSOCK +}; + +struct test table[] = { + { S_IFMT, S_IFSOCK, 0, 's' }, + { S_IFMT, S_IFLNK, 0, 'l' }, + { S_IFMT, S_IFBLK, 0, 'b' }, + { S_IFMT, S_IFDIR, 0, 'd' }, + { S_IFMT, S_IFCHR, 0, 'c' }, + { S_IFMT, S_IFIFO, 0, 'p' }, + { S_IRUSR, S_IRUSR, 1, 'r' }, + { S_IWUSR, S_IWUSR, 2, 'w' }, + { S_IRGRP, S_IRGRP, 4, 'r' }, + { S_IWGRP, S_IWGRP, 5, 'w' }, + { S_IROTH, S_IROTH, 7, 'r' }, + { S_IWOTH, S_IWOTH, 8, 'w' }, + { S_IXUSR | S_ISUID, S_IXUSR | S_ISUID, 3, 's' }, + { S_IXUSR | S_ISUID, S_ISUID, 3, 'S' }, + { S_IXUSR | S_ISUID, S_IXUSR, 3, 'x' }, + { S_IXGRP | S_ISGID, S_IXGRP | S_ISGID, 6, 's' }, + { S_IXGRP | S_ISGID, S_ISGID, 6, 'S' }, + { S_IXGRP | S_ISGID, S_IXGRP, 6, 'x' }, + { S_IXOTH | S_ISVTX, S_IXOTH | S_ISVTX, 9, 't' }, + { S_IXOTH | S_ISVTX, S_ISVTX, 9, 'T' }, + { S_IXOTH | S_ISVTX, S_IXOTH, 9, 'x' }, + { 0, 0, 0, 0} +}; + +void progress_bar(long long current, long long max, int columns); + +#define MAX_LINE 16384 + +void prep_exit() +{ +} + + +void sigwinch_handler() +{ + struct winsize winsize; + + if(ioctl(1, TIOCGWINSZ, &winsize) == -1) { + if(isatty(STDOUT_FILENO)) + ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 " + "columns\n"); + columns = 80; + } else + columns = winsize.ws_col; +} + + +void sigalrm_handler() +{ + rotate = (rotate + 1) % 4; +} + + +int add_overflow(int a, int b) +{ + return (INT_MAX - a) < b; +} + + +int shift_overflow(int a, int shift) +{ + return (INT_MAX >> shift) < a; +} + + +int multiply_overflow(int a, int multiplier) +{ + return (INT_MAX / multiplier) < a; +} + + +struct queue *queue_init(int size) +{ + struct queue *queue = malloc(sizeof(struct queue)); + + if(queue == NULL) + EXIT_UNSQUASH("Out of memory in queue_init\n"); + + if(add_overflow(size, 1) || + multiply_overflow(size + 1, sizeof(void *))) + EXIT_UNSQUASH("Size too large in queue_init\n"); + + queue->data = malloc(sizeof(void *) * (size + 1)); + if(queue->data == NULL) + EXIT_UNSQUASH("Out of memory in queue_init\n"); + + queue->size = size + 1; + queue->readp = queue->writep = 0; + pthread_mutex_init(&queue->mutex, NULL); + pthread_cond_init(&queue->empty, NULL); + pthread_cond_init(&queue->full, NULL); + + return queue; +} + + +void queue_put(struct queue *queue, void *data) +{ + int nextp; + + pthread_mutex_lock(&queue->mutex); + + while((nextp = (queue->writep + 1) % queue->size) == queue->readp) + pthread_cond_wait(&queue->full, &queue->mutex); + + queue->data[queue->writep] = data; + queue->writep = nextp; + pthread_cond_signal(&queue->empty); + pthread_mutex_unlock(&queue->mutex); +} + + +void *queue_get(struct queue *queue) +{ + void *data; + pthread_mutex_lock(&queue->mutex); + + while(queue->readp == queue->writep) + pthread_cond_wait(&queue->empty, &queue->mutex); + + data = queue->data[queue->readp]; + queue->readp = (queue->readp + 1) % queue->size; + pthread_cond_signal(&queue->full); + pthread_mutex_unlock(&queue->mutex); + + return data; +} + + +void dump_queue(struct queue *queue) +{ + pthread_mutex_lock(&queue->mutex); + + printf("Max size %d, size %d%s\n", queue->size - 1, + queue->readp <= queue->writep ? queue->writep - queue->readp : + queue->size - queue->readp + queue->writep, + queue->readp == queue->writep ? " (EMPTY)" : + ((queue->writep + 1) % queue->size) == queue->readp ? + " (FULL)" : ""); + + pthread_mutex_unlock(&queue->mutex); +} + + +/* Called with the cache mutex held */ +void insert_hash_table(struct cache *cache, struct cache_entry *entry) +{ + int hash = CALCULATE_HASH(entry->block); + + entry->hash_next = cache->hash_table[hash]; + cache->hash_table[hash] = entry; + entry->hash_prev = NULL; + if(entry->hash_next) + entry->hash_next->hash_prev = entry; +} + + +/* Called with the cache mutex held */ +void remove_hash_table(struct cache *cache, struct cache_entry *entry) +{ + if(entry->hash_prev) + entry->hash_prev->hash_next = entry->hash_next; + else + cache->hash_table[CALCULATE_HASH(entry->block)] = + entry->hash_next; + if(entry->hash_next) + entry->hash_next->hash_prev = entry->hash_prev; + + entry->hash_prev = entry->hash_next = NULL; +} + + +/* Called with the cache mutex held */ +void insert_free_list(struct cache *cache, struct cache_entry *entry) +{ + if(cache->free_list) { + entry->free_next = cache->free_list; + entry->free_prev = cache->free_list->free_prev; + cache->free_list->free_prev->free_next = entry; + cache->free_list->free_prev = entry; + } else { + cache->free_list = entry; + entry->free_prev = entry->free_next = entry; + } +} + + +/* Called with the cache mutex held */ +void remove_free_list(struct cache *cache, struct cache_entry *entry) +{ + if(entry->free_prev == NULL || entry->free_next == NULL) + /* not in free list */ + return; + else if(entry->free_prev == entry && entry->free_next == entry) { + /* only this entry in the free list */ + cache->free_list = NULL; + } else { + /* more than one entry in the free list */ + entry->free_next->free_prev = entry->free_prev; + entry->free_prev->free_next = entry->free_next; + if(cache->free_list == entry) + cache->free_list = entry->free_next; + } + + entry->free_prev = entry->free_next = NULL; +} + + +struct cache *cache_init(int buffer_size, int max_buffers) +{ + struct cache *cache = malloc(sizeof(struct cache)); + + if(cache == NULL) + EXIT_UNSQUASH("Out of memory in cache_init\n"); + + cache->max_buffers = max_buffers; + cache->buffer_size = buffer_size; + cache->count = 0; + cache->used = 0; + cache->free_list = NULL; + memset(cache->hash_table, 0, sizeof(struct cache_entry *) * 65536); + cache->wait_free = FALSE; + cache->wait_pending = FALSE; + pthread_mutex_init(&cache->mutex, NULL); + pthread_cond_init(&cache->wait_for_free, NULL); + pthread_cond_init(&cache->wait_for_pending, NULL); + + return cache; +} + + +struct cache_entry *cache_get(struct cache *cache, long long block, int size) +{ + /* + * Get a block out of the cache. If the block isn't in the cache + * it is added and queued to the reader() and inflate() threads for + * reading off disk and decompression. The cache grows until max_blocks + * is reached, once this occurs existing discarded blocks on the free + * list are reused + */ + int hash = CALCULATE_HASH(block); + struct cache_entry *entry; + + pthread_mutex_lock(&cache->mutex); + + for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next) + if(entry->block == block) + break; + + if(entry) { + /* + * found the block in the cache. If the block is currently unused + * remove it from the free list and increment cache used count. + */ + if(entry->used == 0) { + cache->used ++; + remove_free_list(cache, entry); + } + entry->used ++; + pthread_mutex_unlock(&cache->mutex); + } else { + /* + * not in the cache + * + * first try to allocate new block + */ + if(cache->count < cache->max_buffers) { + entry = malloc(sizeof(struct cache_entry)); + if(entry == NULL) + EXIT_UNSQUASH("Out of memory in cache_get\n"); + entry->data = malloc(cache->buffer_size); + if(entry->data == NULL) + EXIT_UNSQUASH("Out of memory in cache_get\n"); + entry->cache = cache; + entry->free_prev = entry->free_next = NULL; + cache->count ++; + } else { + /* + * try to get from free list + */ + while(cache->free_list == NULL) { + cache->wait_free = TRUE; + pthread_cond_wait(&cache->wait_for_free, + &cache->mutex); + } + entry = cache->free_list; + remove_free_list(cache, entry); + remove_hash_table(cache, entry); + } + + /* + * Initialise block and insert into the hash table. + * Increment used which tracks how many buffers in the + * cache are actively in use (the other blocks, count - used, + * are in the cache and available for lookup, but can also be + * re-used). + */ + entry->block = block; + entry->size = size; + entry->used = 1; + entry->error = FALSE; + entry->pending = TRUE; + insert_hash_table(cache, entry); + cache->used ++; + + /* + * queue to read thread to read and ultimately (via the + * decompress threads) decompress the buffer + */ + pthread_mutex_unlock(&cache->mutex); + queue_put(to_reader, entry); + } + + return entry; +} + + +void cache_block_ready(struct cache_entry *entry, int error) +{ + /* + * mark cache entry as being complete, reading and (if necessary) + * decompression has taken place, and the buffer is valid for use. + * If an error occurs reading or decompressing, the buffer also + * becomes ready but with an error... + */ + pthread_mutex_lock(&entry->cache->mutex); + entry->pending = FALSE; + entry->error = error; + + /* + * if the wait_pending flag is set, one or more threads may be waiting + * on this buffer + */ + if(entry->cache->wait_pending) { + entry->cache->wait_pending = FALSE; + pthread_cond_broadcast(&entry->cache->wait_for_pending); + } + + pthread_mutex_unlock(&entry->cache->mutex); +} + + +void cache_block_wait(struct cache_entry *entry) +{ + /* + * wait for this cache entry to become ready, when reading and (if + * necessary) decompression has taken place + */ + pthread_mutex_lock(&entry->cache->mutex); + + while(entry->pending) { + entry->cache->wait_pending = TRUE; + pthread_cond_wait(&entry->cache->wait_for_pending, + &entry->cache->mutex); + } + + pthread_mutex_unlock(&entry->cache->mutex); +} + + +void cache_block_put(struct cache_entry *entry) +{ + /* + * finished with this cache entry, once the usage count reaches zero it + * can be reused and is put onto the free list. As it remains + * accessible via the hash table it can be found getting a new lease of + * life before it is reused. + */ + pthread_mutex_lock(&entry->cache->mutex); + + entry->used --; + if(entry->used == 0) { + insert_free_list(entry->cache, entry); + entry->cache->used --; + + /* + * if the wait_free flag is set, one or more threads may be + * waiting on this buffer + */ + if(entry->cache->wait_free) { + entry->cache->wait_free = FALSE; + pthread_cond_broadcast(&entry->cache->wait_for_free); + } + } + + pthread_mutex_unlock(&entry->cache->mutex); +} + + +void dump_cache(struct cache *cache) +{ + pthread_mutex_lock(&cache->mutex); + + printf("Max buffers %d, Current size %d, Used %d, %s\n", + cache->max_buffers, cache->count, cache->used, + cache->free_list ? "Free buffers" : "No free buffers"); + + pthread_mutex_unlock(&cache->mutex); +} + + +char *modestr(char *str, int mode) +{ + int i; + + strcpy(str, "----------"); + + for(i = 0; table[i].mask != 0; i++) { + if((mode & table[i].mask) == table[i].value) + str[table[i].position] = table[i].mode; + } + + return str; +} + + +#define TOTALCHARS 25 +int print_filename(char *pathname, struct inode *inode) +{ + char str[11], dummy[12], dummy2[12]; /* overflow safe */ + char *userstr, *groupstr; + int padchars; + struct passwd *user; + struct group *group; + struct tm *t; + + if(short_ls) { + printf("%s\n", pathname); + return 1; + } + + user = getpwuid(inode->uid); + if(user == NULL) { + int res = snprintf(dummy, 12, "%d", inode->uid); + if(res < 0) + EXIT_UNSQUASH("snprintf failed in print_filename()\n"); + else if(res >= 12) + /* unsigned int shouldn't ever need more than 11 bytes + * (including terminating '\0') to print in base 10 */ + userstr = "*"; + else + userstr = dummy; + } else + userstr = user->pw_name; + + group = getgrgid(inode->gid); + if(group == NULL) { + int res = snprintf(dummy2, 12, "%d", inode->gid); + if(res < 0) + EXIT_UNSQUASH("snprintf failed in print_filename()\n"); + else if(res >= 12) + /* unsigned int shouldn't ever need more than 11 bytes + * (including terminating '\0') to print in base 10 */ + groupstr = "*"; + else + groupstr = dummy2; + } else + groupstr = group->gr_name; + + printf("%s %s/%s ", modestr(str, inode->mode), userstr, groupstr); + + switch(inode->mode & S_IFMT) { + case S_IFREG: + case S_IFDIR: + case S_IFSOCK: + case S_IFIFO: + case S_IFLNK: + padchars = TOTALCHARS - strlen(userstr) - + strlen(groupstr); + + printf("%*lld ", padchars > 0 ? padchars : 0, + inode->data); + break; + case S_IFCHR: + case S_IFBLK: + padchars = TOTALCHARS - strlen(userstr) - + strlen(groupstr) - 7; + + printf("%*s%3d,%3d ", padchars > 0 ? padchars : 0, " ", + (int) inode->data >> 8, (int) inode->data & + 0xff); + break; + } + + t = localtime(&inode->time); + + printf("%d-%02d-%02d %02d:%02d %s", t->tm_year + 1900, t->tm_mon + 1, + t->tm_mday, t->tm_hour, t->tm_min, pathname); + if((inode->mode & S_IFMT) == S_IFLNK) + printf(" -> %s", inode->symlink); + printf("\n"); + + return 1; +} + + +void add_entry(struct hash_table_entry *hash_table[], long long start, + int bytes) +{ + int hash = CALCULATE_HASH(start); + struct hash_table_entry *hash_table_entry; + + hash_table_entry = malloc(sizeof(struct hash_table_entry)); + if(hash_table_entry == NULL) + EXIT_UNSQUASH("Out of memory in add_entry\n"); + + hash_table_entry->start = start; + hash_table_entry->bytes = bytes; + hash_table_entry->next = hash_table[hash]; + hash_table[hash] = hash_table_entry; +} + + +int lookup_entry(struct hash_table_entry *hash_table[], long long start) +{ + int hash = CALCULATE_HASH(start); + struct hash_table_entry *hash_table_entry; + + for(hash_table_entry = hash_table[hash]; hash_table_entry; + hash_table_entry = hash_table_entry->next) + + if(hash_table_entry->start == start) + return hash_table_entry->bytes; + + return -1; +} + + +int read_fs_bytes(int fd, long long byte, int bytes, void *buff) +{ + off_t off = byte; + int res, count; + + TRACE("read_bytes: reading from position 0x%llx, bytes %d\n", byte, + bytes); + + if(lseek(fd, off, SEEK_SET) == -1) { + ERROR("Lseek failed because %s\n", strerror(errno)); + return FALSE; + } + + for(count = 0; count < bytes; count += res) { + res = read(fd, buff + count, bytes - count); + if(res < 1) { + if(res == 0) { + ERROR("Read on filesystem failed because " + "EOF\n"); + return FALSE; + } else if(errno != EINTR) { + ERROR("Read on filesystem failed because %s\n", + strerror(errno)); + return FALSE; + } else + res = 0; + } + } + + return TRUE; +} + + +int read_block(int fd, long long start, long long *next, int expected, + void *block) +{ + unsigned short c_byte; + int offset = 2, res, compressed; + int outlen = expected ? expected : SQUASHFS_METADATA_SIZE; + + if(swap) { + if(read_fs_bytes(fd, start, 2, &c_byte) == FALSE) + goto failed; + c_byte = (c_byte >> 8) | ((c_byte & 0xff) << 8); + } else + if(read_fs_bytes(fd, start, 2, &c_byte) == FALSE) + goto failed; + + TRACE("read_block: block @0x%llx, %d %s bytes\n", start, + SQUASHFS_COMPRESSED_SIZE(c_byte), SQUASHFS_COMPRESSED(c_byte) ? + "compressed" : "uncompressed"); + + if(SQUASHFS_CHECK_DATA(sBlk.s.flags)) + offset = 3; + + compressed = SQUASHFS_COMPRESSED(c_byte); + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); + + /* + * The block size should not be larger than + * the uncompressed size (or max uncompressed size if + * expected is 0) + */ + if(c_byte > outlen) + return 0; + + if(compressed) { + char buffer[c_byte]; + int error; + + res = read_fs_bytes(fd, start + offset, c_byte, buffer); + if(res == FALSE) + goto failed; + + res = compressor_uncompress(comp, block, buffer, c_byte, + outlen, &error); + + if(res == -1) { + ERROR("%s uncompress failed with error code %d\n", + comp->name, error); + goto failed; + } + } else { + res = read_fs_bytes(fd, start + offset, c_byte, block); + if(res == FALSE) + goto failed; + res = c_byte; + } + + if(next) + *next = start + offset + c_byte; + + /* + * if expected, then check the (uncompressed) return data + * is of the expected size + */ + if(expected && expected != res) + return 0; + else + return res; + +failed: + ERROR("read_block: failed to read block @0x%llx\n", start); + return FALSE; +} + + +int read_data_block(long long start, unsigned int size, char *block) +{ + int error, res; + int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(size); + + TRACE("read_data_block: block @0x%llx, %d %s bytes\n", start, + c_byte, SQUASHFS_COMPRESSED_BLOCK(size) ? "compressed" : + "uncompressed"); + + if(SQUASHFS_COMPRESSED_BLOCK(size)) { + if(read_fs_bytes(fd, start, c_byte, data) == FALSE) + goto failed; + + res = compressor_uncompress(comp, block, data, c_byte, + block_size, &error); + + if(res == -1) { + ERROR("%s uncompress failed with error code %d\n", + comp->name, error); + goto failed; + } + + return res; + } else { + if(read_fs_bytes(fd, start, c_byte, block) == FALSE) + goto failed; + + return c_byte; + } + +failed: + ERROR("read_data_block: failed to read block @0x%llx, size %d\n", start, + c_byte); + return FALSE; +} + + +int read_inode_table(long long start, long long end) +{ + int size = 0, bytes = 0, res; + + TRACE("read_inode_table: start %lld, end %lld\n", start, end); + + while(start < end) { + if(size - bytes < SQUASHFS_METADATA_SIZE) { + inode_table = realloc(inode_table, size += + SQUASHFS_METADATA_SIZE); + if(inode_table == NULL) { + ERROR("Out of memory in read_inode_table"); + goto failed; + } + } + + add_entry(inode_table_hash, start, bytes); + + res = read_block(fd, start, &start, 0, inode_table + bytes); + if(res == 0) { + ERROR("read_inode_table: failed to read block\n"); + goto failed; + } + bytes += res; + + /* + * If this is not the last metadata block in the inode table + * then it should be SQUASHFS_METADATA_SIZE in size. + * Note, we can't use expected in read_block() above for this + * because we don't know if this is the last block until + * after reading. + */ + if(start != end && res != SQUASHFS_METADATA_SIZE) { + ERROR("read_inode_table: metadata block should be %d " + "bytes in length, it is %d bytes\n", + SQUASHFS_METADATA_SIZE, res); + + goto failed; + } + } + + return TRUE; + +failed: + free(inode_table); + return FALSE; +} + + +int set_attributes(char *pathname, int mode, uid_t uid, gid_t guid, time_t time, + unsigned int xattr, unsigned int set_mode) +{ + struct utimbuf times = { time, time }; + + write_xattr(pathname, xattr); + + if(utime(pathname, ×) == -1) { + ERROR("set_attributes: failed to set time on %s, because %s\n", + pathname, strerror(errno)); + return FALSE; + } + + if(root_process) { + if(chown(pathname, uid, guid) == -1) { + ERROR("set_attributes: failed to change uid and gids " + "on %s, because %s\n", pathname, + strerror(errno)); + return FALSE; + } + } else + mode &= ~07000; + + if((set_mode || (mode & 07000)) && chmod(pathname, (mode_t) mode) == -1) { + ERROR("set_attributes: failed to change mode %s, because %s\n", + pathname, strerror(errno)); + return FALSE; + } + + return TRUE; +} + + +int write_bytes(int fd, char *buff, int bytes) +{ + int res, count; + + for(count = 0; count < bytes; count += res) { + res = write(fd, buff + count, bytes - count); + if(res == -1) { + if(errno != EINTR) { + ERROR("Write on output file failed because " + "%s\n", strerror(errno)); + return -1; + } + res = 0; + } + } + + return 0; +} + + +int lseek_broken = FALSE; +char *zero_data = NULL; + +int write_block(int file_fd, char *buffer, int size, long long hole, int sparse) +{ + off_t off = hole; + + if(hole) { + if(sparse && lseek_broken == FALSE) { + int error = lseek(file_fd, off, SEEK_CUR); + if(error == -1) + /* failed to seek beyond end of file */ + lseek_broken = TRUE; + } + + if((sparse == FALSE || lseek_broken) && zero_data == NULL) { + if((zero_data = malloc(block_size)) == NULL) + EXIT_UNSQUASH("write_block: failed to alloc " + "zero data block\n"); + memset(zero_data, 0, block_size); + } + + if(sparse == FALSE || lseek_broken) { + int blocks = (hole + block_size -1) / block_size; + int avail_bytes, i; + for(i = 0; i < blocks; i++, hole -= avail_bytes) { + avail_bytes = hole > block_size ? block_size : + hole; + if(write_bytes(file_fd, zero_data, avail_bytes) + == -1) + goto failure; + } + } + } + + if(write_bytes(file_fd, buffer, size) == -1) + goto failure; + + return TRUE; + +failure: + return FALSE; +} + + +pthread_mutex_t open_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t open_empty = PTHREAD_COND_INITIALIZER; +int open_unlimited, open_count; +#define OPEN_FILE_MARGIN 10 + + +void open_init(int count) +{ + open_count = count; + open_unlimited = count == -1; +} + + +int open_wait(char *pathname, int flags, mode_t mode) +{ + if (!open_unlimited) { + pthread_mutex_lock(&open_mutex); + while (open_count == 0) + pthread_cond_wait(&open_empty, &open_mutex); + open_count --; + pthread_mutex_unlock(&open_mutex); + } + + return open(pathname, flags, mode); +} + + +void close_wake(int fd) +{ + close(fd); + + if (!open_unlimited) { + pthread_mutex_lock(&open_mutex); + open_count ++; + pthread_cond_signal(&open_empty); + pthread_mutex_unlock(&open_mutex); + } +} + + +void queue_file(char *pathname, int file_fd, struct inode *inode) +{ + struct squashfs_file *file = malloc(sizeof(struct squashfs_file)); + if(file == NULL) + EXIT_UNSQUASH("queue_file: unable to malloc file\n"); + + file->fd = file_fd; + file->file_size = inode->data; + file->mode = inode->mode; + file->gid = inode->gid; + file->uid = inode->uid; + file->time = inode->time; + file->pathname = strdup(pathname); + file->blocks = inode->blocks + (inode->frag_bytes > 0); + file->sparse = inode->sparse; + file->xattr = inode->xattr; + queue_put(to_writer, file); +} + + +void queue_dir(char *pathname, struct dir *dir) +{ + struct squashfs_file *file = malloc(sizeof(struct squashfs_file)); + if(file == NULL) + EXIT_UNSQUASH("queue_dir: unable to malloc file\n"); + + file->fd = -1; + file->mode = dir->mode; + file->gid = dir->guid; + file->uid = dir->uid; + file->time = dir->mtime; + file->pathname = strdup(pathname); + file->xattr = dir->xattr; + queue_put(to_writer, file); +} + + +int write_file(struct inode *inode, char *pathname) +{ + unsigned int file_fd, i; + unsigned int *block_list; + int file_end = inode->data / block_size; + long long start = inode->start; + + TRACE("write_file: regular file, blocks %d\n", inode->blocks); + + file_fd = open_wait(pathname, O_CREAT | O_WRONLY | + (force ? O_TRUNC : 0), (mode_t) inode->mode & 0777); + if(file_fd == -1) { + ERROR("write_file: failed to create file %s, because %s\n", + pathname, strerror(errno)); + return FALSE; + } + + block_list = malloc(inode->blocks * sizeof(unsigned int)); + if(block_list == NULL) + EXIT_UNSQUASH("write_file: unable to malloc block list\n"); + + s_ops.read_block_list(block_list, inode->block_ptr, inode->blocks); + + /* + * the writer thread is queued a squashfs_file structure describing the + * file. If the file has one or more blocks or a fragment they are + * queued separately (references to blocks in the cache). + */ + queue_file(pathname, file_fd, inode); + + for(i = 0; i < inode->blocks; i++) { + int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]); + struct file_entry *block = malloc(sizeof(struct file_entry)); + + if(block == NULL) + EXIT_UNSQUASH("write_file: unable to malloc file\n"); + block->offset = 0; + block->size = i == file_end ? inode->data & (block_size - 1) : + block_size; + if(block_list[i] == 0) /* sparse block */ + block->buffer = NULL; + else { + block->buffer = cache_get(data_cache, start, + block_list[i]); + start += c_byte; + } + queue_put(to_writer, block); + } + + if(inode->frag_bytes) { + int size; + long long start; + struct file_entry *block = malloc(sizeof(struct file_entry)); + + if(block == NULL) + EXIT_UNSQUASH("write_file: unable to malloc file\n"); + s_ops.read_fragment(inode->fragment, &start, &size); + block->buffer = cache_get(fragment_cache, start, size); + block->offset = inode->offset; + block->size = inode->frag_bytes; + queue_put(to_writer, block); + } + + free(block_list); + return TRUE; +} + + +int create_inode(char *pathname, struct inode *i) +{ + TRACE("create_inode: pathname %s\n", pathname); + + if(created_inode[i->inode_number - 1]) { + TRACE("create_inode: hard link\n"); + if(force) + unlink(pathname); + + if(link(created_inode[i->inode_number - 1], pathname) == -1) { + ERROR("create_inode: failed to create hardlink, " + "because %s\n", strerror(errno)); + return FALSE; + } + + return TRUE; + } + + switch(i->type) { + case SQUASHFS_FILE_TYPE: + case SQUASHFS_LREG_TYPE: + TRACE("create_inode: regular file, file_size %lld, " + "blocks %d\n", i->data, i->blocks); + + if(write_file(i, pathname)) + file_count ++; + break; + case SQUASHFS_SYMLINK_TYPE: + case SQUASHFS_LSYMLINK_TYPE: + TRACE("create_inode: symlink, symlink_size %lld\n", + i->data); + + if(force) + unlink(pathname); + + if(symlink(i->symlink, pathname) == -1) { + ERROR("create_inode: failed to create symlink " + "%s, because %s\n", pathname, + strerror(errno)); + break; + } + + write_xattr(pathname, i->xattr); + + if(root_process) { + if(lchown(pathname, i->uid, i->gid) == -1) + ERROR("create_inode: failed to change " + "uid and gids on %s, because " + "%s\n", pathname, + strerror(errno)); + } + + sym_count ++; + break; + case SQUASHFS_BLKDEV_TYPE: + case SQUASHFS_CHRDEV_TYPE: + case SQUASHFS_LBLKDEV_TYPE: + case SQUASHFS_LCHRDEV_TYPE: { + int chrdev = i->type == SQUASHFS_CHRDEV_TYPE; + TRACE("create_inode: dev, rdev 0x%llx\n", i->data); + + if(root_process) { + if(force) + unlink(pathname); + + if(mknod(pathname, chrdev ? S_IFCHR : S_IFBLK, + makedev((i->data >> 8) & 0xff, + i->data & 0xff)) == -1) { + ERROR("create_inode: failed to create " + "%s device %s, because %s\n", + chrdev ? "character" : "block", + pathname, strerror(errno)); + break; + } + set_attributes(pathname, i->mode, i->uid, + i->gid, i->time, i->xattr, TRUE); + dev_count ++; + } else + ERROR("create_inode: could not create %s " + "device %s, because you're not " + "superuser!\n", chrdev ? "character" : + "block", pathname); + break; + } + case SQUASHFS_FIFO_TYPE: + case SQUASHFS_LFIFO_TYPE: + TRACE("create_inode: fifo\n"); + + if(force) + unlink(pathname); + + if(mknod(pathname, S_IFIFO, 0) == -1) { + ERROR("create_inode: failed to create fifo %s, " + "because %s\n", pathname, + strerror(errno)); + break; + } + set_attributes(pathname, i->mode, i->uid, i->gid, + i->time, i->xattr, TRUE); + fifo_count ++; + break; + case SQUASHFS_SOCKET_TYPE: + case SQUASHFS_LSOCKET_TYPE: + TRACE("create_inode: socket\n"); + ERROR("create_inode: socket %s ignored\n", pathname); + break; + default: + ERROR("Unknown inode type %d in create_inode_table!\n", + i->type); + return FALSE; + } + + created_inode[i->inode_number - 1] = strdup(pathname); + + return TRUE; +} + + +int read_directory_table(long long start, long long end) +{ + int bytes = 0, size = 0, res; + + TRACE("read_directory_table: start %lld, end %lld\n", start, end); + + while(start < end) { + if(size - bytes < SQUASHFS_METADATA_SIZE) { + directory_table = realloc(directory_table, size += + SQUASHFS_METADATA_SIZE); + if(directory_table == NULL) { + ERROR("Out of memory in " + "read_directory_table\n"); + goto failed; + } + } + + add_entry(directory_table_hash, start, bytes); + + res = read_block(fd, start, &start, 0, directory_table + bytes); + if(res == 0) { + ERROR("read_directory_table: failed to read block\n"); + goto failed; + } + + bytes += res; + + /* + * If this is not the last metadata block in the directory table + * then it should be SQUASHFS_METADATA_SIZE in size. + * Note, we can't use expected in read_block() above for this + * because we don't know if this is the last block until + * after reading. + */ + if(start != end && res != SQUASHFS_METADATA_SIZE) { + ERROR("read_directory_table: metadata block " + "should be %d bytes in length, it is %d " + "bytes\n", SQUASHFS_METADATA_SIZE, res); + goto failed; + } + } + + return TRUE; + +failed: + free(directory_table); + return FALSE; +} + + +int squashfs_readdir(struct dir *dir, char **name, unsigned int *start_block, +unsigned int *offset, unsigned int *type) +{ + if(dir->cur_entry == dir->dir_count) + return FALSE; + + *name = dir->dirs[dir->cur_entry].name; + *start_block = dir->dirs[dir->cur_entry].start_block; + *offset = dir->dirs[dir->cur_entry].offset; + *type = dir->dirs[dir->cur_entry].type; + dir->cur_entry ++; + + return TRUE; +} + + +void squashfs_closedir(struct dir *dir) +{ + free(dir->dirs); + free(dir); +} + + +char *get_component(char *target, char **targname) +{ + char *start; + + while(*target == '/') + target ++; + + start = target; + while(*target != '/' && *target != '\0') + target ++; + + *targname = strndup(start, target - start); + + while(*target == '/') + target ++; + + return target; +} + + +void free_path(struct pathname *paths) +{ + int i; + + for(i = 0; i < paths->names; i++) { + if(paths->name[i].paths) + free_path(paths->name[i].paths); + free(paths->name[i].name); + if(paths->name[i].preg) { + regfree(paths->name[i].preg); + free(paths->name[i].preg); + } + } + + free(paths); +} + + +struct pathname *add_path(struct pathname *paths, char *target, char *alltarget) +{ + char *targname; + int i, error; + + TRACE("add_path: adding \"%s\" extract file\n", target); + + target = get_component(target, &targname); + + if(paths == NULL) { + paths = malloc(sizeof(struct pathname)); + if(paths == NULL) + EXIT_UNSQUASH("failed to allocate paths\n"); + + paths->names = 0; + paths->name = NULL; + } + + for(i = 0; i < paths->names; i++) + if(strcmp(paths->name[i].name, targname) == 0) + break; + + if(i == paths->names) { + /* + * allocate new name entry + */ + paths->names ++; + paths->name = realloc(paths->name, (i + 1) * + sizeof(struct path_entry)); + if(paths->name == NULL) + EXIT_UNSQUASH("Out of memory in add_path\n"); + paths->name[i].name = targname; + paths->name[i].paths = NULL; + if(use_regex) { + paths->name[i].preg = malloc(sizeof(regex_t)); + if(paths->name[i].preg == NULL) + EXIT_UNSQUASH("Out of memory in add_path\n"); + error = regcomp(paths->name[i].preg, targname, + REG_EXTENDED|REG_NOSUB); + if(error) { + char str[1024]; /* overflow safe */ + + regerror(error, paths->name[i].preg, str, 1024); + EXIT_UNSQUASH("invalid regex %s in export %s, " + "because %s\n", targname, alltarget, + str); + } + } else + paths->name[i].preg = NULL; + + if(target[0] == '\0') + /* + * at leaf pathname component + */ + paths->name[i].paths = NULL; + else + /* + * recurse adding child components + */ + paths->name[i].paths = add_path(NULL, target, alltarget); + } else { + /* + * existing matching entry + */ + free(targname); + + if(paths->name[i].paths == NULL) { + /* + * No sub-directory which means this is the leaf + * component of a pre-existing extract which subsumes + * the extract currently being added, in which case stop + * adding components + */ + } else if(target[0] == '\0') { + /* + * at leaf pathname component and child components exist + * from more specific extracts, delete as they're + * subsumed by this extract + */ + free_path(paths->name[i].paths); + paths->name[i].paths = NULL; + } else + /* + * recurse adding child components + */ + add_path(paths->name[i].paths, target, alltarget); + } + + return paths; +} + + +struct pathnames *init_subdir() +{ + struct pathnames *new = malloc(sizeof(struct pathnames)); + if(new == NULL) + EXIT_UNSQUASH("Out of memory in init_subdir\n"); + new->count = 0; + return new; +} + + +struct pathnames *add_subdir(struct pathnames *paths, struct pathname *path) +{ + if(paths->count % PATHS_ALLOC_SIZE == 0) { + paths = realloc(paths, sizeof(struct pathnames *) + + (paths->count + PATHS_ALLOC_SIZE) * + sizeof(struct pathname *)); + if(paths == NULL) + EXIT_UNSQUASH("Out of memory in add_subdir\n"); + } + + paths->path[paths->count++] = path; + return paths; +} + + +void free_subdir(struct pathnames *paths) +{ + free(paths); +} + + +int matches(struct pathnames *paths, char *name, struct pathnames **new) +{ + int i, n; + + if(paths == NULL) { + *new = NULL; + return TRUE; + } + + *new = init_subdir(); + + for(n = 0; n < paths->count; n++) { + struct pathname *path = paths->path[n]; + for(i = 0; i < path->names; i++) { + int match = use_regex ? + regexec(path->name[i].preg, name, (size_t) 0, + NULL, 0) == 0 : fnmatch(path->name[i].name, + name, FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == + 0; + if(match && path->name[i].paths == NULL) + /* + * match on a leaf component, any subdirectories + * will implicitly match, therefore return an + * empty new search set + */ + goto empty_set; + + if(match) + /* + * match on a non-leaf component, add any + * subdirectories to the new set of + * subdirectories to scan for this name + */ + *new = add_subdir(*new, path->name[i].paths); + } + } + + if((*new)->count == 0) { + /* + * no matching names found, delete empty search set, and return + * FALSE + */ + free_subdir(*new); + *new = NULL; + return FALSE; + } + + /* + * one or more matches with sub-directories found (no leaf matches), + * return new search set and return TRUE + */ + return TRUE; + +empty_set: + /* + * found matching leaf exclude, return empty search set and return TRUE + */ + free_subdir(*new); + *new = NULL; + return TRUE; +} + + +void pre_scan(char *parent_name, unsigned int start_block, unsigned int offset, + struct pathnames *paths) +{ + unsigned int type; + char *name; + struct pathnames *new; + struct inode *i; + struct dir *dir = s_ops.squashfs_opendir(start_block, offset, &i); + + if(dir == NULL) + return; + + while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) { + struct inode *i; + char *pathname; + int res; + + TRACE("pre_scan: name %s, start_block %d, offset %d, type %d\n", + name, start_block, offset, type); + + if(!matches(paths, name, &new)) + continue; + + res = asprintf(&pathname, "%s/%s", parent_name, name); + if(res == -1) + EXIT_UNSQUASH("asprintf failed in dir_scan\n"); + + if(type == SQUASHFS_DIR_TYPE) + pre_scan(parent_name, start_block, offset, new); + else if(new == NULL) { + if(type == SQUASHFS_FILE_TYPE || + type == SQUASHFS_LREG_TYPE) { + i = s_ops.read_inode(start_block, offset); + if(created_inode[i->inode_number - 1] == NULL) { + created_inode[i->inode_number - 1] = + (char *) i; + total_blocks += (i->data + + (block_size - 1)) >> block_log; + } + total_files ++; + } + total_inodes ++; + } + + free_subdir(new); + free(pathname); + } + + squashfs_closedir(dir); +} + + +void dir_scan(char *parent_name, unsigned int start_block, unsigned int offset, + struct pathnames *paths) +{ + unsigned int type; + char *name; + struct pathnames *new; + struct inode *i; + struct dir *dir = s_ops.squashfs_opendir(start_block, offset, &i); + + if(dir == NULL) { + ERROR("dir_scan: failed to read directory %s, skipping\n", + parent_name); + return; + } + + if(lsonly || info) + print_filename(parent_name, i); + + if(!lsonly) { + /* + * Make directory with default User rwx permissions rather than + * the permissions from the filesystem, as these may not have + * write/execute permission. These are fixed up later in + * set_attributes(). + */ + int res = mkdir(parent_name, S_IRUSR|S_IWUSR|S_IXUSR); + if(res == -1) { + /* + * Skip directory if mkdir fails, unless we're + * forcing and the error is -EEXIST + */ + if(!force || errno != EEXIST) { + ERROR("dir_scan: failed to make directory %s, " + "because %s\n", parent_name, + strerror(errno)); + squashfs_closedir(dir); + return; + } + + /* + * Try to change permissions of existing directory so + * that we can write to it + */ + res = chmod(parent_name, S_IRUSR|S_IWUSR|S_IXUSR); + if (res == -1) + ERROR("dir_scan: failed to change permissions " + "for directory %s, because %s\n", + parent_name, strerror(errno)); + } + } + + while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) { + char *pathname; + int res; + + TRACE("dir_scan: name %s, start_block %d, offset %d, type %d\n", + name, start_block, offset, type); + + + if(!matches(paths, name, &new)) + continue; + + res = asprintf(&pathname, "%s/%s", parent_name, name); + if(res == -1) + EXIT_UNSQUASH("asprintf failed in dir_scan\n"); + + if(type == SQUASHFS_DIR_TYPE) { + dir_scan(pathname, start_block, offset, new); + free(pathname); + } else if(new == NULL) { + update_info(pathname); + + i = s_ops.read_inode(start_block, offset); + + if(lsonly || info) + print_filename(pathname, i); + + if(!lsonly) + create_inode(pathname, i); + + if(i->type == SQUASHFS_SYMLINK_TYPE || + i->type == SQUASHFS_LSYMLINK_TYPE) + free(i->symlink); + } else + free(pathname); + + free_subdir(new); + } + + if(!lsonly) + queue_dir(parent_name, dir); + + squashfs_closedir(dir); + dir_count ++; +} + + +void squashfs_stat(char *source) +{ + time_t mkfs_time = (time_t) sBlk.s.mkfs_time; + char *mkfs_str = ctime(&mkfs_time); + +#if __BYTE_ORDER == __BIG_ENDIAN + printf("Found a valid %sSQUASHFS %d:%d superblock on %s.\n", + sBlk.s.s_major == 4 ? "" : swap ? "little endian " : + "big endian ", sBlk.s.s_major, sBlk.s.s_minor, source); +#else + printf("Found a valid %sSQUASHFS %d:%d superblock on %s.\n", + sBlk.s.s_major == 4 ? "" : swap ? "big endian " : + "little endian ", sBlk.s.s_major, sBlk.s.s_minor, source); +#endif + + printf("Creation or last append time %s", mkfs_str ? mkfs_str : + "failed to get time\n"); + printf("Filesystem size %.2f Kbytes (%.2f Mbytes)\n", + sBlk.s.bytes_used / 1024.0, sBlk.s.bytes_used / + (1024.0 * 1024.0)); + + if(sBlk.s.s_major == 4) { + printf("Compression %s\n", comp->name); + + if(SQUASHFS_COMP_OPTS(sBlk.s.flags)) { + char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned)); + int bytes; + + bytes = read_block(fd, sizeof(sBlk.s), NULL, 0, buffer); + if(bytes == 0) { + ERROR("Failed to read compressor options\n"); + return; + } + + compressor_display_options(comp, buffer, bytes); + } + } + + printf("Block size %d\n", sBlk.s.block_size); + printf("Filesystem is %sexportable via NFS\n", + SQUASHFS_EXPORTABLE(sBlk.s.flags) ? "" : "not "); + printf("Inodes are %scompressed\n", + SQUASHFS_UNCOMPRESSED_INODES(sBlk.s.flags) ? "un" : ""); + printf("Data is %scompressed\n", + SQUASHFS_UNCOMPRESSED_DATA(sBlk.s.flags) ? "un" : ""); + + if(sBlk.s.s_major > 1) { + if(SQUASHFS_NO_FRAGMENTS(sBlk.s.flags)) + printf("Fragments are not stored\n"); + else { + printf("Fragments are %scompressed\n", + SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk.s.flags) ? + "un" : ""); + printf("Always-use-fragments option is %sspecified\n", + SQUASHFS_ALWAYS_FRAGMENTS(sBlk.s.flags) ? "" : + "not "); + } + } + + if(sBlk.s.s_major == 4) { + if(SQUASHFS_NO_XATTRS(sBlk.s.flags)) + printf("Xattrs are not stored\n"); + else + printf("Xattrs are %scompressed\n", + SQUASHFS_UNCOMPRESSED_XATTRS(sBlk.s.flags) ? + "un" : ""); + } + + if(sBlk.s.s_major < 4) + printf("Check data is %spresent in the filesystem\n", + SQUASHFS_CHECK_DATA(sBlk.s.flags) ? "" : + "not "); + + if(sBlk.s.s_major > 1) + printf("Duplicates are %sremoved\n", + SQUASHFS_DUPLICATES(sBlk.s.flags) ? "" : "not "); + else + printf("Duplicates are removed\n"); + + if(sBlk.s.s_major > 1) + printf("Number of fragments %d\n", sBlk.s.fragments); + + printf("Number of inodes %d\n", sBlk.s.inodes); + + if(sBlk.s.s_major == 4) + printf("Number of ids %d\n", sBlk.s.no_ids); + else { + printf("Number of uids %d\n", sBlk.no_uids); + printf("Number of gids %d\n", sBlk.no_guids); + } + + TRACE("sBlk.s.inode_table_start 0x%llx\n", sBlk.s.inode_table_start); + TRACE("sBlk.s.directory_table_start 0x%llx\n", + sBlk.s.directory_table_start); + + if(sBlk.s.s_major > 1) + TRACE("sBlk.s.fragment_table_start 0x%llx\n\n", + sBlk.s.fragment_table_start); + + if(sBlk.s.s_major > 2) + TRACE("sBlk.s.lookup_table_start 0x%llx\n\n", + sBlk.s.lookup_table_start); + + if(sBlk.s.s_major == 4) { + TRACE("sBlk.s.id_table_start 0x%llx\n", sBlk.s.id_table_start); + TRACE("sBlk.s.xattr_id_table_start 0x%llx\n", + sBlk.s.xattr_id_table_start); + } else { + TRACE("sBlk.uid_start 0x%llx\n", sBlk.uid_start); + TRACE("sBlk.guid_start 0x%llx\n", sBlk.guid_start); + } +} + + +int check_compression(struct compressor *comp) +{ + int res, bytes = 0; + char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned)); + + if(!comp->supported) { + ERROR("Filesystem uses %s compression, this is " + "unsupported by this version\n", comp->name); + ERROR("Decompressors available:\n"); + display_compressors("", ""); + return 0; + } + + /* + * Read compression options from disk if present, and pass to + * the compressor to ensure we know how to decompress a filesystem + * compressed with these compression options. + * + * Note, even if there is no compression options we still call the + * compressor because some compression options may be mandatory + * for some compressors. + */ + if(SQUASHFS_COMP_OPTS(sBlk.s.flags)) { + bytes = read_block(fd, sizeof(sBlk.s), NULL, 0, buffer); + if(bytes == 0) { + ERROR("Failed to read compressor options\n"); + return 0; + } + } + + res = compressor_check_options(comp, sBlk.s.block_size, buffer, bytes); + + return res != -1; +} + + +int read_super(char *source) +{ + squashfs_super_block_3 sBlk_3; + struct squashfs_super_block sBlk_4; + + /* + * Try to read a Squashfs 4 superblock + */ + read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block), + &sBlk_4); + swap = sBlk_4.s_magic != SQUASHFS_MAGIC; + SQUASHFS_INSWAP_SUPER_BLOCK(&sBlk_4); + + if(sBlk_4.s_magic == SQUASHFS_MAGIC && sBlk_4.s_major == 4 && + sBlk_4.s_minor == 0) { + s_ops.squashfs_opendir = squashfs_opendir_4; + s_ops.read_fragment = read_fragment_4; + s_ops.read_fragment_table = read_fragment_table_4; + s_ops.read_block_list = read_block_list_2; + s_ops.read_inode = read_inode_4; + s_ops.read_uids_guids = read_uids_guids_4; + memcpy(&sBlk, &sBlk_4, sizeof(sBlk_4)); + + /* + * Check the compression type + */ + comp = lookup_compressor_id(sBlk.s.compression); + return TRUE; + } + + /* + * Not a Squashfs 4 superblock, try to read a squashfs 3 superblock + * (compatible with 1 and 2 filesystems) + */ + read_fs_bytes(fd, SQUASHFS_START, sizeof(squashfs_super_block_3), + &sBlk_3); + + /* + * Check it is a SQUASHFS superblock + */ + swap = 0; + if(sBlk_3.s_magic != SQUASHFS_MAGIC) { + if(sBlk_3.s_magic == SQUASHFS_MAGIC_SWAP) { + squashfs_super_block_3 sblk; + ERROR("Reading a different endian SQUASHFS filesystem " + "on %s\n", source); + SQUASHFS_SWAP_SUPER_BLOCK_3(&sblk, &sBlk_3); + memcpy(&sBlk_3, &sblk, sizeof(squashfs_super_block_3)); + swap = 1; + } else { + ERROR("Can't find a SQUASHFS superblock on %s\n", + source); + goto failed_mount; + } + } + + sBlk.s.s_magic = sBlk_3.s_magic; + sBlk.s.inodes = sBlk_3.inodes; + sBlk.s.mkfs_time = sBlk_3.mkfs_time; + sBlk.s.block_size = sBlk_3.block_size; + sBlk.s.fragments = sBlk_3.fragments; + sBlk.s.block_log = sBlk_3.block_log; + sBlk.s.flags = sBlk_3.flags; + sBlk.s.s_major = sBlk_3.s_major; + sBlk.s.s_minor = sBlk_3.s_minor; + sBlk.s.root_inode = sBlk_3.root_inode; + sBlk.s.bytes_used = sBlk_3.bytes_used; + sBlk.s.inode_table_start = sBlk_3.inode_table_start; + sBlk.s.directory_table_start = sBlk_3.directory_table_start; + sBlk.s.fragment_table_start = sBlk_3.fragment_table_start; + sBlk.s.lookup_table_start = sBlk_3.lookup_table_start; + sBlk.no_uids = sBlk_3.no_uids; + sBlk.no_guids = sBlk_3.no_guids; + sBlk.uid_start = sBlk_3.uid_start; + sBlk.guid_start = sBlk_3.guid_start; + sBlk.s.xattr_id_table_start = SQUASHFS_INVALID_BLK; + + /* Check the MAJOR & MINOR versions */ + if(sBlk.s.s_major == 1 || sBlk.s.s_major == 2) { + sBlk.s.bytes_used = sBlk_3.bytes_used_2; + sBlk.uid_start = sBlk_3.uid_start_2; + sBlk.guid_start = sBlk_3.guid_start_2; + sBlk.s.inode_table_start = sBlk_3.inode_table_start_2; + sBlk.s.directory_table_start = sBlk_3.directory_table_start_2; + + if(sBlk.s.s_major == 1) { + sBlk.s.block_size = sBlk_3.block_size_1; + sBlk.s.fragment_table_start = sBlk.uid_start; + s_ops.squashfs_opendir = squashfs_opendir_1; + s_ops.read_fragment_table = read_fragment_table_1; + s_ops.read_block_list = read_block_list_1; + s_ops.read_inode = read_inode_1; + s_ops.read_uids_guids = read_uids_guids_1; + } else { + sBlk.s.fragment_table_start = + sBlk_3.fragment_table_start_2; + s_ops.squashfs_opendir = squashfs_opendir_1; + s_ops.read_fragment = read_fragment_2; + s_ops.read_fragment_table = read_fragment_table_2; + s_ops.read_block_list = read_block_list_2; + s_ops.read_inode = read_inode_2; + s_ops.read_uids_guids = read_uids_guids_1; + } + } else if(sBlk.s.s_major == 3) { + s_ops.squashfs_opendir = squashfs_opendir_3; + s_ops.read_fragment = read_fragment_3; + s_ops.read_fragment_table = read_fragment_table_3; + s_ops.read_block_list = read_block_list_2; + s_ops.read_inode = read_inode_3; + s_ops.read_uids_guids = read_uids_guids_1; + } else { + ERROR("Filesystem on %s is (%d:%d), ", source, sBlk.s.s_major, + sBlk.s.s_minor); + ERROR("which is a later filesystem version than I support!\n"); + goto failed_mount; + } + + /* + * 1.x, 2.x and 3.x filesystems use gzip compression. + */ + comp = lookup_compressor("gzip"); + return TRUE; + +failed_mount: + return FALSE; +} + + +struct pathname *process_extract_files(struct pathname *path, char *filename) +{ + FILE *fd; + char buffer[MAX_LINE + 1]; /* overflow safe */ + char *name; + + fd = fopen(filename, "r"); + if(fd == NULL) + EXIT_UNSQUASH("Failed to open extract file \"%s\" because %s\n", + filename, strerror(errno)); + + while(fgets(name = buffer, MAX_LINE + 1, fd) != NULL) { + int len = strlen(name); + + if(len == MAX_LINE && name[len - 1] != '\n') + /* line too large */ + EXIT_UNSQUASH("Line too long when reading " + "extract file \"%s\", larger than %d " + "bytes\n", filename, MAX_LINE); + + /* + * Remove '\n' terminator if it exists (the last line + * in the file may not be '\n' terminated) + */ + if(len && name[len - 1] == '\n') + name[len - 1] = '\0'; + + /* Skip any leading whitespace */ + while(isspace(*name)) + name ++; + + /* if comment line, skip */ + if(*name == '#') + continue; + + /* check for initial backslash, to accommodate + * filenames with leading space or leading # character + */ + if(*name == '\\') + name ++; + + /* if line is now empty after skipping characters, skip it */ + if(*name == '\0') + continue; + + path = add_path(path, name, name); + } + + if(ferror(fd)) + EXIT_UNSQUASH("Reading extract file \"%s\" failed because %s\n", + filename, strerror(errno)); + + fclose(fd); + return path; +} + + +/* + * reader thread. This thread processes read requests queued by the + * cache_get() routine. + */ +void *reader(void *arg) +{ + while(1) { + struct cache_entry *entry = queue_get(to_reader); + int res = read_fs_bytes(fd, entry->block, + SQUASHFS_COMPRESSED_SIZE_BLOCK(entry->size), + entry->data); + + if(res && SQUASHFS_COMPRESSED_BLOCK(entry->size)) + /* + * queue successfully read block to the inflate + * thread(s) for further processing + */ + queue_put(to_inflate, entry); + else + /* + * block has either been successfully read and is + * uncompressed, or an error has occurred, clear pending + * flag, set error appropriately, and wake up any + * threads waiting on this buffer + */ + cache_block_ready(entry, !res); + } +} + + +/* + * writer thread. This processes file write requests queued by the + * write_file() routine. + */ +void *writer(void *arg) +{ + int i; + + while(1) { + struct squashfs_file *file = queue_get(to_writer); + int file_fd; + long long hole = 0; + int failed = FALSE; + int error; + + if(file == NULL) { + queue_put(from_writer, NULL); + continue; + } else if(file->fd == -1) { + /* write attributes for directory file->pathname */ + set_attributes(file->pathname, file->mode, file->uid, + file->gid, file->time, file->xattr, TRUE); + free(file->pathname); + free(file); + continue; + } + + TRACE("writer: regular file, blocks %d\n", file->blocks); + + file_fd = file->fd; + + for(i = 0; i < file->blocks; i++, cur_blocks ++) { + struct file_entry *block = queue_get(to_writer); + + if(block->buffer == 0) { /* sparse file */ + hole += block->size; + free(block); + continue; + } + + cache_block_wait(block->buffer); + + if(block->buffer->error) + failed = TRUE; + + if(failed) + continue; + + error = write_block(file_fd, block->buffer->data + + block->offset, block->size, hole, file->sparse); + + if(error == FALSE) { + ERROR("writer: failed to write data block %d\n", + i); + failed = TRUE; + } + + hole = 0; + cache_block_put(block->buffer); + free(block); + } + + if(hole && failed == FALSE) { + /* + * corner case for hole extending to end of file + */ + if(file->sparse == FALSE || + lseek(file_fd, hole, SEEK_CUR) == -1) { + /* + * for files which we don't want to write + * sparsely, or for broken lseeks which cannot + * seek beyond end of file, write_block will do + * the right thing + */ + hole --; + if(write_block(file_fd, "\0", 1, hole, + file->sparse) == FALSE) { + ERROR("writer: failed to write sparse " + "data block\n"); + failed = TRUE; + } + } else if(ftruncate(file_fd, file->file_size) == -1) { + ERROR("writer: failed to write sparse data " + "block\n"); + failed = TRUE; + } + } + + close_wake(file_fd); + if(failed == FALSE) + set_attributes(file->pathname, file->mode, file->uid, + file->gid, file->time, file->xattr, force); + else { + ERROR("Failed to write %s, skipping\n", file->pathname); + unlink(file->pathname); + } + free(file->pathname); + free(file); + + } +} + + +/* + * decompress thread. This decompresses buffers queued by the read thread + */ +void *inflator(void *arg) +{ + char tmp[block_size]; + + while(1) { + struct cache_entry *entry = queue_get(to_inflate); + int error, res; + + res = compressor_uncompress(comp, tmp, entry->data, + SQUASHFS_COMPRESSED_SIZE_BLOCK(entry->size), block_size, + &error); + + if(res == -1) + ERROR("%s uncompress failed with error code %d\n", + comp->name, error); + else + memcpy(entry->data, tmp, res); + + /* + * block has been either successfully decompressed, or an error + * occurred, clear pending flag, set error appropriately and + * wake up any threads waiting on this block + */ + cache_block_ready(entry, res == -1); + } +} + + +void *progress_thread(void *arg) +{ + struct timespec requested_time, remaining; + struct itimerval itimerval; + struct winsize winsize; + + if(ioctl(1, TIOCGWINSZ, &winsize) == -1) { + if(isatty(STDOUT_FILENO)) + ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 " + "columns\n"); + columns = 80; + } else + columns = winsize.ws_col; + signal(SIGWINCH, sigwinch_handler); + signal(SIGALRM, sigalrm_handler); + + itimerval.it_value.tv_sec = 0; + itimerval.it_value.tv_usec = 250000; + itimerval.it_interval.tv_sec = 0; + itimerval.it_interval.tv_usec = 250000; + setitimer(ITIMER_REAL, &itimerval, NULL); + + requested_time.tv_sec = 0; + requested_time.tv_nsec = 250000000; + + while(1) { + int res = nanosleep(&requested_time, &remaining); + + if(res == -1 && errno != EINTR) + EXIT_UNSQUASH("nanosleep failed in progress thread\n"); + + if(progress_enabled) { + pthread_mutex_lock(&screen_mutex); + progress_bar(sym_count + dev_count + + fifo_count + cur_blocks, total_inodes - + total_files + total_blocks, columns); + pthread_mutex_unlock(&screen_mutex); + } + } +} + + +void initialise_threads(int fragment_buffer_size, int data_buffer_size) +{ + struct rlimit rlim; + int i, max_files, res; + sigset_t sigmask, old_mask; + + /* block SIGQUIT and SIGHUP, these are handled by the info thread */ + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGHUP); + sigaddset(&sigmask, SIGALRM); + if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == -1) + EXIT_UNSQUASH("Failed to set signal mask in initialise_threads" + "\n"); + + /* + * temporarily block these signals so the created sub-threads will + * ignore them, ensuring the main thread handles them + */ + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGTERM); + if(pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask) == -1) + EXIT_UNSQUASH("Failed to set signal mask in initialise_threads" + "\n"); + + if(processors == -1) { +#ifndef linux + int mib[2]; + size_t len = sizeof(processors); + + mib[0] = CTL_HW; +#ifdef HW_AVAILCPU + mib[1] = HW_AVAILCPU; +#else + mib[1] = HW_NCPU; +#endif + + if(sysctl(mib, 2, &processors, &len, NULL, 0) == -1) { + ERROR("Failed to get number of available processors. " + "Defaulting to 1\n"); + processors = 1; + } +#else + processors = sysconf(_SC_NPROCESSORS_ONLN); +#endif + } + + if(add_overflow(processors, 3) || + multiply_overflow(processors + 3, sizeof(pthread_t))) + EXIT_UNSQUASH("Processors too large\n"); + + thread = malloc((3 + processors) * sizeof(pthread_t)); + if(thread == NULL) + EXIT_UNSQUASH("Out of memory allocating thread descriptors\n"); + inflator_thread = &thread[3]; + + /* + * dimensioning the to_reader and to_inflate queues. The size of + * these queues is directly related to the amount of block + * read-ahead possible. To_reader queues block read requests to + * the reader thread and to_inflate queues block decompression + * requests to the inflate thread(s) (once the block has been read by + * the reader thread). The amount of read-ahead is determined by + * the combined size of the data_block and fragment caches which + * determine the total number of blocks which can be "in flight" + * at any one time (either being read or being decompressed) + * + * The maximum file open limit, however, affects the read-ahead + * possible, in that for normal sizes of the fragment and data block + * caches, where the incoming files have few data blocks or one fragment + * only, the file open limit is likely to be reached before the + * caches are full. This means the worst case sizing of the combined + * sizes of the caches is unlikely to ever be necessary. However, is is + * obvious read-ahead up to the data block cache size is always possible + * irrespective of the file open limit, because a single file could + * contain that number of blocks. + * + * Choosing the size as "file open limit + data block cache size" seems + * to be a reasonable estimate. We can reasonably assume the maximum + * likely read-ahead possible is data block cache size + one fragment + * per open file. + * + * dimensioning the to_writer queue. The size of this queue is + * directly related to the amount of block read-ahead possible. + * However, unlike the to_reader and to_inflate queues, this is + * complicated by the fact the to_writer queue not only contains + * entries for fragments and data_blocks but it also contains + * file entries, one per open file in the read-ahead. + * + * Choosing the size as "2 * (file open limit) + + * data block cache size" seems to be a reasonable estimate. + * We can reasonably assume the maximum likely read-ahead possible + * is data block cache size + one fragment per open file, and then + * we will have a file_entry for each open file. + */ + res = getrlimit(RLIMIT_NOFILE, &rlim); + if (res == -1) { + ERROR("failed to get open file limit! Defaulting to 1\n"); + rlim.rlim_cur = 1; + } + + if (rlim.rlim_cur != RLIM_INFINITY) { + /* + * leave OPEN_FILE_MARGIN free (rlim_cur includes fds used by + * stdin, stdout, stderr and filesystem fd + */ + if (rlim.rlim_cur <= OPEN_FILE_MARGIN) + /* no margin, use minimum possible */ + max_files = 1; + else + max_files = rlim.rlim_cur - OPEN_FILE_MARGIN; + } else + max_files = -1; + + /* set amount of available files for use by open_wait and close_wake */ + open_init(max_files); + + /* + * allocate to_reader, to_inflate and to_writer queues. Set based on + * open file limit and cache size, unless open file limit is unlimited, + * in which case set purely based on cache limits + * + * In doing so, check that the user supplied values do not overflow + * a signed int + */ + if (max_files != -1) { + if(add_overflow(data_buffer_size, max_files) || + add_overflow(data_buffer_size, max_files * 2)) + EXIT_UNSQUASH("Data queue size is too large\n"); + + to_reader = queue_init(max_files + data_buffer_size); + to_inflate = queue_init(max_files + data_buffer_size); + to_writer = queue_init(max_files * 2 + data_buffer_size); + } else { + int all_buffers_size; + + if(add_overflow(fragment_buffer_size, data_buffer_size)) + EXIT_UNSQUASH("Data and fragment queues combined are" + " too large\n"); + + all_buffers_size = fragment_buffer_size + data_buffer_size; + + if(add_overflow(all_buffers_size, all_buffers_size)) + EXIT_UNSQUASH("Data and fragment queues combined are" + " too large\n"); + + to_reader = queue_init(all_buffers_size); + to_inflate = queue_init(all_buffers_size); + to_writer = queue_init(all_buffers_size * 2); + } + + from_writer = queue_init(1); + + fragment_cache = cache_init(block_size, fragment_buffer_size); + data_cache = cache_init(block_size, data_buffer_size); + pthread_create(&thread[0], NULL, reader, NULL); + pthread_create(&thread[1], NULL, writer, NULL); + pthread_create(&thread[2], NULL, progress_thread, NULL); + init_info(); + pthread_mutex_init(&fragment_mutex, NULL); + + for(i = 0; i < processors; i++) { + if(pthread_create(&inflator_thread[i], NULL, inflator, NULL) != + 0) + EXIT_UNSQUASH("Failed to create thread\n"); + } + + printf("Parallel unsquashfs: Using %d processor%s\n", processors, + processors == 1 ? "" : "s"); + + if(pthread_sigmask(SIG_SETMASK, &old_mask, NULL) == -1) + EXIT_UNSQUASH("Failed to set signal mask in initialise_threads" + "\n"); +} + + +void enable_progress_bar() +{ + pthread_mutex_lock(&screen_mutex); + progress_enabled = progress; + pthread_mutex_unlock(&screen_mutex); +} + + +void disable_progress_bar() +{ + pthread_mutex_lock(&screen_mutex); + if(progress_enabled) { + progress_bar(sym_count + dev_count + fifo_count + cur_blocks, + total_inodes - total_files + total_blocks, columns); + printf("\n"); + } + progress_enabled = FALSE; + pthread_mutex_unlock(&screen_mutex); +} + + +void progressbar_error(char *fmt, ...) +{ + va_list ap; + + pthread_mutex_lock(&screen_mutex); + + if(progress_enabled) + fprintf(stderr, "\n"); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + pthread_mutex_unlock(&screen_mutex); +} + + +void progressbar_info(char *fmt, ...) +{ + va_list ap; + + pthread_mutex_lock(&screen_mutex); + + if(progress_enabled) + printf("\n"); + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + pthread_mutex_unlock(&screen_mutex); +} + +void progress_bar(long long current, long long max, int columns) +{ + char rotate_list[] = { '|', '/', '-', '\\' }; + int max_digits, used, hashes, spaces; + static int tty = -1; + + if(max == 0) + return; + + max_digits = floor(log10(max)) + 1; + used = max_digits * 2 + 11; + hashes = (current * (columns - used)) / max; + spaces = columns - used - hashes; + + if((current > max) || (columns - used < 0)) + return; + + if(tty == -1) + tty = isatty(STDOUT_FILENO); + if(!tty) { + static long long previous = -1; + + /* + * Updating much more frequently than this results in huge + * log files. + */ + if((current % 100) != 0 && current != max) + return; + /* Don't update just to rotate the spinner. */ + if(current == previous) + return; + previous = current; + } + + printf("\r["); + + while (hashes --) + putchar('='); + + putchar(rotate_list[rotate]); + + while(spaces --) + putchar(' '); + + printf("] %*lld/%*lld", max_digits, current, max_digits, max); + printf(" %3lld%%", current * 100 / max); + fflush(stdout); +} + + +int parse_number(char *arg, int *res) +{ + char *b; + long number = strtol(arg, &b, 10); + + /* check for trailing junk after number */ + if(*b != '\0') + return 0; + + /* + * check for strtol underflow or overflow in conversion. + * Note: strtol can validly return LONG_MIN and LONG_MAX + * if the user entered these values, but, additional code + * to distinguish this scenario is unnecessary, because for + * our purposes LONG_MIN and LONG_MAX are too large anyway + */ + if(number == LONG_MIN || number == LONG_MAX) + return 0; + + /* reject negative numbers as invalid */ + if(number < 0) + return 0; + + /* check if long result will overflow signed int */ + if(number > INT_MAX) + return 0; + + *res = number; + return 1; +} + + +#define VERSION() \ + printf("unsquashfs version 4.3 (2014/05/12)\n");\ + printf("copyright (C) 2014 Phillip Lougher "\ + "<phillip@squashfs.org.uk>\n\n");\ + printf("This program is free software; you can redistribute it and/or"\ + "\n");\ + printf("modify it under the terms of the GNU General Public License"\ + "\n");\ + printf("as published by the Free Software Foundation; either version "\ + "2,\n");\ + printf("or (at your option) any later version.\n\n");\ + printf("This program is distributed in the hope that it will be "\ + "useful,\n");\ + printf("but WITHOUT ANY WARRANTY; without even the implied warranty of"\ + "\n");\ + printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"\ + "\n");\ + printf("GNU General Public License for more details.\n"); +int main(int argc, char *argv[]) +{ + char *dest = "squashfs-root"; + int i, stat_sys = FALSE, version = FALSE; + int n; + struct pathnames *paths = NULL; + struct pathname *path = NULL; + long long directory_table_end; + int fragment_buffer_size = FRAGMENT_BUFFER_DEFAULT; + int data_buffer_size = DATA_BUFFER_DEFAULT; + + pthread_mutex_init(&screen_mutex, NULL); + root_process = geteuid() == 0; + if(root_process) + umask(0); + + for(i = 1; i < argc; i++) { + if(*argv[i] != '-') + break; + if(strcmp(argv[i], "-version") == 0 || + strcmp(argv[i], "-v") == 0) { + VERSION(); + version = TRUE; + } else if(strcmp(argv[i], "-info") == 0 || + strcmp(argv[i], "-i") == 0) + info = TRUE; + else if(strcmp(argv[i], "-ls") == 0 || + strcmp(argv[i], "-l") == 0) + lsonly = TRUE; + else if(strcmp(argv[i], "-no-progress") == 0 || + strcmp(argv[i], "-n") == 0) + progress = FALSE; + else if(strcmp(argv[i], "-no-xattrs") == 0 || + strcmp(argv[i], "-no") == 0) + no_xattrs = TRUE; + else if(strcmp(argv[i], "-xattrs") == 0 || + strcmp(argv[i], "-x") == 0) + no_xattrs = FALSE; + else if(strcmp(argv[i], "-user-xattrs") == 0 || + strcmp(argv[i], "-u") == 0) { + user_xattrs = TRUE; + no_xattrs = FALSE; + } else if(strcmp(argv[i], "-dest") == 0 || + strcmp(argv[i], "-d") == 0) { + if(++i == argc) { + fprintf(stderr, "%s: -dest missing filename\n", + argv[0]); + exit(1); + } + dest = argv[i]; + } else if(strcmp(argv[i], "-processors") == 0 || + strcmp(argv[i], "-p") == 0) { + if((++i == argc) || + !parse_number(argv[i], + &processors)) { + ERROR("%s: -processors missing or invalid " + "processor number\n", argv[0]); + exit(1); + } + if(processors < 1) { + ERROR("%s: -processors should be 1 or larger\n", + argv[0]); + exit(1); + } + } else if(strcmp(argv[i], "-data-queue") == 0 || + strcmp(argv[i], "-da") == 0) { + if((++i == argc) || + !parse_number(argv[i], + &data_buffer_size)) { + ERROR("%s: -data-queue missing or invalid " + "queue size\n", argv[0]); + exit(1); + } + if(data_buffer_size < 1) { + ERROR("%s: -data-queue should be 1 Mbyte or " + "larger\n", argv[0]); + exit(1); + } + } else if(strcmp(argv[i], "-frag-queue") == 0 || + strcmp(argv[i], "-fr") == 0) { + if((++i == argc) || + !parse_number(argv[i], + &fragment_buffer_size)) { + ERROR("%s: -frag-queue missing or invalid " + "queue size\n", argv[0]); + exit(1); + } + if(fragment_buffer_size < 1) { + ERROR("%s: -frag-queue should be 1 Mbyte or " + "larger\n", argv[0]); + exit(1); + } + } else if(strcmp(argv[i], "-force") == 0 || + strcmp(argv[i], "-f") == 0) + force = TRUE; + else if(strcmp(argv[i], "-stat") == 0 || + strcmp(argv[i], "-s") == 0) + stat_sys = TRUE; + else if(strcmp(argv[i], "-lls") == 0 || + strcmp(argv[i], "-ll") == 0) { + lsonly = TRUE; + short_ls = FALSE; + } else if(strcmp(argv[i], "-linfo") == 0 || + strcmp(argv[i], "-li") == 0) { + info = TRUE; + short_ls = FALSE; + } else if(strcmp(argv[i], "-ef") == 0 || + strcmp(argv[i], "-e") == 0) { + if(++i == argc) { + fprintf(stderr, "%s: -ef missing filename\n", + argv[0]); + exit(1); + } + path = process_extract_files(path, argv[i]); + } else if(strcmp(argv[i], "-regex") == 0 || + strcmp(argv[i], "-r") == 0) + use_regex = TRUE; + else + goto options; + } + + if(lsonly || info) + progress = FALSE; + +#ifdef SQUASHFS_TRACE + /* + * Disable progress bar if full debug tracing is enabled. + * The progress bar in this case just gets in the way of the + * debug trace output + */ + progress = FALSE; +#endif + + if(i == argc) { + if(!version) { +options: + ERROR("SYNTAX: %s [options] filesystem [directories or " + "files to extract]\n", argv[0]); + ERROR("\t-v[ersion]\t\tprint version, licence and " + "copyright information\n"); + ERROR("\t-d[est] <pathname>\tunsquash to <pathname>, " + "default \"squashfs-root\"\n"); + ERROR("\t-n[o-progress]\t\tdon't display the progress " + "bar\n"); + ERROR("\t-no[-xattrs]\t\tdon't extract xattrs in file system" + NOXOPT_STR"\n"); + ERROR("\t-x[attrs]\t\textract xattrs in file system" + XOPT_STR "\n"); + ERROR("\t-u[ser-xattrs]\t\tonly extract user xattrs in " + "file system.\n\t\t\t\tEnables extracting " + "xattrs\n"); + ERROR("\t-p[rocessors] <number>\tuse <number> " + "processors. By default will use\n"); + ERROR("\t\t\t\tnumber of processors available\n"); + ERROR("\t-i[nfo]\t\t\tprint files as they are " + "unsquashed\n"); + ERROR("\t-li[nfo]\t\tprint files as they are " + "unsquashed with file\n"); + ERROR("\t\t\t\tattributes (like ls -l output)\n"); + ERROR("\t-l[s]\t\t\tlist filesystem, but don't unsquash" + "\n"); + ERROR("\t-ll[s]\t\t\tlist filesystem with file " + "attributes (like\n"); + ERROR("\t\t\t\tls -l output), but don't unsquash\n"); + ERROR("\t-f[orce]\t\tif file already exists then " + "overwrite\n"); + ERROR("\t-s[tat]\t\t\tdisplay filesystem superblock " + "information\n"); + ERROR("\t-e[f] <extract file>\tlist of directories or " + "files to extract.\n\t\t\t\tOne per line\n"); + ERROR("\t-da[ta-queue] <size>\tSet data queue to " + "<size> Mbytes. Default %d\n\t\t\t\tMbytes\n", + DATA_BUFFER_DEFAULT); + ERROR("\t-fr[ag-queue] <size>\tSet fragment queue to " + "<size> Mbytes. Default\n\t\t\t\t%d Mbytes\n", + FRAGMENT_BUFFER_DEFAULT); + ERROR("\t-r[egex]\t\ttreat extract names as POSIX " + "regular expressions\n"); + ERROR("\t\t\t\trather than use the default shell " + "wildcard\n\t\t\t\texpansion (globbing)\n"); + ERROR("\nDecompressors available:\n"); + display_compressors("", ""); + } + exit(1); + } + + for(n = i + 1; n < argc; n++) + path = add_path(path, argv[n], argv[n]); + + if((fd = open(argv[i], O_RDONLY)) == -1) { + ERROR("Could not open %s, because %s\n", argv[i], + strerror(errno)); + exit(1); + } + + if(read_super(argv[i]) == FALSE) + exit(1); + + if(stat_sys) { + squashfs_stat(argv[i]); + exit(0); + } + + if(!check_compression(comp)) + exit(1); + + block_size = sBlk.s.block_size; + block_log = sBlk.s.block_log; + + /* + * Sanity check block size and block log. + * + * Check they're within correct limits + */ + if(block_size > SQUASHFS_FILE_MAX_SIZE || + block_log > SQUASHFS_FILE_MAX_LOG) + EXIT_UNSQUASH("Block size or block_log too large." + " File system is corrupt.\n"); + + /* + * Check block_size and block_log match + */ + if(block_size != (1 << block_log)) + EXIT_UNSQUASH("Block size and block_log do not match." + " File system is corrupt.\n"); + + /* + * convert from queue size in Mbytes to queue size in + * blocks. + * + * In doing so, check that the user supplied values do not + * overflow a signed int + */ + if(shift_overflow(fragment_buffer_size, 20 - block_log)) + EXIT_UNSQUASH("Fragment queue size is too large\n"); + else + fragment_buffer_size <<= 20 - block_log; + + if(shift_overflow(data_buffer_size, 20 - block_log)) + EXIT_UNSQUASH("Data queue size is too large\n"); + else + data_buffer_size <<= 20 - block_log; + + initialise_threads(fragment_buffer_size, data_buffer_size); + + fragment_data = malloc(block_size); + if(fragment_data == NULL) + EXIT_UNSQUASH("failed to allocate fragment_data\n"); + + file_data = malloc(block_size); + if(file_data == NULL) + EXIT_UNSQUASH("failed to allocate file_data"); + + data = malloc(block_size); + if(data == NULL) + EXIT_UNSQUASH("failed to allocate data\n"); + + created_inode = malloc(sBlk.s.inodes * sizeof(char *)); + if(created_inode == NULL) + EXIT_UNSQUASH("failed to allocate created_inode\n"); + + memset(created_inode, 0, sBlk.s.inodes * sizeof(char *)); + + if(s_ops.read_uids_guids() == FALSE) + EXIT_UNSQUASH("failed to uid/gid table\n"); + + if(s_ops.read_fragment_table(&directory_table_end) == FALSE) + EXIT_UNSQUASH("failed to read fragment table\n"); + + if(read_inode_table(sBlk.s.inode_table_start, + sBlk.s.directory_table_start) == FALSE) + EXIT_UNSQUASH("failed to read inode table\n"); + + if(read_directory_table(sBlk.s.directory_table_start, + directory_table_end) == FALSE) + EXIT_UNSQUASH("failed to read directory table\n"); + + if(no_xattrs) + sBlk.s.xattr_id_table_start = SQUASHFS_INVALID_BLK; + + if(read_xattrs_from_disk(fd, &sBlk.s) == 0) + EXIT_UNSQUASH("failed to read the xattr table\n"); + + if(path) { + paths = init_subdir(); + paths = add_subdir(paths, path); + } + + pre_scan(dest, SQUASHFS_INODE_BLK(sBlk.s.root_inode), + SQUASHFS_INODE_OFFSET(sBlk.s.root_inode), paths); + + memset(created_inode, 0, sBlk.s.inodes * sizeof(char *)); + inode_number = 1; + + printf("%d inodes (%d blocks) to write\n\n", total_inodes, + total_inodes - total_files + total_blocks); + + enable_progress_bar(); + + dir_scan(dest, SQUASHFS_INODE_BLK(sBlk.s.root_inode), + SQUASHFS_INODE_OFFSET(sBlk.s.root_inode), paths); + + queue_put(to_writer, NULL); + queue_get(from_writer); + + disable_progress_bar(); + + if(!lsonly) { + printf("\n"); + printf("created %d files\n", file_count); + printf("created %d directories\n", dir_count); + printf("created %d symlinks\n", sym_count); + printf("created %d devices\n", dev_count); + printf("created %d fifos\n", fifo_count); + } + + return 0; +}
diff --git a/squashfs-tools/squashfs-tools/unsquashfs.h b/squashfs-tools/squashfs-tools/unsquashfs.h new file mode 100644 index 0000000..4836b8d --- /dev/null +++ b/squashfs-tools/squashfs-tools/unsquashfs.h
@@ -0,0 +1,282 @@ +#ifndef UNSQUASHFS_H +#define UNSQUASHFS_H +/* + * Unsquash a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2009, 2010, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquashfs.h + */ + +#define TRUE 1 +#define FALSE 0 +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <sys/mman.h> +#include <utime.h> +#include <pwd.h> +#include <grp.h> +#include <time.h> +#include <regex.h> +#include <fnmatch.h> +#include <signal.h> +#include <pthread.h> +#include <math.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +#ifndef FNM_EXTMATCH /* glibc extension */ + #define FNM_EXTMATCH 0 +#endif + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include <endian.h> +#endif + +#include "squashfs_fs.h" +#include "error.h" + +#define CALCULATE_HASH(start) (start & 0xffff) + +/* + * Unified superblock containing fields for all superblocks + */ +struct super_block { + struct squashfs_super_block s; + /* fields only used by squashfs 3 and earlier layouts */ + unsigned int no_uids; + unsigned int no_guids; + long long uid_start; + long long guid_start; +}; + +struct hash_table_entry { + long long start; + int bytes; + struct hash_table_entry *next; +}; + +struct inode { + int blocks; + char *block_ptr; + long long data; + int fragment; + int frag_bytes; + gid_t gid; + int inode_number; + int mode; + int offset; + long long start; + char *symlink; + time_t time; + int type; + uid_t uid; + char sparse; + unsigned int xattr; +}; + +typedef struct squashfs_operations { + struct dir *(*squashfs_opendir)(unsigned int block_start, + unsigned int offset, struct inode **i); + void (*read_fragment)(unsigned int fragment, long long *start_block, + int *size); + int (*read_fragment_table)(long long *); + void (*read_block_list)(unsigned int *block_list, char *block_ptr, + int blocks); + struct inode *(*read_inode)(unsigned int start_block, + unsigned int offset); + int (*read_uids_guids)(); +} squashfs_operations; + +struct test { + int mask; + int value; + int position; + char mode; +}; + + +/* Cache status struct. Caches are used to keep + track of memory buffers passed between different threads */ +struct cache { + int max_buffers; + int count; + int used; + int buffer_size; + int wait_free; + int wait_pending; + pthread_mutex_t mutex; + pthread_cond_t wait_for_free; + pthread_cond_t wait_for_pending; + struct cache_entry *free_list; + struct cache_entry *hash_table[65536]; +}; + +/* struct describing a cache entry passed between threads */ +struct cache_entry { + struct cache *cache; + long long block; + int size; + int used; + int error; + int pending; + struct cache_entry *hash_next; + struct cache_entry *hash_prev; + struct cache_entry *free_next; + struct cache_entry *free_prev; + char *data; +}; + +/* struct describing queues used to pass data between threads */ +struct queue { + int size; + int readp; + int writep; + pthread_mutex_t mutex; + pthread_cond_t empty; + pthread_cond_t full; + void **data; +}; + +/* default size of fragment buffer in Mbytes */ +#define FRAGMENT_BUFFER_DEFAULT 256 +/* default size of data buffer in Mbytes */ +#define DATA_BUFFER_DEFAULT 256 + +#define DIR_ENT_SIZE 16 + +struct dir_ent { + char name[SQUASHFS_NAME_LEN + 1]; + unsigned int start_block; + unsigned int offset; + unsigned int type; +}; + +struct dir { + int dir_count; + int cur_entry; + unsigned int mode; + uid_t uid; + gid_t guid; + unsigned int mtime; + unsigned int xattr; + struct dir_ent *dirs; +}; + +struct file_entry { + int offset; + int size; + struct cache_entry *buffer; +}; + + +struct squashfs_file { + int fd; + int blocks; + long long file_size; + int mode; + uid_t uid; + gid_t gid; + time_t time; + char *pathname; + char sparse; + unsigned int xattr; +}; + +struct path_entry { + char *name; + regex_t *preg; + struct pathname *paths; +}; + +struct pathname { + int names; + struct path_entry *name; +}; + +struct pathnames { + int count; + struct pathname *path[0]; +}; +#define PATHS_ALLOC_SIZE 10 + +/* globals */ +extern struct super_block sBlk; +extern squashfs_operations s_ops; +extern int swap; +extern char *inode_table, *directory_table; +extern struct hash_table_entry *inode_table_hash[65536], + *directory_table_hash[65536]; +extern unsigned int *uid_table, *guid_table; +extern pthread_mutex_t screen_mutex; +extern int progress_enabled; +extern int inode_number; +extern int lookup_type[]; +extern int fd; +extern struct queue *to_reader, *to_inflate, *to_writer; +extern struct cache *fragment_cache, *data_cache; + +/* unsquashfs.c */ +extern int lookup_entry(struct hash_table_entry **, long long); +extern int read_fs_bytes(int fd, long long, int, void *); +extern int read_block(int, long long, long long *, int, void *); +extern void enable_progress_bar(); +extern void disable_progress_bar(); +extern void dump_queue(struct queue *); +extern void dump_cache(struct cache *); + +/* unsquash-1.c */ +extern void read_block_list_1(unsigned int *, char *, int); +extern int read_fragment_table_1(long long *); +extern struct inode *read_inode_1(unsigned int, unsigned int); +extern struct dir *squashfs_opendir_1(unsigned int, unsigned int, + struct inode **); +extern int read_uids_guids_1(); + +/* unsquash-2.c */ +extern void read_block_list_2(unsigned int *, char *, int); +extern int read_fragment_table_2(long long *); +extern void read_fragment_2(unsigned int, long long *, int *); +extern struct inode *read_inode_2(unsigned int, unsigned int); + +/* unsquash-3.c */ +extern int read_fragment_table_3(long long *); +extern void read_fragment_3(unsigned int, long long *, int *); +extern struct inode *read_inode_3(unsigned int, unsigned int); +extern struct dir *squashfs_opendir_3(unsigned int, unsigned int, + struct inode **); + +/* unsquash-4.c */ +extern int read_fragment_table_4(long long *); +extern void read_fragment_4(unsigned int, long long *, int *); +extern struct inode *read_inode_4(unsigned int, unsigned int); +extern struct dir *squashfs_opendir_4(unsigned int, unsigned int, + struct inode **); +extern int read_uids_guids_4(); +#endif
diff --git a/squashfs-tools/squashfs-tools/unsquashfs_info.c b/squashfs-tools/squashfs-tools/unsquashfs_info.c new file mode 100644 index 0000000..7d4f7af --- /dev/null +++ b/squashfs-tools/squashfs-tools/unsquashfs_info.c
@@ -0,0 +1,140 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquashfs_info.c + */ + +#include <pthread.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#include <stdio.h> +#include <math.h> +#include <stdarg.h> +#include <errno.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> + +#include "squashfs_fs.h" +#include "unsquashfs.h" +#include "error.h" + +static int silent = 0; +char *pathname = NULL; + +pthread_t info_thread; + + +void disable_info() +{ + if(pathname) + free(pathname); + + pathname = NULL; +} + + +void update_info(char *name) +{ + if(pathname) + free(pathname); + + pathname = name; +} + + +void dump_state() +{ + disable_progress_bar(); + + printf("Queue and cache status dump\n"); + printf("===========================\n"); + + printf("file buffer read queue (main thread -> reader thread)\n"); + dump_queue(to_reader); + + printf("file buffer decompress queue (reader thread -> inflate" + " thread(s))\n"); + dump_queue(to_inflate); + + printf("file buffer write queue (main thread -> writer thread)\n"); + dump_queue(to_writer); + + printf("\nbuffer cache (uncompressed blocks and compressed blocks " + "'in flight')\n"); + dump_cache(data_cache); + + printf("fragment buffer cache (uncompressed frags and compressed" + " frags 'in flight')\n"); + dump_cache(fragment_cache); + + enable_progress_bar(); +} + + +void *info_thrd(void *arg) +{ + sigset_t sigmask; + int sig, err, waiting = 0; + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGHUP); + sigaddset(&sigmask, SIGALRM); + + while(1) { + err = sigwait(&sigmask, &sig); + + if(err == -1) { + switch(errno) { + case EINTR: + continue; + default: + BAD_ERROR("sigwait failed " + "because %s\n", strerror(errno)); + } + } + + if(sig == SIGQUIT && !waiting) { + if(pathname) + INFO("%s\n", pathname); + + /* set one second interval period, if ^\ received + within then, dump queue and cache status */ + waiting = 1; + alarm(1); + } else if (sig == SIGQUIT) { + dump_state(); + } else if (sig == SIGALRM) { + waiting = 0; + } + } +} + + +void init_info() +{ + pthread_create(&info_thread, NULL, info_thrd, NULL); +}
diff --git a/squashfs-tools/squashfs-tools/unsquashfs_info.h b/squashfs-tools/squashfs-tools/unsquashfs_info.h new file mode 100644 index 0000000..f85efd1 --- /dev/null +++ b/squashfs-tools/squashfs-tools/unsquashfs_info.h
@@ -0,0 +1,30 @@ +#ifndef UNSQUASHFS_INFO_H +#define UNSQUASHFS_INFO_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquashfs_info.h + */ + +extern void disable_info(); +extern void update_info(char *); +extern void init_info(); +#endif
diff --git a/squashfs-tools/squashfs-tools/unsquashfs_xattr.c b/squashfs-tools/squashfs-tools/unsquashfs_xattr.c new file mode 100644 index 0000000..13f0e35 --- /dev/null +++ b/squashfs-tools/squashfs-tools/unsquashfs_xattr.c
@@ -0,0 +1,144 @@ +/* + * Unsquash a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2010, 2012 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquashfs_xattr.c + */ + +#include "unsquashfs.h" +#include "xattr.h" + +#include <sys/xattr.h> + +#ifdef XATTR_NOFOLLOW /* Apple's xattrs */ + #define lsetxattr(path_, name_, val_, sz_, flags_) \ + setxattr(path_, name_, val_, sz_, 0, flags_ | XATTR_NOFOLLOW) +#endif + +#define NOSPACE_MAX 10 + +extern int root_process; +extern int user_xattrs; + +void write_xattr(char *pathname, unsigned int xattr) +{ + unsigned int count; + struct xattr_list *xattr_list; + int i; + static int nonsuper_error = FALSE; + static int ignore_xattrs = FALSE; + static int nospace_error = 0; + + if(ignore_xattrs || xattr == SQUASHFS_INVALID_XATTR || + sBlk.s.xattr_id_table_start == SQUASHFS_INVALID_BLK) + return; + + xattr_list = get_xattr(xattr, &count, 1); + if(xattr_list == NULL) { + ERROR("Failed to read xattrs for file %s\n", pathname); + return; + } + + for(i = 0; i < count; i++) { + int prefix = xattr_list[i].type & SQUASHFS_XATTR_PREFIX_MASK; + + if(user_xattrs && prefix != SQUASHFS_XATTR_USER) + continue; + + if(root_process || prefix == SQUASHFS_XATTR_USER) { + int res = lsetxattr(pathname, xattr_list[i].full_name, + xattr_list[i].value, xattr_list[i].vsize, 0); + + if(res == -1) { + if(errno == ENOTSUP) { + /* + * If the destination filesystem cannot + * suppport xattrs, print error, and + * disable xattr output as this error is + * unlikely to go away, and printing + * screenfulls of the same error message + * is rather annoying + */ + ERROR("write_xattr: failed to write " + "xattr %s for file %s because " + "extended attributes are not " + "supported by the destination " + "filesystem\n", + xattr_list[i].full_name, + pathname); + ERROR("Ignoring xattrs in " + "filesystem\n"); + ERROR("To avoid this error message, " + "specify -no-xattrs\n"); + ignore_xattrs = TRUE; + } else if((errno == ENOSPC || errno == EDQUOT) + && nospace_error < NOSPACE_MAX) { + /* + * Many filesystems like ext2/3/4 have + * limits on the amount of xattr + * data that can be stored per file + * (typically one block or 4K), so + * we shouldn't disable xattr ouput, + * as the error may be restriced to one + * file only. If we get a lot of these + * then suppress the error messsage + */ + ERROR("write_xattr: failed to write " + "xattr %s for file %s because " + "no extended attribute space " + "remaining (per file or " + "filesystem limit)\n", + xattr_list[i].full_name, + pathname); + if(++ nospace_error == NOSPACE_MAX) + ERROR("%d of these errors " + "printed, further error " + "messages of this type " + "are suppressed!\n", + NOSPACE_MAX); + } else + ERROR("write_xattr: failed to write " + "xattr %s for file %s because " + "%s\n", xattr_list[i].full_name, + pathname, strerror(errno)); + } + } else if(nonsuper_error == FALSE) { + /* + * if extract user xattrs only then + * error message is suppressed, if not + * print error, and then suppress further error + * messages to avoid possible screenfulls of the + * same error message! + */ + ERROR("write_xattr: could not write xattr %s " + "for file %s because you're not " + "superuser!\n", + xattr_list[i].full_name, pathname); + ERROR("write_xattr: to avoid this error message, either" + " specify -user-xattrs, -no-xattrs, or run as " + "superuser!\n"); + ERROR("Further error messages of this type are " + "suppressed!\n"); + nonsuper_error = TRUE; + } + } + + free_xattr(xattr_list, count); +}
diff --git a/squashfs-tools/squashfs-tools/xattr.c b/squashfs-tools/squashfs-tools/xattr.c new file mode 100644 index 0000000..6cecb74 --- /dev/null +++ b/squashfs-tools/squashfs-tools/xattr.c
@@ -0,0 +1,819 @@ +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2008, 2009, 2010, 2012, 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. + * + * xattr.c + */ + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include <endian.h> +#endif + +#define TRUE 1 +#define FALSE 0 + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <dirent.h> +#include <string.h> +#include <stdlib.h> +#include <sys/xattr.h> + +#ifdef XATTR_NOFOLLOW /* Apple's xattrs */ + #define llistxattr(path_, buf_, sz_) \ + listxattr(path_, buf_, sz_, XATTR_NOFOLLOW) + #define lgetxattr(path_, name_, val_, sz_) \ + getxattr(path_, name_, val_, sz_, 0, XATTR_NOFOLLOW) +#endif + +#include "squashfs_fs.h" +#include "squashfs_swap.h" +#include "mksquashfs.h" +#include "xattr.h" +#include "error.h" +#include "progressbar.h" + +/* ANDROID CHANGES START*/ +#ifdef ANDROID +#include "android.h" +#ifdef __ANDROID__ +#include <linux/capability.h> +#else +#include <private/android_filesystem_capability.h> +#endif +static struct selabel_handle *sehnd = NULL; +#endif +/* ANDROID CHANGES END */ + +/* compressed xattr table */ +static char *xattr_table = NULL; +static unsigned int xattr_size = 0; + +/* cached uncompressed xattr data */ +static char *data_cache = NULL; +static int cache_bytes = 0, cache_size = 0; + +/* cached uncompressed xattr id table */ +static struct squashfs_xattr_id *xattr_id_table = NULL; +static int xattr_ids = 0; + +/* saved compressed xattr table */ +unsigned int sxattr_bytes = 0, stotal_xattr_bytes = 0; + +/* saved cached uncompressed xattr data */ +static char *sdata_cache = NULL; +static int scache_bytes = 0; + +/* saved cached uncompressed xattr id table */ +static int sxattr_ids = 0; + +/* xattr hash table for value duplicate detection */ +static struct xattr_list *dupl_value[65536]; + +/* xattr hash table for id duplicate detection */ +static struct dupl_id *dupl_id[65536]; + +/* file system globals from mksquashfs.c */ +extern int no_xattrs, noX; +extern long long bytes; +extern int fd; +extern unsigned int xattr_bytes, total_xattr_bytes; +/* ANDROID CHANGES START*/ +extern char *context_file; +extern char *mount_point; +/* ANDROID CHANGES END */ + +/* helper functions from mksquashfs.c */ +extern unsigned short get_checksum(char *, int, unsigned short); +extern void write_destination(int, long long, int, void *); +extern long long generic_write_table(int, void *, int, void *, int); +extern int mangle(char *, char *, int, int, int, int); +extern char *pathname(struct dir_ent *); +/* ANDROID CHANGES START*/ +#ifdef ANDROID +extern char *subpathname(struct dir_ent *); +#endif +/* ANDROID CHANGES END */ + +/* helper functions and definitions from read_xattrs.c */ +extern int read_xattrs_from_disk(int, struct squashfs_super_block *); +extern struct xattr_list *get_xattr(int, unsigned int *, int); +extern struct prefix prefix_table[]; + + +static int get_prefix(struct xattr_list *xattr, char *name) +{ + int i; + + xattr->full_name = strdup(name); + + for(i = 0; prefix_table[i].type != -1; i++) { + struct prefix *p = &prefix_table[i]; + if(strncmp(xattr->full_name, p->prefix, strlen(p->prefix)) == 0) + break; + } + + if(prefix_table[i].type != -1) { + xattr->name = xattr->full_name + strlen(prefix_table[i].prefix); + xattr->size = strlen(xattr->name); + } + + return prefix_table[i].type; +} + + +/* ANDROID CHANGES START*/ +#ifdef ANDROID +static struct xattr_list *next_xattr_list(int *xattr_count, struct xattr_list **xattrs) { + struct xattr_list *x; + x = realloc(*xattrs, ++*xattr_count * sizeof(struct xattr_list)); + if (x == NULL) MEM_ERROR(); + *xattrs = x; + return &x[*xattr_count - 1]; +} + +static void read_selinux_xattr_from_sehnd(char *filename, int mode, + struct selabel_handle *sehnd, struct xattr_list *xattrs) +{ + char *attr_val; + + xattrs->type = get_prefix(xattrs, "security.selinux"); + attr_val = set_selabel(filename, mode, sehnd); + xattrs->value = (void *)attr_val; + xattrs->vsize = strlen(attr_val); +} + +static void set_caps_xattr(uint64_t caps, struct xattr_list *xattrs) +{ + struct vfs_cap_data *attr_val; + attr_val = malloc(sizeof(*attr_val)); + if (attr_val == NULL) MEM_ERROR(); + + xattrs->type = get_prefix(xattrs, "security.capability"); + *attr_val = set_caps(caps); + xattrs->value = attr_val; + xattrs->vsize = sizeof(*attr_val); +} +#endif +/* ANDROID CHANGES END */ + + +static int read_xattrs_from_system(char *filename, struct xattr_list **xattrs) +{ + ssize_t size, vsize; + char *xattr_names, *p; + int i; + struct xattr_list *xattr_list = NULL; + + while(1) { + size = llistxattr(filename, NULL, 0); + if(size <= 0) { + if(size < 0 && errno != ENOTSUP) { + ERROR_START("llistxattr for %s failed in " + "read_attrs, because %s", filename, + strerror(errno)); + ERROR_EXIT(". Ignoring"); + } + return 0; + } + + xattr_names = malloc(size); + if(xattr_names == NULL) + MEM_ERROR(); + + size = llistxattr(filename, xattr_names, size); + if(size < 0) { + free(xattr_names); + if(errno == ERANGE) + /* xattr list grew? Try again */ + continue; + else { + ERROR_START("llistxattr for %s failed in " + "read_attrs, because %s", filename, + strerror(errno)); + ERROR_EXIT(". Ignoring"); + return 0; + } + } + + break; + } + + for(i = 0, p = xattr_names; p < xattr_names + size; i++) { + struct xattr_list *x = realloc(xattr_list, (i + 1) * + sizeof(struct xattr_list)); + if(x == NULL) + MEM_ERROR(); + xattr_list = x; + + xattr_list[i].type = get_prefix(&xattr_list[i], p); + p += strlen(p) + 1; + if(xattr_list[i].type == -1) { + ERROR("Unrecognised xattr prefix %s\n", + xattr_list[i].full_name); + free(xattr_list[i].full_name); + i--; + continue; + } + + while(1) { + vsize = lgetxattr(filename, xattr_list[i].full_name, + NULL, 0); + if(vsize < 0) { + ERROR_START("lgetxattr failed for %s in " + "read_attrs, because %s", filename, + strerror(errno)); + ERROR_EXIT(". Ignoring"); + free(xattr_list[i].full_name); + goto failed; + } + + xattr_list[i].value = malloc(vsize); + if(xattr_list[i].value == NULL) + MEM_ERROR(); + + vsize = lgetxattr(filename, xattr_list[i].full_name, + xattr_list[i].value, vsize); + if(vsize < 0) { + free(xattr_list[i].value); + if(errno == ERANGE) + /* xattr grew? Try again */ + continue; + else { + ERROR_START("lgetxattr failed for %s " + "in read_attrs, because %s", + filename, strerror(errno)); + ERROR_EXIT(". Ignoring"); + free(xattr_list[i].full_name); + goto failed; + } + } + + break; + } + xattr_list[i].vsize = vsize; + + TRACE("read_xattrs_from_system: filename %s, xattr name %s," + " vsize %d\n", filename, xattr_list[i].full_name, + xattr_list[i].vsize); + } + free(xattr_names); + *xattrs = xattr_list; + return i; + +failed: + while(--i >= 0) { + free(xattr_list[i].full_name); + free(xattr_list[i].value); + } + free(xattr_list); + free(xattr_names); + return 0; +} + + +static int get_xattr_size(struct xattr_list *xattr) +{ + int size = sizeof(struct squashfs_xattr_entry) + + sizeof(struct squashfs_xattr_val) + xattr->size; + + if(xattr->type & XATTR_VALUE_OOL) + size += XATTR_VALUE_OOL_SIZE; + else + size += xattr->vsize; + + return size; +} + + +static void *get_xattr_space(unsigned int req_size, long long *disk) +{ + int data_space; + unsigned short c_byte; + + /* + * Move and compress cached uncompressed data into xattr table. + */ + while(cache_bytes >= SQUASHFS_METADATA_SIZE) { + if((xattr_size - xattr_bytes) < + ((SQUASHFS_METADATA_SIZE << 1)) + 2) { + xattr_table = realloc(xattr_table, xattr_size + + (SQUASHFS_METADATA_SIZE << 1) + 2); + if(xattr_table == NULL) + MEM_ERROR(); + xattr_size += (SQUASHFS_METADATA_SIZE << 1) + 2; + } + + c_byte = mangle(xattr_table + xattr_bytes + BLOCK_OFFSET, + data_cache, SQUASHFS_METADATA_SIZE, + SQUASHFS_METADATA_SIZE, noX, 0); + TRACE("Xattr block @ 0x%x, size %d\n", xattr_bytes, c_byte); + SQUASHFS_SWAP_SHORTS(&c_byte, xattr_table + xattr_bytes, 1); + xattr_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET; + memmove(data_cache, data_cache + SQUASHFS_METADATA_SIZE, + cache_bytes - SQUASHFS_METADATA_SIZE); + cache_bytes -= SQUASHFS_METADATA_SIZE; + } + + /* + * Ensure there's enough space in the uncompressed data cache + */ + data_space = cache_size - cache_bytes; + if(data_space < req_size) { + int realloc_size = req_size - data_space; + data_cache = realloc(data_cache, cache_size + + realloc_size); + if(data_cache == NULL) + MEM_ERROR(); + cache_size += realloc_size; + } + + if(disk) + *disk = ((long long) xattr_bytes << 16) | cache_bytes; + cache_bytes += req_size; + return data_cache + cache_bytes - req_size; +} + + +static struct dupl_id *check_id_dupl(struct xattr_list *xattr_list, int xattrs) +{ + struct dupl_id *entry; + int i; + unsigned short checksum = 0; + + /* compute checksum over all xattrs */ + for(i = 0; i < xattrs; i++) { + struct xattr_list *xattr = &xattr_list[i]; + + checksum = get_checksum(xattr->full_name, + strlen(xattr->full_name), checksum); + checksum = get_checksum(xattr->value, + xattr->vsize, checksum); + } + + for(entry = dupl_id[checksum]; entry; entry = entry->next) { + if (entry->xattrs != xattrs) + continue; + + for(i = 0; i < xattrs; i++) { + struct xattr_list *xattr = &xattr_list[i]; + struct xattr_list *dup_xattr = &entry->xattr_list[i]; + + if(strcmp(xattr->full_name, dup_xattr->full_name)) + break; + + if(memcmp(xattr->value, dup_xattr->value, xattr->vsize)) + break; + } + + if(i == xattrs) + break; + } + + if(entry == NULL) { + /* no duplicate exists */ + entry = malloc(sizeof(*entry)); + if(entry == NULL) + MEM_ERROR(); + entry->xattrs = xattrs; + entry->xattr_list = xattr_list; + entry->xattr_id = SQUASHFS_INVALID_XATTR; + entry->next = dupl_id[checksum]; + dupl_id[checksum] = entry; + } + + return entry; +} + + +static void check_value_dupl(struct xattr_list *xattr) +{ + struct xattr_list *entry; + + if(xattr->vsize < XATTR_VALUE_OOL_SIZE) + return; + + /* Check if this is a duplicate of an existing value */ + xattr->vchecksum = get_checksum(xattr->value, xattr->vsize, 0); + for(entry = dupl_value[xattr->vchecksum]; entry; entry = entry->vnext) { + if(entry->vsize != xattr->vsize) + continue; + + if(memcmp(entry->value, xattr->value, xattr->vsize) == 0) + break; + } + + if(entry == NULL) { + /* + * No duplicate exists, add to hash table, and mark as + * requiring writing + */ + xattr->vnext = dupl_value[xattr->vchecksum]; + dupl_value[xattr->vchecksum] = xattr; + xattr->ool_value = SQUASHFS_INVALID_BLK; + } else { + /* + * Duplicate exists, make type XATTR_VALUE_OOL, and + * remember where the duplicate is + */ + xattr->type |= XATTR_VALUE_OOL; + xattr->ool_value = entry->ool_value; + /* on appending don't free duplicate values because the + * duplicate value already points to the non-duplicate value */ + if(xattr->value != entry->value) { + free(xattr->value); + xattr->value = entry->value; + } + } +} + + +static int get_xattr_id(int xattrs, struct xattr_list *xattr_list, + long long xattr_disk, struct dupl_id *xattr_dupl) +{ + int i, size = 0; + struct squashfs_xattr_id *xattr_id; + + xattr_id_table = realloc(xattr_id_table, (xattr_ids + 1) * + sizeof(struct squashfs_xattr_id)); + if(xattr_id_table == NULL) + MEM_ERROR(); + + /* get total uncompressed size of xattr data, needed for stat */ + for(i = 0; i < xattrs; i++) + size += strlen(xattr_list[i].full_name) + 1 + + xattr_list[i].vsize; + + xattr_id = &xattr_id_table[xattr_ids]; + xattr_id->xattr = xattr_disk; + xattr_id->count = xattrs; + xattr_id->size = size; + + /* + * keep track of total uncompressed xattr data, needed for mksquashfs + * file system summary + */ + total_xattr_bytes += size; + + xattr_dupl->xattr_id = xattr_ids ++; + return xattr_dupl->xattr_id; +} + + +long long write_xattrs() +{ + unsigned short c_byte; + int i, avail_bytes; + char *datap = data_cache; + long long start_bytes = bytes; + struct squashfs_xattr_table header; + + if(xattr_ids == 0) + return SQUASHFS_INVALID_BLK; + + /* + * Move and compress cached uncompressed data into xattr table. + */ + while(cache_bytes) { + if((xattr_size - xattr_bytes) < + ((SQUASHFS_METADATA_SIZE << 1)) + 2) { + xattr_table = realloc(xattr_table, xattr_size + + (SQUASHFS_METADATA_SIZE << 1) + 2); + if(xattr_table == NULL) + MEM_ERROR(); + xattr_size += (SQUASHFS_METADATA_SIZE << 1) + 2; + } + + avail_bytes = cache_bytes > SQUASHFS_METADATA_SIZE ? + SQUASHFS_METADATA_SIZE : cache_bytes; + c_byte = mangle(xattr_table + xattr_bytes + BLOCK_OFFSET, datap, + avail_bytes, SQUASHFS_METADATA_SIZE, noX, 0); + TRACE("Xattr block @ 0x%x, size %d\n", xattr_bytes, c_byte); + SQUASHFS_SWAP_SHORTS(&c_byte, xattr_table + xattr_bytes, 1); + xattr_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET; + datap += avail_bytes; + cache_bytes -= avail_bytes; + } + + /* + * Write compressed xattr table to file system + */ + write_destination(fd, bytes, xattr_bytes, xattr_table); + bytes += xattr_bytes; + + /* + * Swap if necessary the xattr id table + */ + for(i = 0; i < xattr_ids; i++) + SQUASHFS_INSWAP_XATTR_ID(&xattr_id_table[i]); + + header.xattr_ids = xattr_ids; + header.xattr_table_start = start_bytes; + SQUASHFS_INSWAP_XATTR_TABLE(&header); + + return generic_write_table(xattr_ids * sizeof(struct squashfs_xattr_id), + xattr_id_table, sizeof(header), &header, noX); +} + + +int generate_xattrs(int xattrs, struct xattr_list *xattr_list) +{ + int total_size, i; + int xattr_value_max; + void *xp; + long long xattr_disk; + struct dupl_id *xattr_dupl; + + /* + * check if the file xattrs are a complete duplicate of a pre-existing + * id + */ + xattr_dupl = check_id_dupl(xattr_list, xattrs); + if(xattr_dupl->xattr_id != SQUASHFS_INVALID_XATTR) + return xattr_dupl->xattr_id; + + /* + * Scan the xattr_list deciding which type to assign to each + * xattr. The choice is fairly straightforward, and depends on the + * size of each xattr name/value and the overall size of the + * resultant xattr list stored in the xattr metadata table. + * + * Choices are whether to store data inline or out of line. + * + * The overall goal is to optimise xattr scanning and lookup, and + * to enable the file system layout to scale from a couple of + * small xattr name/values to a large number of large xattr + * names/values without affecting performance. While hopefully + * enabling the common case of a couple of small xattr name/values + * to be stored efficiently + * + * Code repeatedly scans, doing the following + * move xattr data out of line if it exceeds + * xattr_value_max. Where xattr_value_max is + * initially XATTR_INLINE_MAX. If the final uncompressed + * xattr list is larger than XATTR_TARGET_MAX then more + * aggressively move xattr data out of line by repeatedly + * setting inline threshold to 1/2, then 1/4, 1/8 of + * XATTR_INLINE_MAX until target achieved or there's + * nothing left to move out of line + */ + xattr_value_max = XATTR_INLINE_MAX; + while(1) { + for(total_size = 0, i = 0; i < xattrs; i++) { + struct xattr_list *xattr = &xattr_list[i]; + xattr->type &= XATTR_PREFIX_MASK; /* all inline */ + if (xattr->vsize > xattr_value_max) + xattr->type |= XATTR_VALUE_OOL; + + total_size += get_xattr_size(xattr); + } + + /* + * If the total size of the uncompressed xattr list is <= + * XATTR_TARGET_MAX we're done + */ + if(total_size <= XATTR_TARGET_MAX) + break; + + if(xattr_value_max == XATTR_VALUE_OOL_SIZE) + break; + + /* + * Inline target not yet at minimum and so reduce it, and + * try again + */ + xattr_value_max /= 2; + if(xattr_value_max < XATTR_VALUE_OOL_SIZE) + xattr_value_max = XATTR_VALUE_OOL_SIZE; + } + + /* + * Check xattr values for duplicates + */ + for(i = 0; i < xattrs; i++) { + check_value_dupl(&xattr_list[i]); + } + + /* + * Add each out of line value to the file system xattr table + * if it doesn't already exist as a duplicate + */ + for(i = 0; i < xattrs; i++) { + struct xattr_list *xattr = &xattr_list[i]; + + if((xattr->type & XATTR_VALUE_OOL) && + (xattr->ool_value == SQUASHFS_INVALID_BLK)) { + struct squashfs_xattr_val val; + int size = sizeof(val) + xattr->vsize; + xp = get_xattr_space(size, &xattr->ool_value); + val.vsize = xattr->vsize; + SQUASHFS_SWAP_XATTR_VAL(&val, xp); + memcpy(xp + sizeof(val), xattr->value, xattr->vsize); + } + } + + /* + * Create xattr list and add to file system xattr table + */ + get_xattr_space(0, &xattr_disk); + for(i = 0; i < xattrs; i++) { + struct xattr_list *xattr = &xattr_list[i]; + struct squashfs_xattr_entry entry; + struct squashfs_xattr_val val; + + xp = get_xattr_space(sizeof(entry) + xattr->size, NULL); + entry.type = xattr->type; + entry.size = xattr->size; + SQUASHFS_SWAP_XATTR_ENTRY(&entry, xp); + memcpy(xp + sizeof(entry), xattr->name, xattr->size); + + if(xattr->type & XATTR_VALUE_OOL) { + int size = sizeof(val) + XATTR_VALUE_OOL_SIZE; + xp = get_xattr_space(size, NULL); + val.vsize = XATTR_VALUE_OOL_SIZE; + SQUASHFS_SWAP_XATTR_VAL(&val, xp); + SQUASHFS_SWAP_LONG_LONGS(&xattr->ool_value, xp + + sizeof(val), 1); + } else { + int size = sizeof(val) + xattr->vsize; + xp = get_xattr_space(size, &xattr->ool_value); + val.vsize = xattr->vsize; + SQUASHFS_SWAP_XATTR_VAL(&val, xp); + memcpy(xp + sizeof(val), xattr->value, xattr->vsize); + } + } + + /* + * Add to xattr id lookup table + */ + return get_xattr_id(xattrs, xattr_list, xattr_disk, xattr_dupl); +} + + +int read_xattrs(void *d) +{ + struct dir_ent *dir_ent = d; + struct inode_info *inode = dir_ent->inode; + char *filename = pathname(dir_ent); +/* ANDROID CHANGES START*/ +#ifdef ANDROID + // NOTE: xattr_list has to point to an array of xattr_list elements + struct xattr_list *xattr_list = NULL, *next_xattr = NULL; + int xattrs = 0; +#else + struct xattr_list *xattr_list; + int xattrs; +#endif +/* ANDROID CHANGES END */ + + if(no_xattrs || IS_PSEUDO(inode) || inode->root_entry) + return SQUASHFS_INVALID_XATTR; + +/* ANDROID CHANGES START*/ +#ifdef ANDROID + if (context_file) { + if (sehnd == NULL) + sehnd = get_sehnd(context_file); + if (mount_point) { + char *mounted_path; + alloc_mounted_path(mount_point, subpathname(dir_ent), &mounted_path); + next_xattr = next_xattr_list(&xattrs, &xattr_list); + read_selinux_xattr_from_sehnd(mounted_path, inode->buf.st_mode, + sehnd, next_xattr); + free(mounted_path); + } else { + next_xattr = next_xattr_list(&xattrs, &xattr_list); + read_selinux_xattr_from_sehnd(filename, inode->buf.st_mode, + sehnd, next_xattr); + } + } + if (dir_ent->capabilities != 0) { + next_xattr = next_xattr_list(&xattrs, &xattr_list); + set_caps_xattr(dir_ent->capabilities, next_xattr); + } +#else + xattrs = read_xattrs_from_system(filename, &xattr_list); +#endif +/* ANDROID CHANGES END */ + + if(xattrs == 0) + return SQUASHFS_INVALID_XATTR; + + return generate_xattrs(xattrs, xattr_list); +} + + +/* + * Add the existing xattr ids and xattr metadata in the file system being + * appended to, to the in-memory xattr cache. This allows duplicate checking to + * take place against the xattrs already in the file system being appended to, + * and ensures the pre-existing xattrs are written out along with any new xattrs + */ +int get_xattrs(int fd, struct squashfs_super_block *sBlk) +{ + int ids, res, i, id; + unsigned int count; + + TRACE("get_xattrs\n"); + + res = read_xattrs_from_disk(fd, sBlk); + if(res == SQUASHFS_INVALID_BLK || res == 0) + goto done; + ids = res; + + /* + * for each xattr id read and construct its list of xattr + * name:value pairs, and add them to the in-memory xattr cache + */ + for(i = 0; i < ids; i++) { + struct xattr_list *xattr_list = get_xattr(i, &count, 0); + if(xattr_list == NULL) { + res = 0; + goto done; + } + id = generate_xattrs(count, xattr_list); + + /* + * Sanity check, the new xattr id should be the same as the + * xattr id in the original file system + */ + if(id != i) { + ERROR("BUG, different xattr_id in get_xattrs\n"); + res = 0; + goto done; + } + } + +done: + return res; +} + + +/* + * Save current state of xattrs, needed for restoring state in the event of an + * abort in appending + */ +void save_xattrs() +{ + /* save the current state of the compressed xattr data */ + sxattr_bytes = xattr_bytes; + stotal_xattr_bytes = total_xattr_bytes; + + /* + * save the current state of the cached uncompressed xattr data. + * Note we have to save the contents of the data cache because future + * operations will delete the current contents + */ + sdata_cache = malloc(cache_bytes); + if(sdata_cache == NULL) + MEM_ERROR(); + + memcpy(sdata_cache, data_cache, cache_bytes); + scache_bytes = cache_bytes; + + /* save the current state of the xattr id table */ + sxattr_ids = xattr_ids; +} + + +/* + * Restore xattrs in the event of an abort in appending + */ +void restore_xattrs() +{ + /* restore the state of the compressed xattr data */ + xattr_bytes = sxattr_bytes; + total_xattr_bytes = stotal_xattr_bytes; + + /* restore the state of the uncomoressed xattr data */ + memcpy(data_cache, sdata_cache, scache_bytes); + cache_bytes = scache_bytes; + + /* restore the state of the xattr id table */ + xattr_ids = sxattr_ids; +}
diff --git a/squashfs-tools/squashfs-tools/xattr.h b/squashfs-tools/squashfs-tools/xattr.h new file mode 100644 index 0000000..9260255 --- /dev/null +++ b/squashfs-tools/squashfs-tools/xattr.h
@@ -0,0 +1,150 @@ +#ifndef XATTR_H +#define XATTR_H +/* + * Create a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2010, 2012, 2013, 2014 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * xattr.h + */ + +#define XATTR_VALUE_OOL SQUASHFS_XATTR_VALUE_OOL +#define XATTR_PREFIX_MASK SQUASHFS_XATTR_PREFIX_MASK + +#define XATTR_VALUE_OOL_SIZE sizeof(long long) + +/* maximum size of xattr value data that will be inlined */ +#define XATTR_INLINE_MAX 128 + +/* the target size of an inode's xattr name:value list. If it + * exceeds this, then xattr value data will be successively out of lined + * until it meets the target */ +#define XATTR_TARGET_MAX 65536 + +#define IS_XATTR(a) (a != SQUASHFS_INVALID_XATTR) + +struct xattr_list { + char *name; + char *full_name; + int size; + int vsize; + void *value; + int type; + long long ool_value; + unsigned short vchecksum; + struct xattr_list *vnext; +}; + +struct dupl_id { + struct xattr_list *xattr_list; + int xattrs; + int xattr_id; + struct dupl_id *next; +}; + +struct prefix { + char *prefix; + int type; +}; + +extern int generate_xattrs(int, struct xattr_list *); + +#ifdef XATTR_SUPPORT +extern int get_xattrs(int, struct squashfs_super_block *); +extern int read_xattrs(void *); +extern long long write_xattrs(); +extern void save_xattrs(); +extern void restore_xattrs(); +extern unsigned int xattr_bytes, total_xattr_bytes; +extern void write_xattr(char *, unsigned int); +extern int read_xattrs_from_disk(int, struct squashfs_super_block *); +extern struct xattr_list *get_xattr(int, unsigned int *, int); +extern void free_xattr(struct xattr_list *, int); +#else +static inline int get_xattrs(int fd, struct squashfs_super_block *sBlk) +{ + if(sBlk->xattr_id_table_start != SQUASHFS_INVALID_BLK) { + fprintf(stderr, "Xattrs in filesystem! These are not " + "supported on this version of Squashfs\n"); + return 0; + } else + return SQUASHFS_INVALID_BLK; +} + + +static inline int read_xattrs(void *dir_ent) +{ + return SQUASHFS_INVALID_XATTR; +} + + +static inline long long write_xattrs() +{ + return SQUASHFS_INVALID_BLK; +} + + +static inline void save_xattrs() +{ +} + + +static inline void restore_xattrs() +{ +} + + +static inline void write_xattr(char *pathname, unsigned int xattr) +{ +} + + +static inline int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk) +{ + if(sBlk->xattr_id_table_start != SQUASHFS_INVALID_BLK) { + fprintf(stderr, "Xattrs in filesystem! These are not " + "supported on this version of Squashfs\n"); + return 0; + } else + return SQUASHFS_INVALID_BLK; +} + + +static inline struct xattr_list *get_xattr(int i, unsigned int *count, int j) +{ + return NULL; +} +#endif + +#ifdef XATTR_SUPPORT +#ifdef XATTR_DEFAULT +#define NOXOPT_STR +#define XOPT_STR " (default)" +#define XATTR_DEF 0 +#else +#define NOXOPT_STR " (default)" +#define XOPT_STR +#define XATTR_DEF 1 +#endif +#else +#define NOXOPT_STR " (default)" +#define XOPT_STR " (unsupported)" +#define XATTR_DEF 1 +#endif +#endif
diff --git a/squashfs-tools/squashfs-tools/xz_wrapper.c b/squashfs-tools/squashfs-tools/xz_wrapper.c new file mode 100644 index 0000000..e77ec59 --- /dev/null +++ b/squashfs-tools/squashfs-tools/xz_wrapper.c
@@ -0,0 +1,540 @@ +/* + * Copyright (c) 2010, 2011, 2012, 2013 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * xz_wrapper.c + * + * Support for XZ (LZMA2) compression using XZ Utils liblzma + * http://tukaani.org/xz/ + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <lzma.h> + +#include "squashfs_fs.h" +#include "xz_wrapper.h" +#include "compressor.h" + +static struct bcj bcj[] = { + { "x86", LZMA_FILTER_X86, 0 }, + { "powerpc", LZMA_FILTER_POWERPC, 0 }, + { "ia64", LZMA_FILTER_IA64, 0 }, + { "arm", LZMA_FILTER_ARM, 0 }, + { "armthumb", LZMA_FILTER_ARMTHUMB, 0 }, + { "sparc", LZMA_FILTER_SPARC, 0 }, + { NULL, LZMA_VLI_UNKNOWN, 0 } +}; + +static int filter_count = 1; +static int dictionary_size = 0; +static float dictionary_percent = 0; + + +/* + * This function is called by the options parsing code in mksquashfs.c + * to parse any -X compressor option. + * + * Two specific options are supported: + * -Xbcj + * -Xdict-size + * + * This function returns: + * >=0 (number of additional args parsed) on success + * -1 if the option was unrecognised, or + * -2 if the option was recognised, but otherwise bad in + * some way (e.g. invalid parameter) + * + * Note: this function sets internal compressor state, but does not + * pass back the results of the parsing other than success/failure. + * The xz_dump_options() function is called later to get the options in + * a format suitable for writing to the filesystem. + */ +static int xz_options(char *argv[], int argc) +{ + int i; + char *name; + + if(strcmp(argv[0], "-Xbcj") == 0) { + if(argc < 2) { + fprintf(stderr, "xz: -Xbcj missing filter\n"); + goto failed; + } + + name = argv[1]; + while(name[0] != '\0') { + for(i = 0; bcj[i].name; i++) { + int n = strlen(bcj[i].name); + if((strncmp(name, bcj[i].name, n) == 0) && + (name[n] == '\0' || + name[n] == ',')) { + if(bcj[i].selected == 0) { + bcj[i].selected = 1; + filter_count++; + } + name += name[n] == ',' ? n + 1 : n; + break; + } + } + if(bcj[i].name == NULL) { + fprintf(stderr, "xz: -Xbcj unrecognised " + "filter\n"); + goto failed; + } + } + + return 1; + } else if(strcmp(argv[0], "-Xdict-size") == 0) { + char *b; + float size; + + if(argc < 2) { + fprintf(stderr, "xz: -Xdict-size missing dict-size\n"); + goto failed; + } + + size = strtof(argv[1], &b); + if(*b == '%') { + if(size <= 0 || size > 100) { + fprintf(stderr, "xz: -Xdict-size percentage " + "should be 0 < dict-size <= 100\n"); + goto failed; + } + + dictionary_percent = size; + dictionary_size = 0; + } else { + if((float) ((int) size) != size) { + fprintf(stderr, "xz: -Xdict-size can't be " + "fractional unless a percentage of the" + " block size\n"); + goto failed; + } + + dictionary_percent = 0; + dictionary_size = (int) size; + + if(*b == 'k' || *b == 'K') + dictionary_size *= 1024; + else if(*b == 'm' || *b == 'M') + dictionary_size *= 1024 * 1024; + else if(*b != '\0') { + fprintf(stderr, "xz: -Xdict-size invalid " + "dict-size\n"); + goto failed; + } + } + + return 1; + } + + return -1; + +failed: + return -2; +} + + +/* + * This function is called after all options have been parsed. + * It is used to do post-processing on the compressor options using + * values that were not expected to be known at option parse time. + * + * In this case block_size may not be known until after -Xdict-size has + * been processed (in the case where -b is specified after -Xdict-size) + * + * This function returns 0 on successful post processing, or + * -1 on error + */ +static int xz_options_post(int block_size) +{ + /* + * if -Xdict-size has been specified use this to compute the datablock + * dictionary size + */ + if(dictionary_size || dictionary_percent) { + int n; + + if(dictionary_size) { + if(dictionary_size > block_size) { + fprintf(stderr, "xz: -Xdict-size is larger than" + " block_size\n"); + goto failed; + } + } else + dictionary_size = block_size * dictionary_percent / 100; + + if(dictionary_size < 8192) { + fprintf(stderr, "xz: -Xdict-size should be 8192 bytes " + "or larger\n"); + goto failed; + } + + /* + * dictionary_size must be storable in xz header as either + * 2^n or as 2^n+2^(n+1) + */ + n = ffs(dictionary_size) - 1; + if(dictionary_size != (1 << n) && + dictionary_size != ((1 << n) + (1 << (n + 1)))) { + fprintf(stderr, "xz: -Xdict-size is an unsupported " + "value, dict-size must be storable in xz " + "header\n"); + fprintf(stderr, "as either 2^n or as 2^n+2^(n+1). " + "Example dict-sizes are 75%%, 50%%, 37.5%%, " + "25%%,\n"); + fprintf(stderr, "or 32K, 16K, 8K etc.\n"); + goto failed; + } + + } else + /* No -Xdict-size specified, use defaults */ + dictionary_size = block_size; + + return 0; + +failed: + return -1; +} + + +/* + * This function is called by mksquashfs to dump the parsed + * compressor options in a format suitable for writing to the + * compressor options field in the filesystem (stored immediately + * after the superblock). + * + * This function returns a pointer to the compression options structure + * to be stored (and the size), or NULL if there are no compression + * options + */ +static void *xz_dump_options(int block_size, int *size) +{ + static struct comp_opts comp_opts; + int flags = 0, i; + + /* + * don't store compressor specific options in file system if the + * default options are being used - no compressor options in the + * file system means the default options are always assumed + * + * Defaults are: + * metadata dictionary size: SQUASHFS_METADATA_SIZE + * datablock dictionary size: block_size + * 1 filter + */ + if(dictionary_size == block_size && filter_count == 1) + return NULL; + + for(i = 0; bcj[i].name; i++) + flags |= bcj[i].selected << i; + + comp_opts.dictionary_size = dictionary_size; + comp_opts.flags = flags; + + SQUASHFS_INSWAP_COMP_OPTS(&comp_opts); + + *size = sizeof(comp_opts); + return &comp_opts; +} + + +/* + * This function is a helper specifically for the append mode of + * mksquashfs. Its purpose is to set the internal compressor state + * to the stored compressor options in the passed compressor options + * structure. + * + * In effect this function sets up the compressor options + * to the same state they were when the filesystem was originally + * generated, this is to ensure on appending, the compressor uses + * the same compression options that were used to generate the + * original filesystem. + * + * Note, even if there are no compressor options, this function is still + * called with an empty compressor structure (size == 0), to explicitly + * set the default options, this is to ensure any user supplied + * -X options on the appending mksquashfs command line are over-ridden + * + * This function returns 0 on sucessful extraction of options, and + * -1 on error + */ +static int xz_extract_options(int block_size, void *buffer, int size) +{ + struct comp_opts *comp_opts = buffer; + int flags, i, n; + + if(size == 0) { + /* set defaults */ + dictionary_size = block_size; + flags = 0; + } else { + /* check passed comp opts struct is of the correct length */ + if(size != sizeof(struct comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + dictionary_size = comp_opts->dictionary_size; + flags = comp_opts->flags; + + /* + * check that the dictionary size seems correct - the dictionary + * size should 2^n or 2^n+2^(n+1) + */ + n = ffs(dictionary_size) - 1; + if(dictionary_size != (1 << n) && + dictionary_size != ((1 << n) + (1 << (n + 1)))) + goto failed; + } + + filter_count = 1; + for(i = 0; bcj[i].name; i++) { + if((flags >> i) & 1) { + bcj[i].selected = 1; + filter_count ++; + } else + bcj[i].selected = 0; + } + + return 0; + +failed: + fprintf(stderr, "xz: error reading stored compressor options from " + "filesystem!\n"); + + return -1; +} + + +void xz_display_options(void *buffer, int size) +{ + struct comp_opts *comp_opts = buffer; + int dictionary_size, flags, printed; + int i, n; + + /* check passed comp opts struct is of the correct length */ + if(size != sizeof(struct comp_opts)) + goto failed; + + SQUASHFS_INSWAP_COMP_OPTS(comp_opts); + + dictionary_size = comp_opts->dictionary_size; + flags = comp_opts->flags; + + /* + * check that the dictionary size seems correct - the dictionary + * size should 2^n or 2^n+2^(n+1) + */ + n = ffs(dictionary_size) - 1; + if(dictionary_size != (1 << n) && + dictionary_size != ((1 << n) + (1 << (n + 1)))) + goto failed; + + printf("\tDictionary size %d\n", dictionary_size); + + printed = 0; + for(i = 0; bcj[i].name; i++) { + if((flags >> i) & 1) { + if(printed) + printf(", "); + else + printf("\tFilters selected: "); + printf("%s", bcj[i].name); + printed = 1; + } + } + + if(!printed) + printf("\tNo filters specified\n"); + else + printf("\n"); + + return; + +failed: + fprintf(stderr, "xz: error reading stored compressor options from " + "filesystem!\n"); +} + + +/* + * This function is called by mksquashfs to initialise the + * compressor, before compress() is called. + * + * This function returns 0 on success, and + * -1 on error + */ +static int xz_init(void **strm, int block_size, int datablock) +{ + int i, j, filters = datablock ? filter_count : 1; + struct filter *filter = malloc(filters * sizeof(struct filter)); + struct xz_stream *stream; + + if(filter == NULL) + goto failed; + + stream = *strm = malloc(sizeof(struct xz_stream)); + if(stream == NULL) + goto failed2; + + stream->filter = filter; + stream->filters = filters; + + memset(filter, 0, filters * sizeof(struct filter)); + + stream->dictionary_size = datablock ? dictionary_size : + SQUASHFS_METADATA_SIZE; + + filter[0].filter[0].id = LZMA_FILTER_LZMA2; + filter[0].filter[0].options = &stream->opt; + filter[0].filter[1].id = LZMA_VLI_UNKNOWN; + + for(i = 0, j = 1; datablock && bcj[i].name; i++) { + if(bcj[i].selected) { + filter[j].buffer = malloc(block_size); + if(filter[j].buffer == NULL) + goto failed3; + filter[j].filter[0].id = bcj[i].id; + filter[j].filter[1].id = LZMA_FILTER_LZMA2; + filter[j].filter[1].options = &stream->opt; + filter[j].filter[2].id = LZMA_VLI_UNKNOWN; + j++; + } + } + + return 0; + +failed3: + for(i = 1; i < filters; i++) + free(filter[i].buffer); + free(stream); + +failed2: + free(filter); + +failed: + return -1; +} + + +static int xz_compress(void *strm, void *dest, void *src, int size, + int block_size, int *error) +{ + int i; + lzma_ret res = 0; + struct xz_stream *stream = strm; + struct filter *selected = NULL; + + stream->filter[0].buffer = dest; + + for(i = 0; i < stream->filters; i++) { + struct filter *filter = &stream->filter[i]; + + if(lzma_lzma_preset(&stream->opt, LZMA_PRESET_DEFAULT)) + goto failed; + + stream->opt.dict_size = stream->dictionary_size; + + filter->length = 0; + res = lzma_stream_buffer_encode(filter->filter, + LZMA_CHECK_CRC32, NULL, src, size, filter->buffer, + &filter->length, block_size); + + if(res == LZMA_OK) { + if(!selected || selected->length > filter->length) + selected = filter; + } else if(res != LZMA_BUF_ERROR) + goto failed; + } + + if(!selected) + /* + * Output buffer overflow. Return out of buffer space + */ + return 0; + + if(selected->buffer != dest) + memcpy(dest, selected->buffer, selected->length); + + return (int) selected->length; + +failed: + /* + * All other errors return failure, with the compressor + * specific error code in *error + */ + *error = res; + return -1; +} + + +static int xz_uncompress(void *dest, void *src, int size, int outsize, + int *error) +{ + size_t src_pos = 0; + size_t dest_pos = 0; + uint64_t memlimit = MEMLIMIT; + + lzma_ret res = lzma_stream_buffer_decode(&memlimit, 0, NULL, + src, &src_pos, size, dest, &dest_pos, outsize); + + if(res == LZMA_OK && size == (int) src_pos) + return (int) dest_pos; + else { + *error = res; + return -1; + } +} + + +void xz_usage() +{ + fprintf(stderr, "\t -Xbcj filter1,filter2,...,filterN\n"); + fprintf(stderr, "\t\tCompress using filter1,filter2,...,filterN in"); + fprintf(stderr, " turn\n\t\t(in addition to no filter), and choose"); + fprintf(stderr, " the best compression.\n"); + fprintf(stderr, "\t\tAvailable filters: x86, arm, armthumb,"); + fprintf(stderr, " powerpc, sparc, ia64\n"); + fprintf(stderr, "\t -Xdict-size <dict-size>\n"); + fprintf(stderr, "\t\tUse <dict-size> as the XZ dictionary size. The"); + fprintf(stderr, " dictionary size\n\t\tcan be specified as a"); + fprintf(stderr, " percentage of the block size, or as an\n\t\t"); + fprintf(stderr, "absolute value. The dictionary size must be less"); + fprintf(stderr, " than or equal\n\t\tto the block size and 8192 bytes"); + fprintf(stderr, " or larger. It must also be\n\t\tstorable in the xz"); + fprintf(stderr, " header as either 2^n or as 2^n+2^(n+1).\n\t\t"); + fprintf(stderr, "Example dict-sizes are 75%%, 50%%, 37.5%%, 25%%, or"); + fprintf(stderr, " 32K, 16K, 8K\n\t\tetc.\n"); +} + + +struct compressor xz_comp_ops = { + .init = xz_init, + .compress = xz_compress, + .uncompress = xz_uncompress, + .options = xz_options, + .options_post = xz_options_post, + .dump_options = xz_dump_options, + .extract_options = xz_extract_options, + .display_options = xz_display_options, + .usage = xz_usage, + .id = XZ_COMPRESSION, + .name = "xz", + .supported = 1 +};
diff --git a/squashfs-tools/squashfs-tools/xz_wrapper.h b/squashfs-tools/squashfs-tools/xz_wrapper.h new file mode 100644 index 0000000..ce2545c --- /dev/null +++ b/squashfs-tools/squashfs-tools/xz_wrapper.h
@@ -0,0 +1,71 @@ +#ifndef XZ_WRAPPER_H +#define XZ_WRAPPER_H +/* + * Squashfs + * + * Copyright (c) 2010 + * Phillip Lougher <phillip@squashfs.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * xz_wrapper.h + * + */ + +#ifndef linux +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#else +#include <endian.h> +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +extern unsigned int inswap_le32(unsigned int); + +#define SQUASHFS_INSWAP_COMP_OPTS(s) { \ + (s)->dictionary_size = inswap_le32((s)->dictionary_size); \ + (s)->flags = inswap_le32((s)->flags); \ +} +#else +#define SQUASHFS_INSWAP_COMP_OPTS(s) +#endif + +#define MEMLIMIT (32 * 1024 * 1024) + +struct bcj { + char *name; + lzma_vli id; + int selected; +}; + +struct filter { + void *buffer; + lzma_filter filter[3]; + size_t length; +}; + +struct xz_stream { + struct filter *filter; + int filters; + int dictionary_size; + lzma_options_lzma opt; +}; + +struct comp_opts { + int dictionary_size; + int flags; +}; +#endif