Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
dadigang
Ventoy
Commits
05a1b863
Commit
05a1b863
authored
Apr 05, 2020
by
longpanda
Browse files
initial commit
parent
2090c6fa
Changes
263
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
7389 additions
and
0 deletions
+7389
-0
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.3
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.3
+182
-0
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/pseudo-file.example
...FS/squashfs-tools-4.4/RELEASE-READMEs/pseudo-file.example
+74
-0
SQUASHFS/squashfs-tools-4.4/USAGE
SQUASHFS/squashfs-tools-4.4/USAGE
+1216
-0
SQUASHFS/squashfs-tools-4.4/kernel/README
SQUASHFS/squashfs-tools-4.4/kernel/README
+3
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/Makefile
...squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/Makefile
+11
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/inode.c
.../squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/inode.c
+2029
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs.h
...uashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs.h
+85
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs2_0.c
...hfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs2_0.c
+751
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs.h
...s-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs.h
+915
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_i.h
...tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_i.h
+44
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_sb.h
...ools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_sb.h
+76
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/Documentation/filesystems/squashfs.txt
.../kernel/kernel-2.6/Documentation/filesystems/squashfs.txt
+225
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/Makefile
...squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/Makefile
+8
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/block.c
.../squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/block.c
+274
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/cache.c
.../squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/cache.c
+412
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/dir.c
...FS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/dir.c
+235
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/export.c
...squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/export.c
+155
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/file.c
...S/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/file.c
+502
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/fragment.c
...uashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/fragment.c
+98
-0
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/id.c
...HFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/id.c
+94
-0
No files found.
Too many changes to show.
To preserve performance only
263 of 263+
files are displayed.
Plain diff
Email patch
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.3
0 → 100644
View file @
05a1b863
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.
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/pseudo-file.example
0 → 100644
View file @
05a1b863
# 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
SQUASHFS/squashfs-tools-4.4/USAGE
0 → 100644
View file @
05a1b863
SQUASHFS 4.4 - A squashed read-only filesystem for Linux
Copyright 2002-2019 Phillip Lougher <phillip@squashfs.org.uk>
Released under the GPL licence (version 2 or later).
Welcome to Squashfs version 4.4. 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/zstd 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, lz4
or zstd compression algorithms.
1.1 Introducing reproducible builds
-----------------------------------
Ever since Mksquashfs was parallelised back in 2006, there
has been a certain randomness in how fragments and multi-block
files are ordered in the output filesystem even if the input
remains the same.
This is because the multiple parallel threads can be scheduled
differently between Mksquashfs runs. For example, the thread
given fragment 10 to compress may finish before the thread
given fragment 9 to compress on one run (writing fragment 10
to the output filesystem before fragment 9), but, on the next
run it could be vice-versa. There are many different scheduling
scenarios here, all of which can have a knock on effect causing
different scheduling and ordering later in the filesystem too.
Mkquashfs doesn't care about the ordering of fragments and
multi-block files within the filesystem, as this does not
affect the correctness of the filesystem.
In fact not caring about the ordering, as it doesn't matter, allows
Mksquashfs to run as fast as possible, maximising CPU and I/O
performance.
But, in the last couple of years, Squashfs has become used in
scenarios (cloud etc) where this randomness is causing problems.
Specifically this appears to be where downloaders, installers etc.
try to work out the differences between Squashfs filesystem
updates to minimise the amount of data that needs to transferred
to update an image.
Additionally, in the last couple of years has arisen the notion
of reproducible builds, that is the same source and build
environment etc should be able to (re-)generate identical
output. This is usually for verification and security, allowing
binaries/distributions to be checked for malicious activity.
See https://reproducible-builds.org/ for more information.
Mksquashfs from release 4.4 now generates reproducible images
by default. Images generated by Mksquashfs will be ordered
identically to previous runs if the same input has been supplied,
and the same options used.
1.1.1 Dealing with timestamps
Timestamps embedded in the filesystem will stiil cause differences.
Each new run of Mksquashfs will produce a different mkfs (make filesystem)
timestamp in the super-block. Moreover if any file timestamps have changed
(even if the content hasn't), this will produce a difference.
To prevent timestamps from producing differences, the following
new Mksquashfs options have been added.
1.1.2 -mkfs-time <time>
This option takes a positive time value (which is the number
of seconds since the epoch of 1970-01-01 00:00:00 UTC), and sets
the file system timestamp to that.
Squashfs uses an unsigned 32-bit integer to store time, and the
time given should be in that range.
Obviously you can use the date command to convert dates into
this value, i.e.
% mksquashfs source source.sqsh -mkfs-time $(date +%s -d "Jan 1 2019 19:00")
1.1.3 -all-time <time>
This option takes a positive time value (which is the number
of seconds since the epoch of 1970-01-01 00:00:00 UTC), and sets
the timestamp on all files to that (but not the mkfs time).
1.1.4 environment variable SOURCE_DATE_EPOCH
As an alternative to the above command line options, you can
set the environment variable SOURCE_DATE_EPOCH to a time value.
This value will be used to set the mkfs time. Also any
file timestamps which are after SOURCE_DATE_EPOCH will be
clamped to SOURCE_DATE_EPOCH.
See https://reproducible-builds.org/docs/source-date-epoch/
for more information.
Note: both SOURCE_DATE_EPOCH and the command line options cannot
be used at the same time. They are different ways to do the same thing,
and both have FORCE sematics which mean they can't be over-ridden
elsewhere (otherwise it would defeat the purpose).
1.1.5 -not-reproducible
This option tells Mksquashfs that the files do not have to be
strictly ordered. This will make Mksquashfs behave like version 4.3.
1.2 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
zstd
-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
-reproducible build images that are reproducible (default)
-not-reproducible build images that are not reproducible
-mkfs-time <time> set mkfs time to <time> which is an unsigned int
-fstime <time> synonym for mkfs-time
-all-time <time> set all inode times to <time> which is an unsigned int
-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
-noId do not compress the uid/gid table (implied by -noI)
-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
-root-mode <mode> set root directory permissions to octal <mode>
-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
Pseudo definitions should be of the format
filename d mode uid gid
filename m mode uid gid
filename b mode uid gid major minor
filename c mode uid gid major minor
filename f mode uid gid command
filename s mode uid gid symlink
-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
-quiet no verbose output
-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 4096M
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
-offset <offset> Skip <offset> bytes at the beginning of <dest>.
Optionally a suffix of K, M or G can be given to specify
Kbytes, Mbytes or Gbytes respectively.
Default 0 bytes.
-o <offset> synonym for -offset
-noInodeCompression alternative name for -noI
-noIdTableCompression alternative name for -noId
-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.
zstd
-Xcompression-level <compression-level>
<compression-level> should be 1 .. 22 (default 15)
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.
zstd
-Xcompression-level <compression-level>
<compression-level> should be 1 .. 22 (default 15)
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.
Mksquashfs 4.4 adds support for Symbolic links.
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. Creating a symbolic link
-------------------------------
Pseudo definition
Filename s mode uid gid symlink
uid and gid can be either specified as a decimal number, or by name.
Note mode is ignored, as symlinks always have "rwxrwxrwx" permissions.
For example:
symlink s 0 root root example
creates a symlink "symlink" to file "example" with root uid/gid.
3.8.5. 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.
Unsquashfs can decompress all official Squashfs filesystem versions.
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"
-q[uiet] no verbose output
-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
-lln[umeric] -lls but with numeric uids and gids
-lc list filesystem concisely, displaying only files
and empty directories. Don't unsquash
-llc list filesystem concisely with file attributes,
displaying only files and empty directories.
Don't unsquash
-o[ffset] <bytes> skip <bytes> at start of <dest>
Optionally a suffix of K, M or G can be given to specify
Kbytes, Mbytes or Gbytes respectively.
Default 0 bytes.
-f[orce] if file already exists then overwrite
-ig[nore-errors] Treat errors writing files to output as non-fatal
-st[rict-errors] Treat all errors as fatal
-s[tat] display filesystem superblock information
-UTC Use UTC rather than local time zone when displaying time
-mkfs-time display filesystem superblock time
-fstime synonym for -mkfs-time
-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
zstd
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 "-lln"
option is the same but displays uids and gids numerically.
The "-lc" option is similar to the -ls option except it only displays files
and empty directories. The -llc option displays file attributes.
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.
The -mkfs-time option displays the make filesystem time contained
in the super-block. This is displayed as the number of seconds since
the epoch of 1970-01-01 00:00:00 UTC.
The -UTC option makes Unsquashfs display all times in the UTC time zone
rather than using the default local time zone.
4.2. Dealing with errors
------------------------
Unsquashfs splits errors into two categories: fatal errors and non-fatal
errors.
Fatal errors are those which cause Unsquashfs to abort instantly.
These are generally due to failure to read the filesystem (corruption),
and/or failure to write files to the output filesystem, due to I/O error
or out of space. Generally anything which is unexpected is a fatal error.
Non-fatal errors are generally where support is lacking in the
output filesystem, and it can be considered to be an expected failure.
This includes the inability to write extended attributes (xattrs) to
a filesystem that doesn't support them, the inability to create files on
filesystem that doesn't support them (i.e. symbolic links on VFAT), and the
inability to execute privileged operations as a user-process.
The user may well know the filesystem cannot support certain operations
and would prefer Unsquashfs to ignore then without aborting.
In the past Unsquashfs was much more tolerant of errors, in this
release a significant number of errors that were non-fatal have been
hardened to fatal.
4.2.1. -ignore-errors
This makes Unsquashfs behave like previous versions, and treats more
errors as non-fatal.
4.2.2 -strict-errors
This makes Unsquashfs treat every error as fatal, and it will abort
instantly.
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@squashfs.org.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!
SQUASHFS/squashfs-tools-4.4/kernel/README
0 → 100644
View file @
05a1b863
Squashfs is now in mainline at www.kernel.org.
These files are obsolete and not updated.
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/Makefile
0 → 100644
View file @
05a1b863
#
# 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
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/inode.c
0 → 100644
View file @
05a1b863
/*
* 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"
);
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs.h
0 → 100644
View file @
05a1b863
/*
* 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
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs2_0.c
0 → 100644
View file @
05a1b863
/*
* 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
;
}
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs.h
0 → 100644
View file @
05a1b863
#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
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_i.h
0 → 100644
View file @
05a1b863
#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
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_sb.h
0 → 100644
View file @
05a1b863
#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
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/Documentation/filesystems/squashfs.txt
0 → 100644
View file @
05a1b863
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.
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/Makefile
0 → 100644
View file @
05a1b863
#
# 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
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/block.c
0 → 100644
View file @
05a1b863
/*
* 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
;
}
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/cache.c
0 → 100644
View file @
05a1b863
/*
* 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
;
}
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/dir.c
0 → 100644
View file @
05a1b863
/*
* 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
};
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/export.c
0 → 100644
View file @
05a1b863
/*
* 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
};
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/file.c
0 → 100644
View file @
05a1b863
/*
* 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
};
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/fragment.c
0 → 100644
View file @
05a1b863
/*
* 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
;
}
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/id.c
0 → 100644
View file @
05a1b863
/*
* 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
;
}
Prev
1
…
8
9
10
11
12
13
14
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment