diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-09 18:18:49 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-09 18:18:49 -0500 |
| commit | 31aeb6c815549948571eec988ad9728c27d7a68d (patch) | |
| tree | e155438be253924ebb1b792182e406947369b3eb | |
| parent | c40f6f8bbc4cbd2902671aacd587400ddca62627 (diff) | |
| parent | fc55584175589b70f4c30cb594f09f4bd6ad5d40 (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-linus
* git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-linus:
MAINTAINERS: squashfs entry
Squashfs: documentation
Squashfs: initrd support
Squashfs: Kconfig entry
Squashfs: Makefiles
Squashfs: header files
Squashfs: block operations
Squashfs: cache operations
Squashfs: uid/gid lookup operations
Squashfs: fragment block operations
Squashfs: export operations
Squashfs: super block operations
Squashfs: symlink operations
Squashfs: regular file operations
Squashfs: directory readdir operations
Squashfs: directory lookup operations
Squashfs: inode operations
| -rw-r--r-- | Documentation/filesystems/squashfs.txt | 225 | ||||
| -rw-r--r-- | MAINTAINERS | 7 | ||||
| -rw-r--r-- | fs/Kconfig | 52 | ||||
| -rw-r--r-- | fs/Makefile | 1 | ||||
| -rw-r--r-- | fs/squashfs/Makefile | 8 | ||||
| -rw-r--r-- | fs/squashfs/block.c | 274 | ||||
| -rw-r--r-- | fs/squashfs/cache.c | 412 | ||||
| -rw-r--r-- | fs/squashfs/dir.c | 235 | ||||
| -rw-r--r-- | fs/squashfs/export.c | 155 | ||||
| -rw-r--r-- | fs/squashfs/file.c | 502 | ||||
| -rw-r--r-- | fs/squashfs/fragment.c | 98 | ||||
| -rw-r--r-- | fs/squashfs/id.c | 94 | ||||
| -rw-r--r-- | fs/squashfs/inode.c | 346 | ||||
| -rw-r--r-- | fs/squashfs/namei.c | 242 | ||||
| -rw-r--r-- | fs/squashfs/squashfs.h | 90 | ||||
| -rw-r--r-- | fs/squashfs/squashfs_fs.h | 381 | ||||
| -rw-r--r-- | fs/squashfs/squashfs_fs_i.h | 45 | ||||
| -rw-r--r-- | fs/squashfs/squashfs_fs_sb.h | 76 | ||||
| -rw-r--r-- | fs/squashfs/super.c | 440 | ||||
| -rw-r--r-- | fs/squashfs/symlink.c | 118 | ||||
| -rw-r--r-- | init/do_mounts_rd.c | 14 |
21 files changed, 3815 insertions, 0 deletions
diff --git a/Documentation/filesystems/squashfs.txt b/Documentation/filesystems/squashfs.txt new file mode 100644 index 000000000000..3e79e4a7a392 --- /dev/null +++ b/Documentation/filesystems/squashfs.txt | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | SQUASHFS 4.0 FILESYSTEM | ||
| 2 | ======================= | ||
| 3 | |||
| 4 | Squashfs is a compressed read-only filesystem for Linux. | ||
| 5 | It uses zlib compression to compress files, inodes and directories. | ||
| 6 | Inodes in the system are very small and all blocks are packed to minimise | ||
| 7 | data overhead. Block sizes greater than 4K are supported up to a maximum | ||
| 8 | of 1Mbytes (default block size 128K). | ||
| 9 | |||
| 10 | Squashfs is intended for general read-only filesystem use, for archival | ||
| 11 | use (i.e. in cases where a .tar.gz file may be used), and in constrained | ||
| 12 | block device/memory systems (e.g. embedded systems) where low overhead is | ||
| 13 | needed. | ||
| 14 | |||
| 15 | Mailing list: squashfs-devel@lists.sourceforge.net | ||
| 16 | Web site: www.squashfs.org | ||
| 17 | |||
| 18 | 1. FILESYSTEM FEATURES | ||
| 19 | ---------------------- | ||
| 20 | |||
| 21 | Squashfs filesystem features versus Cramfs: | ||
| 22 | |||
| 23 | Squashfs Cramfs | ||
| 24 | |||
| 25 | Max filesystem size: 2^64 16 MiB | ||
| 26 | Max file size: ~ 2 TiB 16 MiB | ||
| 27 | Max files: unlimited unlimited | ||
| 28 | Max directories: unlimited unlimited | ||
| 29 | Max entries per directory: unlimited unlimited | ||
| 30 | Max block size: 1 MiB 4 KiB | ||
| 31 | Metadata compression: yes no | ||
| 32 | Directory indexes: yes no | ||
| 33 | Sparse file support: yes no | ||
| 34 | Tail-end packing (fragments): yes no | ||
| 35 | Exportable (NFS etc.): yes no | ||
| 36 | Hard link support: yes no | ||
| 37 | "." and ".." in readdir: yes no | ||
| 38 | Real inode numbers: yes no | ||
| 39 | 32-bit uids/gids: yes no | ||
| 40 | File creation time: yes no | ||
| 41 | Xattr and ACL support: no no | ||
| 42 | |||
| 43 | Squashfs compresses data, inodes and directories. In addition, inode and | ||
| 44 | directory data are highly compacted, and packed on byte boundaries. Each | ||
| 45 | compressed inode is on average 8 bytes in length (the exact length varies on | ||
| 46 | file type, i.e. regular file, directory, symbolic link, and block/char device | ||
| 47 | inodes have different sizes). | ||
| 48 | |||
| 49 | 2. USING SQUASHFS | ||
| 50 | ----------------- | ||
| 51 | |||
| 52 | As squashfs is a read-only filesystem, the mksquashfs program must be used to | ||
| 53 | create populated squashfs filesystems. This and other squashfs utilities | ||
| 54 | can be obtained from http://www.squashfs.org. Usage instructions can be | ||
| 55 | obtained from this site also. | ||
| 56 | |||
| 57 | |||
| 58 | 3. SQUASHFS FILESYSTEM DESIGN | ||
| 59 | ----------------------------- | ||
| 60 | |||
| 61 | A squashfs filesystem consists of seven parts, packed together on a byte | ||
| 62 | alignment: | ||
| 63 | |||
| 64 | --------------- | ||
| 65 | | superblock | | ||
| 66 | |---------------| | ||
| 67 | | datablocks | | ||
| 68 | | & fragments | | ||
| 69 | |---------------| | ||
| 70 | | inode table | | ||
| 71 | |---------------| | ||
| 72 | | directory | | ||
| 73 | | table | | ||
| 74 | |---------------| | ||
| 75 | | fragment | | ||
| 76 | | table | | ||
| 77 | |---------------| | ||
| 78 | | export | | ||
| 79 | | table | | ||
| 80 | |---------------| | ||
| 81 | | uid/gid | | ||
| 82 | | lookup table | | ||
| 83 | --------------- | ||
| 84 | |||
| 85 | Compressed data blocks are written to the filesystem as files are read from | ||
| 86 | the source directory, and checked for duplicates. Once all file data has been | ||
| 87 | written the completed inode, directory, fragment, export and uid/gid lookup | ||
| 88 | tables are written. | ||
| 89 | |||
| 90 | 3.1 Inodes | ||
| 91 | ---------- | ||
| 92 | |||
| 93 | Metadata (inodes and directories) are compressed in 8Kbyte blocks. Each | ||
| 94 | compressed block is prefixed by a two byte length, the top bit is set if the | ||
| 95 | block is uncompressed. A block will be uncompressed if the -noI option is set, | ||
| 96 | or if the compressed block was larger than the uncompressed block. | ||
| 97 | |||
| 98 | Inodes are packed into the metadata blocks, and are not aligned to block | ||
| 99 | boundaries, therefore inodes overlap compressed blocks. Inodes are identified | ||
| 100 | by a 48-bit number which encodes the location of the compressed metadata block | ||
| 101 | containing the inode, and the byte offset into that block where the inode is | ||
| 102 | placed (<block, offset>). | ||
| 103 | |||
| 104 | To maximise compression there are different inodes for each file type | ||
| 105 | (regular file, directory, device, etc.), the inode contents and length | ||
| 106 | varying with the type. | ||
| 107 | |||
| 108 | To further maximise compression, two types of regular file inode and | ||
| 109 | directory inode are defined: inodes optimised for frequently occurring | ||
| 110 | regular files and directories, and extended types where extra | ||
| 111 | information has to be stored. | ||
| 112 | |||
| 113 | 3.2 Directories | ||
| 114 | --------------- | ||
| 115 | |||
| 116 | Like inodes, directories are packed into compressed metadata blocks, stored | ||
| 117 | in a directory table. Directories are accessed using the start address of | ||
| 118 | the metablock containing the directory and the offset into the | ||
| 119 | decompressed block (<block, offset>). | ||
| 120 | |||
| 121 | Directories are organised in a slightly complex way, and are not simply | ||
| 122 | a list of file names. The organisation takes advantage of the | ||
| 123 | fact that (in most cases) the inodes of the files will be in the same | ||
| 124 | compressed metadata block, and therefore, can share the start block. | ||
| 125 | Directories are therefore organised in a two level list, a directory | ||
| 126 | header containing the shared start block value, and a sequence of directory | ||
| 127 | entries, each of which share the shared start block. A new directory header | ||
| 128 | is written once/if the inode start block changes. The directory | ||
| 129 | header/directory entry list is repeated as many times as necessary. | ||
| 130 | |||
| 131 | Directories are sorted, and can contain a directory index to speed up | ||
| 132 | file lookup. Directory indexes store one entry per metablock, each entry | ||
| 133 | storing the index/filename mapping to the first directory header | ||
| 134 | in each metadata block. Directories are sorted in alphabetical order, | ||
| 135 | and at lookup the index is scanned linearly looking for the first filename | ||
| 136 | alphabetically larger than the filename being looked up. At this point the | ||
| 137 | location of the metadata block the filename is in has been found. | ||
| 138 | The general idea of the index is ensure only one metadata block needs to be | ||
| 139 | decompressed to do a lookup irrespective of the length of the directory. | ||
| 140 | This scheme has the advantage that it doesn't require extra memory overhead | ||
| 141 | and doesn't require much extra storage on disk. | ||
| 142 | |||
| 143 | 3.3 File data | ||
| 144 | ------------- | ||
| 145 | |||
| 146 | Regular files consist of a sequence of contiguous compressed blocks, and/or a | ||
| 147 | compressed fragment block (tail-end packed block). The compressed size | ||
| 148 | of each datablock is stored in a block list contained within the | ||
| 149 | file inode. | ||
| 150 | |||
| 151 | To speed up access to datablocks when reading 'large' files (256 Mbytes or | ||
| 152 | larger), the code implements an index cache that caches the mapping from | ||
| 153 | block index to datablock location on disk. | ||
| 154 | |||
| 155 | The index cache allows Squashfs to handle large files (up to 1.75 TiB) while | ||
| 156 | retaining a simple and space-efficient block list on disk. The cache | ||
| 157 | is split into slots, caching up to eight 224 GiB files (128 KiB blocks). | ||
| 158 | Larger files use multiple slots, with 1.75 TiB files using all 8 slots. | ||
| 159 | The index cache is designed to be memory efficient, and by default uses | ||
| 160 | 16 KiB. | ||
| 161 | |||
| 162 | 3.4 Fragment lookup table | ||
| 163 | ------------------------- | ||
| 164 | |||
| 165 | Regular files can contain a fragment index which is mapped to a fragment | ||
| 166 | location on disk and compressed size using a fragment lookup table. This | ||
| 167 | fragment lookup table is itself stored compressed into metadata blocks. | ||
| 168 | A second index table is used to locate these. This second index table for | ||
| 169 | speed of access (and because it is small) is read at mount time and cached | ||
| 170 | in memory. | ||
| 171 | |||
| 172 | 3.5 Uid/gid lookup table | ||
| 173 | ------------------------ | ||
| 174 | |||
| 175 | For space efficiency regular files store uid and gid indexes, which are | ||
| 176 | converted to 32-bit uids/gids using an id look up table. This table is | ||
| 177 | stored compressed into metadata blocks. A second index table is used to | ||
| 178 | locate these. This second index table for speed of access (and because it | ||
| 179 | is small) is read at mount time and cached in memory. | ||
| 180 | |||
| 181 | 3.6 Export table | ||
| 182 | ---------------- | ||
| 183 | |||
| 184 | To enable Squashfs filesystems to be exportable (via NFS etc.) filesystems | ||
| 185 | can optionally (disabled with the -no-exports Mksquashfs option) contain | ||
| 186 | an inode number to inode disk location lookup table. This is required to | ||
| 187 | enable Squashfs to map inode numbers passed in filehandles to the inode | ||
| 188 | location on disk, which is necessary when the export code reinstantiates | ||
| 189 | expired/flushed inodes. | ||
| 190 | |||
| 191 | This table is stored compressed into metadata blocks. A second index table is | ||
| 192 | used to locate these. This second index table for speed of access (and because | ||
| 193 | it is small) is read at mount time and cached in memory. | ||
| 194 | |||
| 195 | |||
| 196 | 4. TODOS AND OUTSTANDING ISSUES | ||
| 197 | ------------------------------- | ||
| 198 | |||
| 199 | 4.1 Todo list | ||
| 200 | ------------- | ||
| 201 | |||
| 202 | Implement Xattr and ACL support. The Squashfs 4.0 filesystem layout has hooks | ||
| 203 | for these but the code has not been written. Once the code has been written | ||
| 204 | the existing layout should not require modification. | ||
| 205 | |||
| 206 | 4.2 Squashfs internal cache | ||
| 207 | --------------------------- | ||
| 208 | |||
| 209 | Blocks in Squashfs are compressed. To avoid repeatedly decompressing | ||
| 210 | recently accessed data Squashfs uses two small metadata and fragment caches. | ||
| 211 | |||
| 212 | The cache is not used for file datablocks, these are decompressed and cached in | ||
| 213 | the page-cache in the normal way. The cache is used to temporarily cache | ||
| 214 | fragment and metadata blocks which have been read as a result of a metadata | ||
| 215 | (i.e. inode or directory) or fragment access. Because metadata and fragments | ||
| 216 | are packed together into blocks (to gain greater compression) the read of a | ||
| 217 | particular piece of metadata or fragment will retrieve other metadata/fragments | ||
| 218 | which have been packed with it, these because of locality-of-reference may be | ||
| 219 | read in the near future. Temporarily caching them ensures they are available | ||
| 220 | for near future access without requiring an additional read and decompress. | ||
| 221 | |||
| 222 | In the future this internal cache may be replaced with an implementation which | ||
| 223 | uses the kernel page cache. Because the page cache operates on page sized | ||
| 224 | units this may introduce additional complexity in terms of locking and | ||
| 225 | associated race conditions. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 57e0309243cc..6f65a269cb17 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -4081,6 +4081,13 @@ L: cbe-oss-dev@ozlabs.org | |||
| 4081 | W: http://www.ibm.com/developerworks/power/cell/ | 4081 | W: http://www.ibm.com/developerworks/power/cell/ |
| 4082 | S: Supported | 4082 | S: Supported |
| 4083 | 4083 | ||
| 4084 | SQUASHFS FILE SYSTEM | ||
| 4085 | P: Phillip Lougher | ||
| 4086 | M: phillip@lougher.demon.co.uk | ||
| 4087 | L: squashfs-devel@lists.sourceforge.net (subscribers-only) | ||
| 4088 | W: http://squashfs.org.uk | ||
| 4089 | S: Maintained | ||
| 4090 | |||
| 4084 | SRM (Alpha) environment access | 4091 | SRM (Alpha) environment access |
| 4085 | P: Jan-Benedict Glaw | 4092 | P: Jan-Benedict Glaw |
| 4086 | M: jbglaw@lug-owl.de | 4093 | M: jbglaw@lug-owl.de |
diff --git a/fs/Kconfig b/fs/Kconfig index 02cff86af1b4..51307b0fdf0f 100644 --- a/fs/Kconfig +++ b/fs/Kconfig | |||
| @@ -932,6 +932,58 @@ config CRAMFS | |||
| 932 | 932 | ||
| 933 | If unsure, say N. | 933 | If unsure, say N. |
| 934 | 934 | ||
| 935 | config SQUASHFS | ||
| 936 | tristate "SquashFS 4.0 - Squashed file system support" | ||
| 937 | depends on BLOCK | ||
| 938 | select ZLIB_INFLATE | ||
| 939 | help | ||
| 940 | Saying Y here includes support for SquashFS 4.0 (a Compressed | ||
| 941 | Read-Only File System). Squashfs is a highly compressed read-only | ||
| 942 | filesystem for Linux. It uses zlib compression to compress both | ||
| 943 | files, inodes and directories. Inodes in the system are very small | ||
| 944 | and all blocks are packed to minimise data overhead. Block sizes | ||
| 945 | greater than 4K are supported up to a maximum of 1 Mbytes (default | ||
| 946 | block size 128K). SquashFS 4.0 supports 64 bit filesystems and files | ||
| 947 | (larger than 4GB), full uid/gid information, hard links and | ||
| 948 | timestamps. | ||
| 949 | |||
| 950 | Squashfs is intended for general read-only filesystem use, for | ||
| 951 | archival use (i.e. in cases where a .tar.gz file may be used), and in | ||
| 952 | embedded systems where low overhead is needed. Further information | ||
| 953 | and tools are available from http://squashfs.sourceforge.net. | ||
| 954 | |||
| 955 | If you want to compile this as a module ( = code which can be | ||
| 956 | inserted in and removed from the running kernel whenever you want), | ||
| 957 | say M here and read <file:Documentation/modules.txt>. The module | ||
| 958 | will be called squashfs. Note that the root file system (the one | ||
| 959 | containing the directory /) cannot be compiled as a module. | ||
| 960 | |||
| 961 | If unsure, say N. | ||
| 962 | |||
| 963 | config SQUASHFS_EMBEDDED | ||
| 964 | |||
| 965 | bool "Additional option for memory-constrained systems" | ||
| 966 | depends on SQUASHFS | ||
| 967 | default n | ||
| 968 | help | ||
| 969 | Saying Y here allows you to specify cache size. | ||
| 970 | |||
| 971 | If unsure, say N. | ||
| 972 | |||
| 973 | config SQUASHFS_FRAGMENT_CACHE_SIZE | ||
| 974 | int "Number of fragments cached" if SQUASHFS_EMBEDDED | ||
| 975 | depends on SQUASHFS | ||
| 976 | default "3" | ||
| 977 | help | ||
| 978 | By default SquashFS caches the last 3 fragments read from | ||
| 979 | the filesystem. Increasing this amount may mean SquashFS | ||
| 980 | has to re-read fragments less often from disk, at the expense | ||
| 981 | of extra system memory. Decreasing this amount will mean | ||
| 982 | SquashFS uses less memory at the expense of extra reads from disk. | ||
| 983 | |||
| 984 | Note there must be at least one cached fragment. Anything | ||
| 985 | much more than three will probably not make much difference. | ||
| 986 | |||
| 935 | config VXFS_FS | 987 | config VXFS_FS |
| 936 | tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" | 988 | tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" |
| 937 | depends on BLOCK | 989 | depends on BLOCK |
diff --git a/fs/Makefile b/fs/Makefile index bc4e14df1082..38bc735c67ad 100644 --- a/fs/Makefile +++ b/fs/Makefile | |||
| @@ -74,6 +74,7 @@ obj-$(CONFIG_JBD) += jbd/ | |||
| 74 | obj-$(CONFIG_JBD2) += jbd2/ | 74 | obj-$(CONFIG_JBD2) += jbd2/ |
| 75 | obj-$(CONFIG_EXT2_FS) += ext2/ | 75 | obj-$(CONFIG_EXT2_FS) += ext2/ |
| 76 | obj-$(CONFIG_CRAMFS) += cramfs/ | 76 | obj-$(CONFIG_CRAMFS) += cramfs/ |
| 77 | obj-$(CONFIG_SQUASHFS) += squashfs/ | ||
| 77 | obj-y += ramfs/ | 78 | obj-y += ramfs/ |
| 78 | obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ | 79 | obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ |
| 79 | obj-$(CONFIG_CODA_FS) += coda/ | 80 | obj-$(CONFIG_CODA_FS) += coda/ |
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile new file mode 100644 index 000000000000..8258cf9a0317 --- /dev/null +++ b/fs/squashfs/Makefile | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the linux squashfs routines. | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_SQUASHFS) += squashfs.o | ||
| 6 | squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o | ||
| 7 | squashfs-y += namei.o super.o symlink.o | ||
| 8 | #squashfs-y += squashfs2_0.o | ||
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c new file mode 100644 index 000000000000..c837dfc2b3c6 --- /dev/null +++ b/fs/squashfs/block.c | |||
| @@ -0,0 +1,274 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * block.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This file implements the low-level routines to read and decompress | ||
| 26 | * datablocks and metadata blocks. | ||
| 27 | */ | ||
| 28 | |||
| 29 | #include <linux/fs.h> | ||
| 30 | #include <linux/vfs.h> | ||
| 31 | #include <linux/slab.h> | ||
| 32 | #include <linux/mutex.h> | ||
| 33 | #include <linux/string.h> | ||
| 34 | #include <linux/buffer_head.h> | ||
| 35 | #include <linux/zlib.h> | ||
| 36 | |||
| 37 | #include "squashfs_fs.h" | ||
| 38 | #include "squashfs_fs_sb.h" | ||
| 39 | #include "squashfs_fs_i.h" | ||
| 40 | #include "squashfs.h" | ||
| 41 | |||
| 42 | /* | ||
| 43 | * Read the metadata block length, this is stored in the first two | ||
| 44 | * bytes of the metadata block. | ||
| 45 | */ | ||
| 46 | static struct buffer_head *get_block_length(struct super_block *sb, | ||
| 47 | u64 *cur_index, int *offset, int *length) | ||
| 48 | { | ||
| 49 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 50 | struct buffer_head *bh; | ||
| 51 | |||
| 52 | bh = sb_bread(sb, *cur_index); | ||
| 53 | if (bh == NULL) | ||
| 54 | return NULL; | ||
| 55 | |||
| 56 | if (msblk->devblksize - *offset == 1) { | ||
| 57 | *length = (unsigned char) bh->b_data[*offset]; | ||
| 58 | put_bh(bh); | ||
| 59 | bh = sb_bread(sb, ++(*cur_index)); | ||
| 60 | if (bh == NULL) | ||
| 61 | return NULL; | ||
| 62 | *length |= (unsigned char) bh->b_data[0] << 8; | ||
| 63 | *offset = 1; | ||
| 64 | } else { | ||
| 65 | *length = (unsigned char) bh->b_data[*offset] | | ||
| 66 | (unsigned char) bh->b_data[*offset + 1] << 8; | ||
| 67 | *offset += 2; | ||
| 68 | } | ||
| 69 | |||
| 70 | return bh; | ||
| 71 | } | ||
| 72 | |||
| 73 | |||
| 74 | /* | ||
| 75 | * Read and decompress a metadata block or datablock. Length is non-zero | ||
| 76 | * if a datablock is being read (the size is stored elsewhere in the | ||
| 77 | * filesystem), otherwise the length is obtained from the first two bytes of | ||
| 78 | * the metadata block. A bit in the length field indicates if the block | ||
| 79 | * is stored uncompressed in the filesystem (usually because compression | ||
| 80 | * generated a larger block - this does occasionally happen with zlib). | ||
| 81 | */ | ||
| 82 | int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | ||
| 83 | int length, u64 *next_index, int srclength) | ||
| 84 | { | ||
| 85 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 86 | struct buffer_head **bh; | ||
| 87 | int offset = index & ((1 << msblk->devblksize_log2) - 1); | ||
| 88 | u64 cur_index = index >> msblk->devblksize_log2; | ||
| 89 | int bytes, compressed, b = 0, k = 0, page = 0, avail; | ||
| 90 | |||
| 91 | |||
| 92 | bh = kcalloc((msblk->block_size >> msblk->devblksize_log2) + 1, | ||
| 93 | sizeof(*bh), GFP_KERNEL); | ||
| 94 | if (bh == NULL) | ||
| 95 | return -ENOMEM; | ||
| 96 | |||
| 97 | if (length) { | ||
| 98 | /* | ||
| 99 | * Datablock. | ||
| 100 | */ | ||
| 101 | bytes = -offset; | ||
| 102 | compressed = SQUASHFS_COMPRESSED_BLOCK(length); | ||
| 103 | length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); | ||
| 104 | if (next_index) | ||
| 105 | *next_index = index + length; | ||
| 106 | |||
| 107 | TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", | ||
| 108 | index, compressed ? "" : "un", length, srclength); | ||
| 109 | |||
| 110 | if (length < 0 || length > srclength || | ||
| 111 | (index + length) > msblk->bytes_used) | ||
| 112 | goto read_failure; | ||
| 113 | |||
| 114 | for (b = 0; bytes < length; b++, cur_index++) { | ||
| 115 | bh[b] = sb_getblk(sb, cur_index); | ||
| 116 | if (bh[b] == NULL) | ||
| 117 | goto block_release; | ||
| 118 | bytes += msblk->devblksize; | ||
| 119 | } | ||
| 120 | ll_rw_block(READ, b, bh); | ||
| 121 | } else { | ||
| 122 | /* | ||
| 123 | * Metadata block. | ||
| 124 | */ | ||
| 125 | if ((index + 2) > msblk->bytes_used) | ||
| 126 | goto read_failure; | ||
| 127 | |||
| 128 | bh[0] = get_block_length(sb, &cur_index, &offset, &length); | ||
| 129 | if (bh[0] == NULL) | ||
| 130 | goto read_failure; | ||
| 131 | b = 1; | ||
| 132 | |||
| 133 | bytes = msblk->devblksize - offset; | ||
| 134 | compressed = SQUASHFS_COMPRESSED(length); | ||
| 135 | length = SQUASHFS_COMPRESSED_SIZE(length); | ||
| 136 | if (next_index) | ||
| 137 | *next_index = index + length + 2; | ||
| 138 | |||
| 139 | TRACE("Block @ 0x%llx, %scompressed size %d\n", index, | ||
| 140 | compressed ? "" : "un", length); | ||
| 141 | |||
| 142 | if (length < 0 || length > srclength || | ||
| 143 | (index + length) > msblk->bytes_used) | ||
| 144 | goto block_release; | ||
| 145 | |||
| 146 | for (; bytes < length; b++) { | ||
| 147 | bh[b] = sb_getblk(sb, ++cur_index); | ||
| 148 | if (bh[b] == NULL) | ||
| 149 | goto block_release; | ||
| 150 | bytes += msblk->devblksize; | ||
| 151 | } | ||
| 152 | ll_rw_block(READ, b - 1, bh + 1); | ||
| 153 | } | ||
| 154 | |||
| 155 | if (compressed) { | ||
| 156 | int zlib_err = 0, zlib_init = 0; | ||
| 157 | |||
| 158 | /* | ||
| 159 | * Uncompress block. | ||
| 160 | */ | ||
| 161 | |||
| 162 | mutex_lock(&msblk->read_data_mutex); | ||
| 163 | |||
| 164 | msblk->stream.avail_out = 0; | ||
| 165 | msblk->stream.avail_in = 0; | ||
| 166 | |||
| 167 | bytes = length; | ||
| 168 | do { | ||
| 169 | if (msblk->stream.avail_in == 0 && k < b) { | ||
| 170 | avail = min(bytes, msblk->devblksize - offset); | ||
| 171 | bytes -= avail; | ||
| 172 | wait_on_buffer(bh[k]); | ||
| 173 | if (!buffer_uptodate(bh[k])) | ||
| 174 | goto release_mutex; | ||
| 175 | |||
| 176 | if (avail == 0) { | ||
| 177 | offset = 0; | ||
| 178 | put_bh(bh[k++]); | ||
| 179 | continue; | ||
| 180 | } | ||
| 181 | |||
| 182 | msblk->stream.next_in = bh[k]->b_data + offset; | ||
| 183 | msblk->stream.avail_in = avail; | ||
| 184 | offset = 0; | ||
| 185 | } | ||
| 186 | |||
| 187 | if (msblk->stream.avail_out == 0) { | ||
| 188 | msblk->stream.next_out = buffer[page++]; | ||
| 189 | msblk->stream.avail_out = PAGE_CACHE_SIZE; | ||
| 190 | } | ||
| 191 | |||
| 192 | if (!zlib_init) { | ||
| 193 | zlib_err = zlib_inflateInit(&msblk->stream); | ||
| 194 | if (zlib_err != Z_OK) { | ||
| 195 | ERROR("zlib_inflateInit returned" | ||
| 196 | " unexpected result 0x%x," | ||
| 197 | " srclength %d\n", zlib_err, | ||
| 198 | srclength); | ||
| 199 | goto release_mutex; | ||
| 200 | } | ||
| 201 | zlib_init = 1; | ||
| 202 | } | ||
| 203 | |||
| 204 | zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH); | ||
| 205 | |||
| 206 | if (msblk->stream.avail_in == 0 && k < b) | ||
| 207 | put_bh(bh[k++]); | ||
| 208 | } while (zlib_err == Z_OK); | ||
| 209 | |||
| 210 | if (zlib_err != Z_STREAM_END) { | ||
| 211 | ERROR("zlib_inflate returned unexpected result" | ||
| 212 | " 0x%x, srclength %d, avail_in %d," | ||
| 213 | " avail_out %d\n", zlib_err, srclength, | ||
| 214 | msblk->stream.avail_in, | ||
| 215 | msblk->stream.avail_out); | ||
| 216 | goto release_mutex; | ||
| 217 | } | ||
| 218 | |||
| 219 | zlib_err = zlib_inflateEnd(&msblk->stream); | ||
| 220 | if (zlib_err != Z_OK) { | ||
| 221 | ERROR("zlib_inflateEnd returned unexpected result 0x%x," | ||
| 222 | " srclength %d\n", zlib_err, srclength); | ||
| 223 | goto release_mutex; | ||
| 224 | } | ||
| 225 | length = msblk->stream.total_out; | ||
| 226 | mutex_unlock(&msblk->read_data_mutex); | ||
| 227 | } else { | ||
| 228 | /* | ||
| 229 | * Block is uncompressed. | ||
| 230 | */ | ||
| 231 | int i, in, pg_offset = 0; | ||
| 232 | |||
| 233 | for (i = 0; i < b; i++) { | ||
| 234 | wait_on_buffer(bh[i]); | ||
| 235 | if (!buffer_uptodate(bh[i])) | ||
| 236 | goto block_release; | ||
| 237 | } | ||
| 238 | |||
| 239 | for (bytes = length; k < b; k++) { | ||
| 240 | in = min(bytes, msblk->devblksize - offset); | ||
| 241 | bytes -= in; | ||
| 242 | while (in) { | ||
| 243 | if (pg_offset == PAGE_CACHE_SIZE) { | ||
| 244 | page++; | ||
| 245 | pg_offset = 0; | ||
| 246 | } | ||
| 247 | avail = min_t(int, in, PAGE_CACHE_SIZE - | ||
| 248 | pg_offset); | ||
| 249 | memcpy(buffer[page] + pg_offset, | ||
| 250 | bh[k]->b_data + offset, avail); | ||
| 251 | in -= avail; | ||
| 252 | pg_offset += avail; | ||
| 253 | offset += avail; | ||
| 254 | } | ||
| 255 | offset = 0; | ||
| 256 | put_bh(bh[k]); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | kfree(bh); | ||
| 261 | return length; | ||
| 262 | |||
| 263 | release_mutex: | ||
| 264 | mutex_unlock(&msblk->read_data_mutex); | ||
| 265 | |||
| 266 | block_release: | ||
| 267 | for (; k < b; k++) | ||
| 268 | put_bh(bh[k]); | ||
| 269 | |||
| 270 | read_failure: | ||
| 271 | ERROR("sb_bread failed reading block 0x%llx\n", cur_index); | ||
| 272 | kfree(bh); | ||
| 273 | return -EIO; | ||
| 274 | } | ||
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c new file mode 100644 index 000000000000..f29eda16d25e --- /dev/null +++ b/fs/squashfs/cache.c | |||
| @@ -0,0 +1,412 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * cache.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * Blocks in Squashfs are compressed. To avoid repeatedly decompressing | ||
| 26 | * recently accessed data Squashfs uses two small metadata and fragment caches. | ||
| 27 | * | ||
| 28 | * This file implements a generic cache implementation used for both caches, | ||
| 29 | * plus functions layered ontop of the generic cache implementation to | ||
| 30 | * access the metadata and fragment caches. | ||
| 31 | * | ||
| 32 | * To avoid out of memory and fragmentation isssues with vmalloc the cache | ||
| 33 | * uses sequences of kmalloced PAGE_CACHE_SIZE buffers. | ||
| 34 | * | ||
| 35 | * It should be noted that the cache is not used for file datablocks, these | ||
| 36 | * are decompressed and cached in the page-cache in the normal way. The | ||
| 37 | * cache is only used to temporarily cache fragment and metadata blocks | ||
| 38 | * which have been read as as a result of a metadata (i.e. inode or | ||
| 39 | * directory) or fragment access. Because metadata and fragments are packed | ||
| 40 | * together into blocks (to gain greater compression) the read of a particular | ||
| 41 | * piece of metadata or fragment will retrieve other metadata/fragments which | ||
| 42 | * have been packed with it, these because of locality-of-reference may be read | ||
| 43 | * in the near future. Temporarily caching them ensures they are available for | ||
| 44 | * near future access without requiring an additional read and decompress. | ||
| 45 | */ | ||
| 46 | |||
| 47 | #include <linux/fs.h> | ||
| 48 | #include <linux/vfs.h> | ||
| 49 | #include <linux/slab.h> | ||
| 50 | #include <linux/vmalloc.h> | ||
| 51 | #include <linux/sched.h> | ||
| 52 | #include <linux/spinlock.h> | ||
| 53 | #include <linux/wait.h> | ||
| 54 | #include <linux/zlib.h> | ||
| 55 | #include <linux/pagemap.h> | ||
| 56 | |||
| 57 | #include "squashfs_fs.h" | ||
| 58 | #include "squashfs_fs_sb.h" | ||
| 59 | #include "squashfs_fs_i.h" | ||
| 60 | #include "squashfs.h" | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Look-up block in cache, and increment usage count. If not in cache, read | ||
| 64 | * and decompress it from disk. | ||
| 65 | */ | ||
| 66 | struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb, | ||
| 67 | struct squashfs_cache *cache, u64 block, int length) | ||
| 68 | { | ||
| 69 | int i, n; | ||
| 70 | struct squashfs_cache_entry *entry; | ||
| 71 | |||
| 72 | spin_lock(&cache->lock); | ||
| 73 | |||
| 74 | while (1) { | ||
| 75 | for (i = 0; i < cache->entries; i++) | ||
| 76 | if (cache->entry[i].block == block) | ||
| 77 | break; | ||
| 78 | |||
| 79 | if (i == cache->entries) { | ||
| 80 | /* | ||
| 81 | * Block not in cache, if all cache entries are used | ||
| 82 | * go to sleep waiting for one to become available. | ||
| 83 | */ | ||
| 84 | if (cache->unused == 0) { | ||
| 85 | cache->num_waiters++; | ||
| 86 | spin_unlock(&cache->lock); | ||
| 87 | wait_event(cache->wait_queue, cache->unused); | ||
| 88 | spin_lock(&cache->lock); | ||
| 89 | cache->num_waiters--; | ||
| 90 | continue; | ||
| 91 | } | ||
| 92 | |||
| 93 | /* | ||
| 94 | * At least one unused cache entry. A simple | ||
| 95 | * round-robin strategy is used to choose the entry to | ||
| 96 | * be evicted from the cache. | ||
| 97 | */ | ||
| 98 | i = cache->next_blk; | ||
| 99 | for (n = 0; n < cache->entries; n++) { | ||
| 100 | if (cache->entry[i].refcount == 0) | ||
| 101 | break; | ||
| 102 | i = (i + 1) % cache->entries; | ||
| 103 | } | ||
| 104 | |||
| 105 | cache->next_blk = (i + 1) % cache->entries; | ||
| 106 | entry = &cache->entry[i]; | ||
| 107 | |||
| 108 | /* | ||
| 109 | * Initialise choosen cache entry, and fill it in from | ||
| 110 | * disk. | ||
| 111 | */ | ||
| 112 | cache->unused--; | ||
| 113 | entry->block = block; | ||
| 114 | entry->refcount = 1; | ||
| 115 | entry->pending = 1; | ||
| 116 | entry->num_waiters = 0; | ||
| 117 | entry->error = 0; | ||
| 118 | spin_unlock(&cache->lock); | ||
| 119 | |||
| 120 | entry->length = squashfs_read_data(sb, entry->data, | ||
| 121 | block, length, &entry->next_index, | ||
| 122 | cache->block_size); | ||
| 123 | |||
| 124 | spin_lock(&cache->lock); | ||
| 125 | |||
| 126 | if (entry->length < 0) | ||
| 127 | entry->error = entry->length; | ||
| 128 | |||
| 129 | entry->pending = 0; | ||
| 130 | |||
| 131 | /* | ||
| 132 | * While filling this entry one or more other processes | ||
| 133 | * have looked it up in the cache, and have slept | ||
| 134 | * waiting for it to become available. | ||
| 135 | */ | ||
| 136 | if (entry->num_waiters) { | ||
| 137 | spin_unlock(&cache->lock); | ||
| 138 | wake_up_all(&entry->wait_queue); | ||
| 139 | } else | ||
| 140 | spin_unlock(&cache->lock); | ||
| 141 | |||
| 142 | goto out; | ||
| 143 | } | ||
| 144 | |||
| 145 | /* | ||
| 146 | * Block already in cache. Increment refcount so it doesn't | ||
| 147 | * get reused until we're finished with it, if it was | ||
| 148 | * previously unused there's one less cache entry available | ||
| 149 | * for reuse. | ||
| 150 | */ | ||
| 151 | entry = &cache->entry[i]; | ||
| 152 | if (entry->refcount == 0) | ||
| 153 | cache->unused--; | ||
| 154 | entry->refcount++; | ||
| 155 | |||
| 156 | /* | ||
| 157 | * If the entry is currently being filled in by another process | ||
| 158 | * go to sleep waiting for it to become available. | ||
| 159 | */ | ||
| 160 | if (entry->pending) { | ||
| 161 | entry->num_waiters++; | ||
| 162 | spin_unlock(&cache->lock); | ||
| 163 | wait_event(entry->wait_queue, !entry->pending); | ||
| 164 | } else | ||
| 165 | spin_unlock(&cache->lock); | ||
| 166 | |||
| 167 | goto out; | ||
| 168 | } | ||
| 169 | |||
| 170 | out: | ||
| 171 | TRACE("Got %s %d, start block %lld, refcount %d, error %d\n", | ||
| 172 | cache->name, i, entry->block, entry->refcount, entry->error); | ||
| 173 | |||
| 174 | if (entry->error) | ||
| 175 | ERROR("Unable to read %s cache entry [%llx]\n", cache->name, | ||
| 176 | block); | ||
| 177 | return entry; | ||
| 178 | } | ||
| 179 | |||
| 180 | |||
| 181 | /* | ||
| 182 | * Release cache entry, once usage count is zero it can be reused. | ||
| 183 | */ | ||
| 184 | void squashfs_cache_put(struct squashfs_cache_entry *entry) | ||
| 185 | { | ||
| 186 | struct squashfs_cache *cache = entry->cache; | ||
| 187 | |||
| 188 | spin_lock(&cache->lock); | ||
| 189 | entry->refcount--; | ||
| 190 | if (entry->refcount == 0) { | ||
| 191 | cache->unused++; | ||
| 192 | /* | ||
| 193 | * If there's any processes waiting for a block to become | ||
| 194 | * available, wake one up. | ||
| 195 | */ | ||
| 196 | if (cache->num_waiters) { | ||
| 197 | spin_unlock(&cache->lock); | ||
| 198 | wake_up(&cache->wait_queue); | ||
| 199 | return; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | spin_unlock(&cache->lock); | ||
| 203 | } | ||
| 204 | |||
| 205 | /* | ||
| 206 | * Delete cache reclaiming all kmalloced buffers. | ||
| 207 | */ | ||
| 208 | void squashfs_cache_delete(struct squashfs_cache *cache) | ||
| 209 | { | ||
| 210 | int i, j; | ||
| 211 | |||
| 212 | if (cache == NULL) | ||
| 213 | return; | ||
| 214 | |||
| 215 | for (i = 0; i < cache->entries; i++) { | ||
| 216 | if (cache->entry[i].data) { | ||
| 217 | for (j = 0; j < cache->pages; j++) | ||
| 218 | kfree(cache->entry[i].data[j]); | ||
| 219 | kfree(cache->entry[i].data); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | kfree(cache->entry); | ||
| 224 | kfree(cache); | ||
| 225 | } | ||
| 226 | |||
| 227 | |||
| 228 | /* | ||
| 229 | * Initialise cache allocating the specified number of entries, each of | ||
| 230 | * size block_size. To avoid vmalloc fragmentation issues each entry | ||
| 231 | * is allocated as a sequence of kmalloced PAGE_CACHE_SIZE buffers. | ||
| 232 | */ | ||
| 233 | struct squashfs_cache *squashfs_cache_init(char *name, int entries, | ||
| 234 | int block_size) | ||
| 235 | { | ||
| 236 | int i, j; | ||
| 237 | struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL); | ||
| 238 | |||
| 239 | if (cache == NULL) { | ||
| 240 | ERROR("Failed to allocate %s cache\n", name); | ||
| 241 | return NULL; | ||
| 242 | } | ||
| 243 | |||
| 244 | cache->entry = kcalloc(entries, sizeof(*(cache->entry)), GFP_KERNEL); | ||
| 245 | if (cache->entry == NULL) { | ||
| 246 | ERROR("Failed to allocate %s cache\n", name); | ||
| 247 | goto cleanup; | ||
| 248 | } | ||
| 249 | |||
| 250 | cache->next_blk = 0; | ||
| 251 | cache->unused = entries; | ||
| 252 | cache->entries = entries; | ||
| 253 | cache->block_size = block_size; | ||
| 254 | cache->pages = block_size >> PAGE_CACHE_SHIFT; | ||
| 255 | cache->name = name; | ||
| 256 | cache->num_waiters = 0; | ||
| 257 | spin_lock_init(&cache->lock); | ||
| 258 | init_waitqueue_head(&cache->wait_queue); | ||
| 259 | |||
| 260 | for (i = 0; i < entries; i++) { | ||
| 261 | struct squashfs_cache_entry *entry = &cache->entry[i]; | ||
| 262 | |||
| 263 | init_waitqueue_head(&cache->entry[i].wait_queue); | ||
| 264 | entry->cache = cache; | ||
| 265 | entry->block = SQUASHFS_INVALID_BLK; | ||
| 266 | entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL); | ||
| 267 | if (entry->data == NULL) { | ||
| 268 | ERROR("Failed to allocate %s cache entry\n", name); | ||
| 269 | goto cleanup; | ||
| 270 | } | ||
| 271 | |||
| 272 | for (j = 0; j < cache->pages; j++) { | ||
| 273 | entry->data[j] = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); | ||
| 274 | if (entry->data[j] == NULL) { | ||
| 275 | ERROR("Failed to allocate %s buffer\n", name); | ||
| 276 | goto cleanup; | ||
| 277 | } | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | return cache; | ||
| 282 | |||
| 283 | cleanup: | ||
| 284 | squashfs_cache_delete(cache); | ||
| 285 | return NULL; | ||
| 286 | } | ||
| 287 | |||
| 288 | |||
| 289 | /* | ||
| 290 | * Copy upto length bytes from cache entry to buffer starting at offset bytes | ||
| 291 | * into the cache entry. If there's not length bytes then copy the number of | ||
| 292 | * bytes available. In all cases return the number of bytes copied. | ||
| 293 | */ | ||
| 294 | int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry, | ||
| 295 | int offset, int length) | ||
| 296 | { | ||
| 297 | int remaining = length; | ||
| 298 | |||
| 299 | if (length == 0) | ||
| 300 | return 0; | ||
| 301 | else if (buffer == NULL) | ||
| 302 | return min(length, entry->length - offset); | ||
| 303 | |||
| 304 | while (offset < entry->length) { | ||
| 305 | void *buff = entry->data[offset / PAGE_CACHE_SIZE] | ||
| 306 | + (offset % PAGE_CACHE_SIZE); | ||
| 307 | int bytes = min_t(int, entry->length - offset, | ||
| 308 | PAGE_CACHE_SIZE - (offset % PAGE_CACHE_SIZE)); | ||
| 309 | |||
| 310 | if (bytes >= remaining) { | ||
| 311 | memcpy(buffer, buff, remaining); | ||
| 312 | remaining = 0; | ||
| 313 | break; | ||
| 314 | } | ||
| 315 | |||
| 316 | memcpy(buffer, buff, bytes); | ||
| 317 | buffer += bytes; | ||
| 318 | remaining -= bytes; | ||
| 319 | offset += bytes; | ||
| 320 | } | ||
| 321 | |||
| 322 | return length - remaining; | ||
| 323 | } | ||
| 324 | |||
| 325 | |||
| 326 | /* | ||
| 327 | * Read length bytes from metadata position <block, offset> (block is the | ||
| 328 | * start of the compressed block on disk, and offset is the offset into | ||
| 329 | * the block once decompressed). Data is packed into consecutive blocks, | ||
| 330 | * and length bytes may require reading more than one block. | ||
| 331 | */ | ||
| 332 | int squashfs_read_metadata(struct super_block *sb, void *buffer, | ||
| 333 | u64 *block, int *offset, int length) | ||
| 334 | { | ||
| 335 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 336 | int bytes, copied = length; | ||
| 337 | struct squashfs_cache_entry *entry; | ||
| 338 | |||
| 339 | TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset); | ||
| 340 | |||
| 341 | while (length) { | ||
| 342 | entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0); | ||
| 343 | if (entry->error) | ||
| 344 | return entry->error; | ||
| 345 | else if (*offset >= entry->length) | ||
| 346 | return -EIO; | ||
| 347 | |||
| 348 | bytes = squashfs_copy_data(buffer, entry, *offset, length); | ||
| 349 | if (buffer) | ||
| 350 | buffer += bytes; | ||
| 351 | length -= bytes; | ||
| 352 | *offset += bytes; | ||
| 353 | |||
| 354 | if (*offset == entry->length) { | ||
| 355 | *block = entry->next_index; | ||
| 356 | *offset = 0; | ||
| 357 | } | ||
| 358 | |||
| 359 | squashfs_cache_put(entry); | ||
| 360 | } | ||
| 361 | |||
| 362 | return copied; | ||
| 363 | } | ||
| 364 | |||
| 365 | |||
| 366 | /* | ||
| 367 | * Look-up in the fragmment cache the fragment located at <start_block> in the | ||
| 368 | * filesystem. If necessary read and decompress it from disk. | ||
| 369 | */ | ||
| 370 | struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *sb, | ||
| 371 | u64 start_block, int length) | ||
| 372 | { | ||
| 373 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 374 | |||
| 375 | return squashfs_cache_get(sb, msblk->fragment_cache, start_block, | ||
| 376 | length); | ||
| 377 | } | ||
| 378 | |||
| 379 | |||
| 380 | /* | ||
| 381 | * Read and decompress the datablock located at <start_block> in the | ||
| 382 | * filesystem. The cache is used here to avoid duplicating locking and | ||
| 383 | * read/decompress code. | ||
| 384 | */ | ||
| 385 | struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb, | ||
| 386 | u64 start_block, int length) | ||
| 387 | { | ||
| 388 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 389 | |||
| 390 | return squashfs_cache_get(sb, msblk->read_page, start_block, length); | ||
| 391 | } | ||
| 392 | |||
| 393 | |||
| 394 | /* | ||
| 395 | * Read a filesystem table (uncompressed sequence of bytes) from disk | ||
| 396 | */ | ||
| 397 | int squashfs_read_table(struct super_block *sb, void *buffer, u64 block, | ||
| 398 | int length) | ||
| 399 | { | ||
| 400 | int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
| 401 | int i, res; | ||
| 402 | void **data = kcalloc(pages, sizeof(void *), GFP_KERNEL); | ||
| 403 | if (data == NULL) | ||
| 404 | return -ENOMEM; | ||
| 405 | |||
| 406 | for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) | ||
| 407 | data[i] = buffer; | ||
| 408 | res = squashfs_read_data(sb, data, block, length | | ||
| 409 | SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length); | ||
| 410 | kfree(data); | ||
| 411 | return res; | ||
| 412 | } | ||
diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c new file mode 100644 index 000000000000..566b0eaed868 --- /dev/null +++ b/fs/squashfs/dir.c | |||
| @@ -0,0 +1,235 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * dir.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This file implements code to read directories from disk. | ||
| 26 | * | ||
| 27 | * See namei.c for a description of directory organisation on disk. | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <linux/fs.h> | ||
| 31 | #include <linux/vfs.h> | ||
| 32 | #include <linux/slab.h> | ||
| 33 | #include <linux/zlib.h> | ||
| 34 | |||
| 35 | #include "squashfs_fs.h" | ||
| 36 | #include "squashfs_fs_sb.h" | ||
| 37 | #include "squashfs_fs_i.h" | ||
| 38 | #include "squashfs.h" | ||
| 39 | |||
| 40 | static const unsigned char squashfs_filetype_table[] = { | ||
| 41 | DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK | ||
| 42 | }; | ||
| 43 | |||
| 44 | /* | ||
| 45 | * Lookup offset (f_pos) in the directory index, returning the | ||
| 46 | * metadata block containing it. | ||
| 47 | * | ||
| 48 | * If we get an error reading the index then return the part of the index | ||
| 49 | * (if any) we have managed to read - the index isn't essential, just | ||
| 50 | * quicker. | ||
| 51 | */ | ||
| 52 | static int get_dir_index_using_offset(struct super_block *sb, | ||
| 53 | u64 *next_block, int *next_offset, u64 index_start, int index_offset, | ||
| 54 | int i_count, u64 f_pos) | ||
| 55 | { | ||
| 56 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 57 | int err, i, index, length = 0; | ||
| 58 | struct squashfs_dir_index dir_index; | ||
| 59 | |||
| 60 | TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %lld\n", | ||
| 61 | i_count, f_pos); | ||
| 62 | |||
| 63 | /* | ||
| 64 | * Translate from external f_pos to the internal f_pos. This | ||
| 65 | * is offset by 3 because we invent "." and ".." entries which are | ||
| 66 | * not actually stored in the directory. | ||
| 67 | */ | ||
| 68 | if (f_pos < 3) | ||
| 69 | return f_pos; | ||
| 70 | f_pos -= 3; | ||
| 71 | |||
| 72 | for (i = 0; i < i_count; i++) { | ||
| 73 | err = squashfs_read_metadata(sb, &dir_index, &index_start, | ||
| 74 | &index_offset, sizeof(dir_index)); | ||
| 75 | if (err < 0) | ||
| 76 | break; | ||
| 77 | |||
| 78 | index = le32_to_cpu(dir_index.index); | ||
| 79 | if (index > f_pos) | ||
| 80 | /* | ||
| 81 | * Found the index we're looking for. | ||
| 82 | */ | ||
| 83 | break; | ||
| 84 | |||
| 85 | err = squashfs_read_metadata(sb, NULL, &index_start, | ||
| 86 | &index_offset, le32_to_cpu(dir_index.size) + 1); | ||
| 87 | if (err < 0) | ||
| 88 | break; | ||
| 89 | |||
| 90 | length = index; | ||
| 91 | *next_block = le32_to_cpu(dir_index.start_block) + | ||
| 92 | msblk->directory_table; | ||
| 93 | } | ||
| 94 | |||
| 95 | *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; | ||
| 96 | |||
| 97 | /* | ||
| 98 | * Translate back from internal f_pos to external f_pos. | ||
| 99 | */ | ||
| 100 | return length + 3; | ||
| 101 | } | ||
| 102 | |||
| 103 | |||
| 104 | static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) | ||
| 105 | { | ||
| 106 | struct inode *inode = file->f_dentry->d_inode; | ||
| 107 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
| 108 | u64 block = squashfs_i(inode)->start + msblk->directory_table; | ||
| 109 | int offset = squashfs_i(inode)->offset, length = 0, dir_count, size, | ||
| 110 | type, err; | ||
| 111 | unsigned int inode_number; | ||
| 112 | struct squashfs_dir_header dirh; | ||
| 113 | struct squashfs_dir_entry *dire; | ||
| 114 | |||
| 115 | TRACE("Entered squashfs_readdir [%llx:%x]\n", block, offset); | ||
| 116 | |||
| 117 | dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL); | ||
| 118 | if (dire == NULL) { | ||
| 119 | ERROR("Failed to allocate squashfs_dir_entry\n"); | ||
| 120 | goto finish; | ||
| 121 | } | ||
| 122 | |||
| 123 | /* | ||
| 124 | * Return "." and ".." entries as the first two filenames in the | ||
| 125 | * directory. To maximise compression these two entries are not | ||
| 126 | * stored in the directory, and so we invent them here. | ||
| 127 | * | ||
| 128 | * It also means that the external f_pos is offset by 3 from the | ||
| 129 | * on-disk directory f_pos. | ||
| 130 | */ | ||
| 131 | while (file->f_pos < 3) { | ||
| 132 | char *name; | ||
| 133 | int i_ino; | ||
| 134 | |||
| 135 | if (file->f_pos == 0) { | ||
| 136 | name = "."; | ||
| 137 | size = 1; | ||
| 138 | i_ino = inode->i_ino; | ||
| 139 | } else { | ||
| 140 | name = ".."; | ||
| 141 | size = 2; | ||
| 142 | i_ino = squashfs_i(inode)->parent; | ||
| 143 | } | ||
| 144 | |||
| 145 | TRACE("Calling filldir(%p, %s, %d, %lld, %d, %d)\n", | ||
| 146 | dirent, name, size, file->f_pos, i_ino, | ||
| 147 | squashfs_filetype_table[1]); | ||
| 148 | |||
| 149 | if (filldir(dirent, name, size, file->f_pos, i_ino, | ||
| 150 | squashfs_filetype_table[1]) < 0) { | ||
| 151 | TRACE("Filldir returned less than 0\n"); | ||
| 152 | goto finish; | ||
| 153 | } | ||
| 154 | |||
| 155 | file->f_pos += size; | ||
| 156 | } | ||
| 157 | |||
| 158 | length = get_dir_index_using_offset(inode->i_sb, &block, &offset, | ||
| 159 | squashfs_i(inode)->dir_idx_start, | ||
| 160 | squashfs_i(inode)->dir_idx_offset, | ||
| 161 | squashfs_i(inode)->dir_idx_cnt, | ||
| 162 | file->f_pos); | ||
| 163 | |||
| 164 | while (length < i_size_read(inode)) { | ||
| 165 | /* | ||
| 166 | * Read directory header | ||
| 167 | */ | ||
| 168 | err = squashfs_read_metadata(inode->i_sb, &dirh, &block, | ||
| 169 | &offset, sizeof(dirh)); | ||
| 170 | if (err < 0) | ||
| 171 | goto failed_read; | ||
| 172 | |||
| 173 | length += sizeof(dirh); | ||
| 174 | |||
| 175 | dir_count = le32_to_cpu(dirh.count) + 1; | ||
| 176 | while (dir_count--) { | ||
| 177 | /* | ||
| 178 | * Read directory entry. | ||
| 179 | */ | ||
| 180 | err = squashfs_read_metadata(inode->i_sb, dire, &block, | ||
| 181 | &offset, sizeof(*dire)); | ||
| 182 | if (err < 0) | ||
| 183 | goto failed_read; | ||
| 184 | |||
| 185 | size = le16_to_cpu(dire->size) + 1; | ||
| 186 | |||
| 187 | err = squashfs_read_metadata(inode->i_sb, dire->name, | ||
| 188 | &block, &offset, size); | ||
| 189 | if (err < 0) | ||
| 190 | goto failed_read; | ||
| 191 | |||
| 192 | length += sizeof(*dire) + size; | ||
| 193 | |||
| 194 | if (file->f_pos >= length) | ||
| 195 | continue; | ||
| 196 | |||
| 197 | dire->name[size] = '\0'; | ||
| 198 | inode_number = le32_to_cpu(dirh.inode_number) + | ||
| 199 | ((short) le16_to_cpu(dire->inode_number)); | ||
| 200 | type = le16_to_cpu(dire->type); | ||
| 201 | |||
| 202 | TRACE("Calling filldir(%p, %s, %d, %lld, %x:%x, %d, %d)" | ||
| 203 | "\n", dirent, dire->name, size, | ||
| 204 | file->f_pos, | ||
| 205 | le32_to_cpu(dirh.start_block), | ||
| 206 | le16_to_cpu(dire->offset), | ||
| 207 | inode_number, | ||
| 208 | squashfs_filetype_table[type]); | ||
| 209 | |||
| 210 | if (filldir(dirent, dire->name, size, file->f_pos, | ||
| 211 | inode_number, | ||
| 212 | squashfs_filetype_table[type]) < 0) { | ||
| 213 | TRACE("Filldir returned less than 0\n"); | ||
| 214 | goto finish; | ||
| 215 | } | ||
| 216 | |||
| 217 | file->f_pos = length; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | finish: | ||
| 222 | kfree(dire); | ||
| 223 | return 0; | ||
| 224 | |||
| 225 | failed_read: | ||
| 226 | ERROR("Unable to read directory block [%llx:%x]\n", block, offset); | ||
| 227 | kfree(dire); | ||
| 228 | return 0; | ||
| 229 | } | ||
| 230 | |||
| 231 | |||
| 232 | const struct file_operations squashfs_dir_ops = { | ||
| 233 | .read = generic_read_dir, | ||
| 234 | .readdir = squashfs_readdir | ||
| 235 | }; | ||
diff --git a/fs/squashfs/export.c b/fs/squashfs/export.c new file mode 100644 index 000000000000..69e971d5ddc1 --- /dev/null +++ b/fs/squashfs/export.c | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * export.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This file implements code to make Squashfs filesystems exportable (NFS etc.) | ||
| 26 | * | ||
| 27 | * The export code uses an inode lookup table to map inode numbers passed in | ||
| 28 | * filehandles to an inode location on disk. This table is stored compressed | ||
| 29 | * into metadata blocks. A second index table is used to locate these. This | ||
| 30 | * second index table for speed of access (and because it is small) is read at | ||
| 31 | * mount time and cached in memory. | ||
| 32 | * | ||
| 33 | * The inode lookup table is used only by the export code, inode disk | ||
| 34 | * locations are directly encoded in directories, enabling direct access | ||
| 35 | * without an intermediate lookup for all operations except the export ops. | ||
| 36 | */ | ||
| 37 | |||
| 38 | #include <linux/fs.h> | ||
| 39 | #include <linux/vfs.h> | ||
| 40 | #include <linux/dcache.h> | ||
| 41 | #include <linux/exportfs.h> | ||
| 42 | #include <linux/zlib.h> | ||
| 43 | |||
| 44 | #include "squashfs_fs.h" | ||
| 45 | #include "squashfs_fs_sb.h" | ||
| 46 | #include "squashfs_fs_i.h" | ||
| 47 | #include "squashfs.h" | ||
| 48 | |||
| 49 | /* | ||
| 50 | * Look-up inode number (ino) in table, returning the inode location. | ||
| 51 | */ | ||
| 52 | static long long squashfs_inode_lookup(struct super_block *sb, int ino_num) | ||
| 53 | { | ||
| 54 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 55 | int blk = SQUASHFS_LOOKUP_BLOCK(ino_num - 1); | ||
| 56 | int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino_num - 1); | ||
| 57 | u64 start = le64_to_cpu(msblk->inode_lookup_table[blk]); | ||
| 58 | __le64 ino; | ||
| 59 | int err; | ||
| 60 | |||
| 61 | TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino_num); | ||
| 62 | |||
| 63 | err = squashfs_read_metadata(sb, &ino, &start, &offset, sizeof(ino)); | ||
| 64 | if (err < 0) | ||
| 65 | return err; | ||
| 66 | |||
| 67 | TRACE("squashfs_inode_lookup, inode = 0x%llx\n", | ||
| 68 | (u64) le64_to_cpu(ino)); | ||
| 69 | |||
| 70 | return le64_to_cpu(ino); | ||
| 71 | } | ||
| 72 | |||
| 73 | |||
| 74 | static struct dentry *squashfs_export_iget(struct super_block *sb, | ||
| 75 | unsigned int ino_num) | ||
| 76 | { | ||
| 77 | long long ino; | ||
| 78 | struct dentry *dentry = ERR_PTR(-ENOENT); | ||
| 79 | |||
| 80 | TRACE("Entered squashfs_export_iget\n"); | ||
| 81 | |||
| 82 | ino = squashfs_inode_lookup(sb, ino_num); | ||
| 83 | if (ino >= 0) | ||
| 84 | dentry = d_obtain_alias(squashfs_iget(sb, ino, ino_num)); | ||
| 85 | |||
| 86 | return dentry; | ||
| 87 | } | ||
| 88 | |||
| 89 | |||
| 90 | static struct dentry *squashfs_fh_to_dentry(struct super_block *sb, | ||
| 91 | struct fid *fid, int fh_len, int fh_type) | ||
| 92 | { | ||
| 93 | if ((fh_type != FILEID_INO32_GEN && fh_type != FILEID_INO32_GEN_PARENT) | ||
| 94 | || fh_len < 2) | ||
| 95 | return NULL; | ||
| 96 | |||
| 97 | return squashfs_export_iget(sb, fid->i32.ino); | ||
| 98 | } | ||
| 99 | |||
| 100 | |||
| 101 | static struct dentry *squashfs_fh_to_parent(struct super_block *sb, | ||
| 102 | struct fid *fid, int fh_len, int fh_type) | ||
| 103 | { | ||
| 104 | if (fh_type != FILEID_INO32_GEN_PARENT || fh_len < 4) | ||
| 105 | return NULL; | ||
| 106 | |||
| 107 | return squashfs_export_iget(sb, fid->i32.parent_ino); | ||
| 108 | } | ||
| 109 | |||
| 110 | |||
| 111 | static struct dentry *squashfs_get_parent(struct dentry *child) | ||
| 112 | { | ||
| 113 | struct inode *inode = child->d_inode; | ||
| 114 | unsigned int parent_ino = squashfs_i(inode)->parent; | ||
| 115 | |||
| 116 | return squashfs_export_iget(inode->i_sb, parent_ino); | ||
| 117 | } | ||
| 118 | |||
| 119 | |||
| 120 | /* | ||
| 121 | * Read uncompressed inode lookup table indexes off disk into memory | ||
| 122 | */ | ||
| 123 | __le64 *squashfs_read_inode_lookup_table(struct super_block *sb, | ||
| 124 | u64 lookup_table_start, unsigned int inodes) | ||
| 125 | { | ||
| 126 | unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes); | ||
| 127 | __le64 *inode_lookup_table; | ||
| 128 | int err; | ||
| 129 | |||
| 130 | TRACE("In read_inode_lookup_table, length %d\n", length); | ||
| 131 | |||
| 132 | /* Allocate inode lookup table indexes */ | ||
| 133 | inode_lookup_table = kmalloc(length, GFP_KERNEL); | ||
| 134 | if (inode_lookup_table == NULL) { | ||
| 135 | ERROR("Failed to allocate inode lookup table\n"); | ||
| 136 | return ERR_PTR(-ENOMEM); | ||
| 137 | } | ||
| 138 | |||
| 139 | err = squashfs_read_table(sb, inode_lookup_table, lookup_table_start, | ||
| 140 | length); | ||
| 141 | if (err < 0) { | ||
| 142 | ERROR("unable to read inode lookup table\n"); | ||
| 143 | kfree(inode_lookup_table); | ||
| 144 | return ERR_PTR(err); | ||
| 145 | } | ||
| 146 | |||
| 147 | return inode_lookup_table; | ||
| 148 | } | ||
| 149 | |||
| 150 | |||
| 151 | const struct export_operations squashfs_export_ops = { | ||
| 152 | .fh_to_dentry = squashfs_fh_to_dentry, | ||
| 153 | .fh_to_parent = squashfs_fh_to_parent, | ||
| 154 | .get_parent = squashfs_get_parent | ||
| 155 | }; | ||
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c new file mode 100644 index 000000000000..717767d831df --- /dev/null +++ b/fs/squashfs/file.c | |||
| @@ -0,0 +1,502 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * file.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This file contains code for handling regular files. A regular file | ||
| 26 | * consists of a sequence of contiguous compressed blocks, and/or a | ||
| 27 | * compressed fragment block (tail-end packed block). The compressed size | ||
| 28 | * of each datablock is stored in a block list contained within the | ||
| 29 | * file inode (itself stored in one or more compressed metadata blocks). | ||
| 30 | * | ||
| 31 | * To speed up access to datablocks when reading 'large' files (256 Mbytes or | ||
| 32 | * larger), the code implements an index cache that caches the mapping from | ||
| 33 | * block index to datablock location on disk. | ||
| 34 | * | ||
| 35 | * The index cache allows Squashfs to handle large files (up to 1.75 TiB) while | ||
| 36 | * retaining a simple and space-efficient block list on disk. The cache | ||
| 37 | * is split into slots, caching up to eight 224 GiB files (128 KiB blocks). | ||
| 38 | * Larger files use multiple slots, with 1.75 TiB files using all 8 slots. | ||
| 39 | * The index cache is designed to be memory efficient, and by default uses | ||
| 40 | * 16 KiB. | ||
| 41 | */ | ||
| 42 | |||
| 43 | #include <linux/fs.h> | ||
| 44 | #include <linux/vfs.h> | ||
| 45 | #include <linux/kernel.h> | ||
| 46 | #include <linux/slab.h> | ||
| 47 | #include <linux/string.h> | ||
| 48 | #include <linux/pagemap.h> | ||
| 49 | #include <linux/mutex.h> | ||
| 50 | #include <linux/zlib.h> | ||
| 51 | |||
| 52 | #include "squashfs_fs.h" | ||
| 53 | #include "squashfs_fs_sb.h" | ||
| 54 | #include "squashfs_fs_i.h" | ||
| 55 | #include "squashfs.h" | ||
| 56 | |||
| 57 | /* | ||
| 58 | * Locate cache slot in range [offset, index] for specified inode. If | ||
| 59 | * there's more than one return the slot closest to index. | ||
| 60 | */ | ||
| 61 | static struct meta_index *locate_meta_index(struct inode *inode, int offset, | ||
| 62 | int index) | ||
| 63 | { | ||
| 64 | struct meta_index *meta = NULL; | ||
| 65 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
| 66 | int i; | ||
| 67 | |||
| 68 | mutex_lock(&msblk->meta_index_mutex); | ||
| 69 | |||
| 70 | TRACE("locate_meta_index: index %d, offset %d\n", index, offset); | ||
| 71 | |||
| 72 | if (msblk->meta_index == NULL) | ||
| 73 | goto not_allocated; | ||
| 74 | |||
| 75 | for (i = 0; i < SQUASHFS_META_SLOTS; i++) { | ||
| 76 | if (msblk->meta_index[i].inode_number == inode->i_ino && | ||
| 77 | msblk->meta_index[i].offset >= offset && | ||
| 78 | msblk->meta_index[i].offset <= index && | ||
| 79 | msblk->meta_index[i].locked == 0) { | ||
| 80 | TRACE("locate_meta_index: entry %d, offset %d\n", i, | ||
| 81 | msblk->meta_index[i].offset); | ||
| 82 | meta = &msblk->meta_index[i]; | ||
| 83 | offset = meta->offset; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | if (meta) | ||
| 88 | meta->locked = 1; | ||
| 89 | |||
| 90 | not_allocated: | ||
| 91 | mutex_unlock(&msblk->meta_index_mutex); | ||
| 92 | |||
| 93 | return meta; | ||
| 94 | } | ||
| 95 | |||
| 96 | |||
| 97 | /* | ||
| 98 | * Find and initialise an empty cache slot for index offset. | ||
| 99 | */ | ||
| 100 | static struct meta_index *empty_meta_index(struct inode *inode, int offset, | ||
| 101 | int skip) | ||
| 102 | { | ||
| 103 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
| 104 | struct meta_index *meta = NULL; | ||
| 105 | int i; | ||
| 106 | |||
| 107 | mutex_lock(&msblk->meta_index_mutex); | ||
| 108 | |||
| 109 | TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); | ||
| 110 | |||
| 111 | if (msblk->meta_index == NULL) { | ||
| 112 | /* | ||
| 113 | * First time cache index has been used, allocate and | ||
| 114 | * initialise. The cache index could be allocated at | ||
| 115 | * mount time but doing it here means it is allocated only | ||
| 116 | * if a 'large' file is read. | ||
| 117 | */ | ||
| 118 | msblk->meta_index = kcalloc(SQUASHFS_META_SLOTS, | ||
| 119 | sizeof(*(msblk->meta_index)), GFP_KERNEL); | ||
| 120 | if (msblk->meta_index == NULL) { | ||
| 121 | ERROR("Failed to allocate meta_index\n"); | ||
| 122 | goto failed; | ||
| 123 | } | ||
| 124 | for (i = 0; i < SQUASHFS_META_SLOTS; i++) { | ||
| 125 | msblk->meta_index[i].inode_number = 0; | ||
| 126 | msblk->meta_index[i].locked = 0; | ||
| 127 | } | ||
| 128 | msblk->next_meta_index = 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | for (i = SQUASHFS_META_SLOTS; i && | ||
| 132 | msblk->meta_index[msblk->next_meta_index].locked; i--) | ||
| 133 | msblk->next_meta_index = (msblk->next_meta_index + 1) % | ||
| 134 | SQUASHFS_META_SLOTS; | ||
| 135 | |||
| 136 | if (i == 0) { | ||
| 137 | TRACE("empty_meta_index: failed!\n"); | ||
| 138 | goto failed; | ||
| 139 | } | ||
| 140 | |||
| 141 | TRACE("empty_meta_index: returned meta entry %d, %p\n", | ||
| 142 | msblk->next_meta_index, | ||
| 143 | &msblk->meta_index[msblk->next_meta_index]); | ||
| 144 | |||
| 145 | meta = &msblk->meta_index[msblk->next_meta_index]; | ||
| 146 | msblk->next_meta_index = (msblk->next_meta_index + 1) % | ||
| 147 | SQUASHFS_META_SLOTS; | ||
| 148 | |||
| 149 | meta->inode_number = inode->i_ino; | ||
| 150 | meta->offset = offset; | ||
| 151 | meta->skip = skip; | ||
| 152 | meta->entries = 0; | ||
| 153 | meta->locked = 1; | ||
| 154 | |||
| 155 | failed: | ||
| 156 | mutex_unlock(&msblk->meta_index_mutex); | ||
| 157 | return meta; | ||
| 158 | } | ||
| 159 | |||
| 160 | |||
| 161 | static void release_meta_index(struct inode *inode, struct meta_index *meta) | ||
| 162 | { | ||
| 163 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
| 164 | mutex_lock(&msblk->meta_index_mutex); | ||
| 165 | meta->locked = 0; | ||
| 166 | mutex_unlock(&msblk->meta_index_mutex); | ||
| 167 | } | ||
| 168 | |||
| 169 | |||
| 170 | /* | ||
| 171 | * Read the next n blocks from the block list, starting from | ||
| 172 | * metadata block <start_block, offset>. | ||
| 173 | */ | ||
| 174 | static long long read_indexes(struct super_block *sb, int n, | ||
| 175 | u64 *start_block, int *offset) | ||
| 176 | { | ||
| 177 | int err, i; | ||
| 178 | long long block = 0; | ||
| 179 | __le32 *blist = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); | ||
| 180 | |||
| 181 | if (blist == NULL) { | ||
| 182 | ERROR("read_indexes: Failed to allocate block_list\n"); | ||
| 183 | return -ENOMEM; | ||
| 184 | } | ||
| 185 | |||
| 186 | while (n) { | ||
| 187 | int blocks = min_t(int, n, PAGE_CACHE_SIZE >> 2); | ||
| 188 | |||
| 189 | err = squashfs_read_metadata(sb, blist, start_block, | ||
| 190 | offset, blocks << 2); | ||
| 191 | if (err < 0) { | ||
| 192 | ERROR("read_indexes: reading block [%llx:%x]\n", | ||
| 193 | *start_block, *offset); | ||
| 194 | goto failure; | ||
| 195 | } | ||
| 196 | |||
| 197 | for (i = 0; i < blocks; i++) { | ||
| 198 | int size = le32_to_cpu(blist[i]); | ||
| 199 | block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size); | ||
| 200 | } | ||
| 201 | n -= blocks; | ||
| 202 | } | ||
| 203 | |||
| 204 | kfree(blist); | ||
| 205 | return block; | ||
| 206 | |||
| 207 | failure: | ||
| 208 | kfree(blist); | ||
| 209 | return err; | ||
| 210 | } | ||
| 211 | |||
| 212 | |||
| 213 | /* | ||
| 214 | * Each cache index slot has SQUASHFS_META_ENTRIES, each of which | ||
| 215 | * can cache one index -> datablock/blocklist-block mapping. We wish | ||
| 216 | * to distribute these over the length of the file, entry[0] maps index x, | ||
| 217 | * entry[1] maps index x + skip, entry[2] maps index x + 2 * skip, and so on. | ||
| 218 | * The larger the file, the greater the skip factor. The skip factor is | ||
| 219 | * limited to the size of the metadata cache (SQUASHFS_CACHED_BLKS) to ensure | ||
| 220 | * the number of metadata blocks that need to be read fits into the cache. | ||
| 221 | * If the skip factor is limited in this way then the file will use multiple | ||
| 222 | * slots. | ||
| 223 | */ | ||
| 224 | static inline int calculate_skip(int blocks) | ||
| 225 | { | ||
| 226 | int skip = blocks / ((SQUASHFS_META_ENTRIES + 1) | ||
| 227 | * SQUASHFS_META_INDEXES); | ||
| 228 | return min(SQUASHFS_CACHED_BLKS - 1, skip + 1); | ||
| 229 | } | ||
| 230 | |||
| 231 | |||
| 232 | /* | ||
| 233 | * Search and grow the index cache for the specified inode, returning the | ||
| 234 | * on-disk locations of the datablock and block list metadata block | ||
| 235 | * <index_block, index_offset> for index (scaled to nearest cache index). | ||
| 236 | */ | ||
| 237 | static int fill_meta_index(struct inode *inode, int index, | ||
| 238 | u64 *index_block, int *index_offset, u64 *data_block) | ||
| 239 | { | ||
| 240 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
| 241 | int skip = calculate_skip(i_size_read(inode) >> msblk->block_log); | ||
| 242 | int offset = 0; | ||
| 243 | struct meta_index *meta; | ||
| 244 | struct meta_entry *meta_entry; | ||
| 245 | u64 cur_index_block = squashfs_i(inode)->block_list_start; | ||
| 246 | int cur_offset = squashfs_i(inode)->offset; | ||
| 247 | u64 cur_data_block = squashfs_i(inode)->start; | ||
| 248 | int err, i; | ||
| 249 | |||
| 250 | /* | ||
| 251 | * Scale index to cache index (cache slot entry) | ||
| 252 | */ | ||
| 253 | index /= SQUASHFS_META_INDEXES * skip; | ||
| 254 | |||
| 255 | while (offset < index) { | ||
| 256 | meta = locate_meta_index(inode, offset + 1, index); | ||
| 257 | |||
| 258 | if (meta == NULL) { | ||
| 259 | meta = empty_meta_index(inode, offset + 1, skip); | ||
| 260 | if (meta == NULL) | ||
| 261 | goto all_done; | ||
| 262 | } else { | ||
| 263 | offset = index < meta->offset + meta->entries ? index : | ||
| 264 | meta->offset + meta->entries - 1; | ||
| 265 | meta_entry = &meta->meta_entry[offset - meta->offset]; | ||
| 266 | cur_index_block = meta_entry->index_block + | ||
| 267 | msblk->inode_table; | ||
| 268 | cur_offset = meta_entry->offset; | ||
| 269 | cur_data_block = meta_entry->data_block; | ||
| 270 | TRACE("get_meta_index: offset %d, meta->offset %d, " | ||
| 271 | "meta->entries %d\n", offset, meta->offset, | ||
| 272 | meta->entries); | ||
| 273 | TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" | ||
| 274 | " data_block 0x%llx\n", cur_index_block, | ||
| 275 | cur_offset, cur_data_block); | ||
| 276 | } | ||
| 277 | |||
| 278 | /* | ||
| 279 | * If necessary grow cache slot by reading block list. Cache | ||
| 280 | * slot is extended up to index or to the end of the slot, in | ||
| 281 | * which case further slots will be used. | ||
| 282 | */ | ||
| 283 | for (i = meta->offset + meta->entries; i <= index && | ||
| 284 | i < meta->offset + SQUASHFS_META_ENTRIES; i++) { | ||
| 285 | int blocks = skip * SQUASHFS_META_INDEXES; | ||
| 286 | long long res = read_indexes(inode->i_sb, blocks, | ||
| 287 | &cur_index_block, &cur_offset); | ||
| 288 | |||
| 289 | if (res < 0) { | ||
| 290 | if (meta->entries == 0) | ||
| 291 | /* | ||
| 292 | * Don't leave an empty slot on read | ||
| 293 | * error allocated to this inode... | ||
| 294 | */ | ||
| 295 | meta->inode_number = 0; | ||
| 296 | err = res; | ||
| 297 | goto failed; | ||
| 298 | } | ||
| 299 | |||
| 300 | cur_data_block += res; | ||
| 301 | meta_entry = &meta->meta_entry[i - meta->offset]; | ||
| 302 | meta_entry->index_block = cur_index_block - | ||
| 303 | msblk->inode_table; | ||
| 304 | meta_entry->offset = cur_offset; | ||
| 305 | meta_entry->data_block = cur_data_block; | ||
| 306 | meta->entries++; | ||
| 307 | offset++; | ||
| 308 | } | ||
| 309 | |||
| 310 | TRACE("get_meta_index: meta->offset %d, meta->entries %d\n", | ||
| 311 | meta->offset, meta->entries); | ||
| 312 | |||
| 313 | release_meta_index(inode, meta); | ||
| 314 | } | ||
| 315 | |||
| 316 | all_done: | ||
| 317 | *index_block = cur_index_block; | ||
| 318 | *index_offset = cur_offset; | ||
| 319 | *data_block = cur_data_block; | ||
| 320 | |||
| 321 | /* | ||
| 322 | * Scale cache index (cache slot entry) to index | ||
| 323 | */ | ||
| 324 | return offset * SQUASHFS_META_INDEXES * skip; | ||
| 325 | |||
| 326 | failed: | ||
| 327 | release_meta_index(inode, meta); | ||
| 328 | return err; | ||
| 329 | } | ||
| 330 | |||
| 331 | |||
| 332 | /* | ||
| 333 | * Get the on-disk location and compressed size of the datablock | ||
| 334 | * specified by index. Fill_meta_index() does most of the work. | ||
| 335 | */ | ||
| 336 | static int read_blocklist(struct inode *inode, int index, u64 *block) | ||
| 337 | { | ||
| 338 | u64 start; | ||
| 339 | long long blks; | ||
| 340 | int offset; | ||
| 341 | __le32 size; | ||
| 342 | int res = fill_meta_index(inode, index, &start, &offset, block); | ||
| 343 | |||
| 344 | TRACE("read_blocklist: res %d, index %d, start 0x%llx, offset" | ||
| 345 | " 0x%x, block 0x%llx\n", res, index, start, offset, | ||
| 346 | *block); | ||
| 347 | |||
| 348 | if (res < 0) | ||
| 349 | return res; | ||
| 350 | |||
| 351 | /* | ||
| 352 | * res contains the index of the mapping returned by fill_meta_index(), | ||
| 353 | * this will likely be less than the desired index (because the | ||
| 354 | * meta_index cache works at a higher granularity). Read any | ||
| 355 | * extra block indexes needed. | ||
| 356 | */ | ||
| 357 | if (res < index) { | ||
| 358 | blks = read_indexes(inode->i_sb, index - res, &start, &offset); | ||
| 359 | if (blks < 0) | ||
| 360 | return (int) blks; | ||
| 361 | *block += blks; | ||
| 362 | } | ||
| 363 | |||
| 364 | /* | ||
| 365 | * Read length of block specified by index. | ||
| 366 | */ | ||
| 367 | res = squashfs_read_metadata(inode->i_sb, &size, &start, &offset, | ||
| 368 | sizeof(size)); | ||
| 369 | if (res < 0) | ||
| 370 | return res; | ||
| 371 | return le32_to_cpu(size); | ||
| 372 | } | ||
| 373 | |||
| 374 | |||
| 375 | static int squashfs_readpage(struct file *file, struct page *page) | ||
| 376 | { | ||
| 377 | struct inode *inode = page->mapping->host; | ||
| 378 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
| 379 | int bytes, i, offset = 0, sparse = 0; | ||
| 380 | struct squashfs_cache_entry *buffer = NULL; | ||
| 381 | void *pageaddr; | ||
| 382 | |||
| 383 | int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; | ||
| 384 | int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); | ||
| 385 | int start_index = page->index & ~mask; | ||
| 386 | int end_index = start_index | mask; | ||
| 387 | int file_end = i_size_read(inode) >> msblk->block_log; | ||
| 388 | |||
| 389 | TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", | ||
| 390 | page->index, squashfs_i(inode)->start); | ||
| 391 | |||
| 392 | if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> | ||
| 393 | PAGE_CACHE_SHIFT)) | ||
| 394 | goto out; | ||
| 395 | |||
| 396 | if (index < file_end || squashfs_i(inode)->fragment_block == | ||
| 397 | SQUASHFS_INVALID_BLK) { | ||
| 398 | /* | ||
| 399 | * Reading a datablock from disk. Need to read block list | ||
| 400 | * to get location and block size. | ||
| 401 | */ | ||
| 402 | u64 block = 0; | ||
| 403 | int bsize = read_blocklist(inode, index, &block); | ||
| 404 | if (bsize < 0) | ||
| 405 | goto error_out; | ||
| 406 | |||
| 407 | if (bsize == 0) { /* hole */ | ||
| 408 | bytes = index == file_end ? | ||
| 409 | (i_size_read(inode) & (msblk->block_size - 1)) : | ||
| 410 | msblk->block_size; | ||
| 411 | sparse = 1; | ||
| 412 | } else { | ||
| 413 | /* | ||
| 414 | * Read and decompress datablock. | ||
| 415 | */ | ||
| 416 | buffer = squashfs_get_datablock(inode->i_sb, | ||
| 417 | block, bsize); | ||
| 418 | if (buffer->error) { | ||
| 419 | ERROR("Unable to read page, block %llx, size %x" | ||
| 420 | "\n", block, bsize); | ||
| 421 | squashfs_cache_put(buffer); | ||
| 422 | goto error_out; | ||
| 423 | } | ||
| 424 | bytes = buffer->length; | ||
| 425 | } | ||
| 426 | } else { | ||
| 427 | /* | ||
| 428 | * Datablock is stored inside a fragment (tail-end packed | ||
| 429 | * block). | ||
| 430 | */ | ||
| 431 | buffer = squashfs_get_fragment(inode->i_sb, | ||
| 432 | squashfs_i(inode)->fragment_block, | ||
| 433 | squashfs_i(inode)->fragment_size); | ||
| 434 | |||
| 435 | if (buffer->error) { | ||
| 436 | ERROR("Unable to read page, block %llx, size %x\n", | ||
| 437 | squashfs_i(inode)->fragment_block, | ||
| 438 | squashfs_i(inode)->fragment_size); | ||
| 439 | squashfs_cache_put(buffer); | ||
| 440 | goto error_out; | ||
| 441 | } | ||
| 442 | bytes = i_size_read(inode) & (msblk->block_size - 1); | ||
| 443 | offset = squashfs_i(inode)->fragment_offset; | ||
| 444 | } | ||
| 445 | |||
| 446 | /* | ||
| 447 | * Loop copying datablock into pages. As the datablock likely covers | ||
| 448 | * many PAGE_CACHE_SIZE pages (default block size is 128 KiB) explicitly | ||
| 449 | * grab the pages from the page cache, except for the page that we've | ||
| 450 | * been called to fill. | ||
| 451 | */ | ||
| 452 | for (i = start_index; i <= end_index && bytes > 0; i++, | ||
| 453 | bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { | ||
| 454 | struct page *push_page; | ||
| 455 | int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE); | ||
| 456 | |||
| 457 | TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail); | ||
| 458 | |||
| 459 | push_page = (i == page->index) ? page : | ||
| 460 | grab_cache_page_nowait(page->mapping, i); | ||
| 461 | |||
| 462 | if (!push_page) | ||
| 463 | continue; | ||
| 464 | |||
| 465 | if (PageUptodate(push_page)) | ||
| 466 | goto skip_page; | ||
| 467 | |||
| 468 | pageaddr = kmap_atomic(push_page, KM_USER0); | ||
| 469 | squashfs_copy_data(pageaddr, buffer, offset, avail); | ||
| 470 | memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); | ||
| 471 | kunmap_atomic(pageaddr, KM_USER0); | ||
| 472 | flush_dcache_page(push_page); | ||
| 473 | SetPageUptodate(push_page); | ||
| 474 | skip_page: | ||
| 475 | unlock_page(push_page); | ||
| 476 | if (i != page->index) | ||
| 477 | page_cache_release(push_page); | ||
| 478 | } | ||
| 479 | |||
| 480 | if (!sparse) | ||
| 481 | squashfs_cache_put(buffer); | ||
| 482 | |||
| 483 | return 0; | ||
| 484 | |||
| 485 | error_out: | ||
| 486 | SetPageError(page); | ||
| 487 | out: | ||
| 488 | pageaddr = kmap_atomic(page, KM_USER0); | ||
| 489 | memset(pageaddr, 0, PAGE_CACHE_SIZE); | ||
| 490 | kunmap_atomic(pageaddr, KM_USER0); | ||
| 491 | flush_dcache_page(page); | ||
| 492 | if (!PageError(page)) | ||
| 493 | SetPageUptodate(page); | ||
| 494 | unlock_page(page); | ||
| 495 | |||
| 496 | return 0; | ||
| 497 | } | ||
| 498 | |||
| 499 | |||
| 500 | const struct address_space_operations squashfs_aops = { | ||
| 501 | .readpage = squashfs_readpage | ||
| 502 | }; | ||
diff --git a/fs/squashfs/fragment.c b/fs/squashfs/fragment.c new file mode 100644 index 000000000000..b5a2c15bbbc7 --- /dev/null +++ b/fs/squashfs/fragment.c | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * fragment.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This file implements code to handle compressed fragments (tail-end packed | ||
| 26 | * datablocks). | ||
| 27 | * | ||
| 28 | * Regular files contain a fragment index which is mapped to a fragment | ||
| 29 | * location on disk and compressed size using a fragment lookup table. | ||
| 30 | * Like everything in Squashfs this fragment lookup table is itself stored | ||
| 31 | * compressed into metadata blocks. A second index table is used to locate | ||
| 32 | * these. This second index table for speed of access (and because it | ||
| 33 | * is small) is read at mount time and cached in memory. | ||
| 34 | */ | ||
| 35 | |||
| 36 | #include <linux/fs.h> | ||
| 37 | #include <linux/vfs.h> | ||
| 38 | #include <linux/slab.h> | ||
| 39 | #include <linux/zlib.h> | ||
| 40 | |||
| 41 | #include "squashfs_fs.h" | ||
| 42 | #include "squashfs_fs_sb.h" | ||
| 43 | #include "squashfs_fs_i.h" | ||
| 44 | #include "squashfs.h" | ||
| 45 | |||
| 46 | /* | ||
| 47 | * Look-up fragment using the fragment index table. Return the on disk | ||
| 48 | * location of the fragment and its compressed size | ||
| 49 | */ | ||
| 50 | int squashfs_frag_lookup(struct super_block *sb, unsigned int fragment, | ||
| 51 | u64 *fragment_block) | ||
| 52 | { | ||
| 53 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 54 | int block = SQUASHFS_FRAGMENT_INDEX(fragment); | ||
| 55 | int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); | ||
| 56 | u64 start_block = le64_to_cpu(msblk->fragment_index[block]); | ||
| 57 | struct squashfs_fragment_entry fragment_entry; | ||
| 58 | int size; | ||
| 59 | |||
| 60 | size = squashfs_read_metadata(sb, &fragment_entry, &start_block, | ||
| 61 | &offset, sizeof(fragment_entry)); | ||
| 62 | if (size < 0) | ||
| 63 | return size; | ||
| 64 | |||
| 65 | *fragment_block = le64_to_cpu(fragment_entry.start_block); | ||
| 66 | size = le32_to_cpu(fragment_entry.size); | ||
| 67 | |||
| 68 | return size; | ||
| 69 | } | ||
| 70 | |||
| 71 | |||
| 72 | /* | ||
| 73 | * Read the uncompressed fragment lookup table indexes off disk into memory | ||
| 74 | */ | ||
| 75 | __le64 *squashfs_read_fragment_index_table(struct super_block *sb, | ||
| 76 | u64 fragment_table_start, unsigned int fragments) | ||
| 77 | { | ||
| 78 | unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(fragments); | ||
| 79 | __le64 *fragment_index; | ||
| 80 | int err; | ||
| 81 | |||
| 82 | /* Allocate fragment lookup table indexes */ | ||
| 83 | fragment_index = kmalloc(length, GFP_KERNEL); | ||
| 84 | if (fragment_index == NULL) { | ||
| 85 | ERROR("Failed to allocate fragment index table\n"); | ||
| 86 | return ERR_PTR(-ENOMEM); | ||
| 87 | } | ||
| 88 | |||
| 89 | err = squashfs_read_table(sb, fragment_index, fragment_table_start, | ||
| 90 | length); | ||
| 91 | if (err < 0) { | ||
| 92 | ERROR("unable to read fragment index table\n"); | ||
| 93 | kfree(fragment_index); | ||
| 94 | return ERR_PTR(err); | ||
| 95 | } | ||
| 96 | |||
| 97 | return fragment_index; | ||
| 98 | } | ||
diff --git a/fs/squashfs/id.c b/fs/squashfs/id.c new file mode 100644 index 000000000000..3795b837ba28 --- /dev/null +++ b/fs/squashfs/id.c | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * id.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This file implements code to handle uids and gids. | ||
| 26 | * | ||
| 27 | * For space efficiency regular files store uid and gid indexes, which are | ||
| 28 | * converted to 32-bit uids/gids using an id look up table. This table is | ||
| 29 | * stored compressed into metadata blocks. A second index table is used to | ||
| 30 | * locate these. This second index table for speed of access (and because it | ||
| 31 | * is small) is read at mount time and cached in memory. | ||
| 32 | */ | ||
| 33 | |||
| 34 | #include <linux/fs.h> | ||
| 35 | #include <linux/vfs.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/zlib.h> | ||
| 38 | |||
| 39 | #include "squashfs_fs.h" | ||
| 40 | #include "squashfs_fs_sb.h" | ||
| 41 | #include "squashfs_fs_i.h" | ||
| 42 | #include "squashfs.h" | ||
| 43 | |||
| 44 | /* | ||
| 45 | * Map uid/gid index into real 32-bit uid/gid using the id look up table | ||
| 46 | */ | ||
| 47 | int squashfs_get_id(struct super_block *sb, unsigned int index, | ||
| 48 | unsigned int *id) | ||
| 49 | { | ||
| 50 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 51 | int block = SQUASHFS_ID_BLOCK(index); | ||
| 52 | int offset = SQUASHFS_ID_BLOCK_OFFSET(index); | ||
| 53 | u64 start_block = le64_to_cpu(msblk->id_table[block]); | ||
| 54 | __le32 disk_id; | ||
| 55 | int err; | ||
| 56 | |||
| 57 | err = squashfs_read_metadata(sb, &disk_id, &start_block, &offset, | ||
| 58 | sizeof(disk_id)); | ||
| 59 | if (err < 0) | ||
| 60 | return err; | ||
| 61 | |||
| 62 | *id = le32_to_cpu(disk_id); | ||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | |||
| 67 | /* | ||
| 68 | * Read uncompressed id lookup table indexes from disk into memory | ||
| 69 | */ | ||
| 70 | __le64 *squashfs_read_id_index_table(struct super_block *sb, | ||
| 71 | u64 id_table_start, unsigned short no_ids) | ||
| 72 | { | ||
| 73 | unsigned int length = SQUASHFS_ID_BLOCK_BYTES(no_ids); | ||
| 74 | __le64 *id_table; | ||
| 75 | int err; | ||
| 76 | |||
| 77 | TRACE("In read_id_index_table, length %d\n", length); | ||
| 78 | |||
| 79 | /* Allocate id lookup table indexes */ | ||
| 80 | id_table = kmalloc(length, GFP_KERNEL); | ||
| 81 | if (id_table == NULL) { | ||
| 82 | ERROR("Failed to allocate id index table\n"); | ||
| 83 | return ERR_PTR(-ENOMEM); | ||
| 84 | } | ||
| 85 | |||
| 86 | err = squashfs_read_table(sb, id_table, id_table_start, length); | ||
| 87 | if (err < 0) { | ||
| 88 | ERROR("unable to read id index table\n"); | ||
| 89 | kfree(id_table); | ||
| 90 | return ERR_PTR(err); | ||
| 91 | } | ||
| 92 | |||
| 93 | return id_table; | ||
| 94 | } | ||
diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c new file mode 100644 index 000000000000..7a63398bb855 --- /dev/null +++ b/fs/squashfs/inode.c | |||
| @@ -0,0 +1,346 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * inode.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This file implements code to create and read inodes from disk. | ||
| 26 | * | ||
| 27 | * Inodes in Squashfs are identified by a 48-bit inode which encodes the | ||
| 28 | * location of the compressed metadata block containing the inode, and the byte | ||
| 29 | * offset into that block where the inode is placed (<block, offset>). | ||
| 30 | * | ||
| 31 | * To maximise compression there are different inodes for each file type | ||
| 32 | * (regular file, directory, device, etc.), the inode contents and length | ||
| 33 | * varying with the type. | ||
| 34 | * | ||
| 35 | * To further maximise compression, two types of regular file inode and | ||
| 36 | * directory inode are defined: inodes optimised for frequently occurring | ||
| 37 | * regular files and directories, and extended types where extra | ||
| 38 | * information has to be stored. | ||
| 39 | */ | ||
| 40 | |||
| 41 | #include <linux/fs.h> | ||
| 42 | #include <linux/vfs.h> | ||
| 43 | #include <linux/zlib.h> | ||
| 44 | |||
| 45 | #include "squashfs_fs.h" | ||
| 46 | #include "squashfs_fs_sb.h" | ||
| 47 | #include "squashfs_fs_i.h" | ||
| 48 | #include "squashfs.h" | ||
| 49 | |||
| 50 | /* | ||
| 51 | * Initialise VFS inode with the base inode information common to all | ||
| 52 | * Squashfs inode types. Sqsh_ino contains the unswapped base inode | ||
| 53 | * off disk. | ||
| 54 | */ | ||
| 55 | static int squashfs_new_inode(struct super_block *sb, struct inode *inode, | ||
| 56 | struct squashfs_base_inode *sqsh_ino) | ||
| 57 | { | ||
| 58 | int err; | ||
| 59 | |||
| 60 | err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->uid), &inode->i_uid); | ||
| 61 | if (err) | ||
| 62 | return err; | ||
| 63 | |||
| 64 | err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->guid), &inode->i_gid); | ||
| 65 | if (err) | ||
| 66 | return err; | ||
| 67 | |||
| 68 | inode->i_ino = le32_to_cpu(sqsh_ino->inode_number); | ||
| 69 | inode->i_mtime.tv_sec = le32_to_cpu(sqsh_ino->mtime); | ||
| 70 | inode->i_atime.tv_sec = inode->i_mtime.tv_sec; | ||
| 71 | inode->i_ctime.tv_sec = inode->i_mtime.tv_sec; | ||
| 72 | inode->i_mode = le16_to_cpu(sqsh_ino->mode); | ||
| 73 | inode->i_size = 0; | ||
| 74 | |||
| 75 | return err; | ||
| 76 | } | ||
| 77 | |||
| 78 | |||
| 79 | struct inode *squashfs_iget(struct super_block *sb, long long ino, | ||
| 80 | unsigned int ino_number) | ||
| 81 | { | ||
| 82 | struct inode *inode = iget_locked(sb, ino_number); | ||
| 83 | int err; | ||
| 84 | |||
| 85 | TRACE("Entered squashfs_iget\n"); | ||
| 86 | |||
| 87 | if (!inode) | ||
| 88 | return ERR_PTR(-ENOMEM); | ||
| 89 | if (!(inode->i_state & I_NEW)) | ||
| 90 | return inode; | ||
| 91 | |||
| 92 | err = squashfs_read_inode(inode, ino); | ||
| 93 | if (err) { | ||
| 94 | iget_failed(inode); | ||
| 95 | return ERR_PTR(err); | ||
| 96 | } | ||
| 97 | |||
| 98 | unlock_new_inode(inode); | ||
| 99 | return inode; | ||
| 100 | } | ||
| 101 | |||
| 102 | |||
| 103 | /* | ||
| 104 | * Initialise VFS inode by reading inode from inode table (compressed | ||
| 105 | * metadata). The format and amount of data read depends on type. | ||
| 106 | */ | ||
| 107 | int squashfs_read_inode(struct inode *inode, long long ino) | ||
| 108 | { | ||
| 109 | struct super_block *sb = inode->i_sb; | ||
| 110 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 111 | u64 block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table; | ||
| 112 | int err, type, offset = SQUASHFS_INODE_OFFSET(ino); | ||
| 113 | union squashfs_inode squashfs_ino; | ||
| 114 | struct squashfs_base_inode *sqshb_ino = &squashfs_ino.base; | ||
| 115 | |||
| 116 | TRACE("Entered squashfs_read_inode\n"); | ||
| 117 | |||
| 118 | /* | ||
| 119 | * Read inode base common to all inode types. | ||
| 120 | */ | ||
| 121 | err = squashfs_read_metadata(sb, sqshb_ino, &block, | ||
| 122 | &offset, sizeof(*sqshb_ino)); | ||
| 123 | if (err < 0) | ||
| 124 | goto failed_read; | ||
| 125 | |||
| 126 | err = squashfs_new_inode(sb, inode, sqshb_ino); | ||
| 127 | if (err) | ||
| 128 | goto failed_read; | ||
| 129 | |||
| 130 | block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table; | ||
| 131 | offset = SQUASHFS_INODE_OFFSET(ino); | ||
| 132 | |||
| 133 | type = le16_to_cpu(sqshb_ino->inode_type); | ||
| 134 | switch (type) { | ||
| 135 | case SQUASHFS_REG_TYPE: { | ||
| 136 | unsigned int frag_offset, frag_size, frag; | ||
| 137 | u64 frag_blk; | ||
| 138 | struct squashfs_reg_inode *sqsh_ino = &squashfs_ino.reg; | ||
| 139 | |||
| 140 | err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset, | ||
| 141 | sizeof(*sqsh_ino)); | ||
| 142 | if (err < 0) | ||
| 143 | goto failed_read; | ||
| 144 | |||
| 145 | frag = le32_to_cpu(sqsh_ino->fragment); | ||
| 146 | if (frag != SQUASHFS_INVALID_FRAG) { | ||
| 147 | frag_offset = le32_to_cpu(sqsh_ino->offset); | ||
| 148 | frag_size = squashfs_frag_lookup(sb, frag, &frag_blk); | ||
| 149 | if (frag_size < 0) { | ||
| 150 | err = frag_size; | ||
| 151 | goto failed_read; | ||
| 152 | } | ||
| 153 | } else { | ||
| 154 | frag_blk = SQUASHFS_INVALID_BLK; | ||
| 155 | frag_size = 0; | ||
| 156 | frag_offset = 0; | ||
| 157 | } | ||
| 158 | |||
| 159 | inode->i_nlink = 1; | ||
| 160 | inode->i_size = le32_to_cpu(sqsh_ino->file_size); | ||
| 161 | inode->i_fop = &generic_ro_fops; | ||
| 162 | inode->i_mode |= S_IFREG; | ||
| 163 | inode->i_blocks = ((inode->i_size - 1) >> 9) + 1; | ||
| 164 | squashfs_i(inode)->fragment_block = frag_blk; | ||
| 165 | squashfs_i(inode)->fragment_size = frag_size; | ||
| 166 | squashfs_i(inode)->fragment_offset = frag_offset; | ||
| 167 | squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block); | ||
| 168 | squashfs_i(inode)->block_list_start = block; | ||
| 169 | squashfs_i(inode)->offset = offset; | ||
| 170 | inode->i_data.a_ops = &squashfs_aops; | ||
| 171 | |||
| 172 | TRACE("File inode %x:%x, start_block %llx, block_list_start " | ||
| 173 | "%llx, offset %x\n", SQUASHFS_INODE_BLK(ino), | ||
| 174 | offset, squashfs_i(inode)->start, block, offset); | ||
| 175 | break; | ||
| 176 | } | ||
| 177 | case SQUASHFS_LREG_TYPE: { | ||
| 178 | unsigned int frag_offset, frag_size, frag; | ||
| 179 | u64 frag_blk; | ||
| 180 | struct squashfs_lreg_inode *sqsh_ino = &squashfs_ino.lreg; | ||
| 181 | |||
| 182 | err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset, | ||
| 183 | sizeof(*sqsh_ino)); | ||
| 184 | if (err < 0) | ||
| 185 | goto failed_read; | ||
| 186 | |||
| 187 | frag = le32_to_cpu(sqsh_ino->fragment); | ||
| 188 | if (frag != SQUASHFS_INVALID_FRAG) { | ||
| 189 | frag_offset = le32_to_cpu(sqsh_ino->offset); | ||
| 190 | frag_size = squashfs_frag_lookup(sb, frag, &frag_blk); | ||
| 191 | if (frag_size < 0) { | ||
| 192 | err = frag_size; | ||
| 193 | goto failed_read; | ||
| 194 | } | ||
| 195 | } else { | ||
| 196 | frag_blk = SQUASHFS_INVALID_BLK; | ||
| 197 | frag_size = 0; | ||
| 198 | frag_offset = 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | inode->i_nlink = le32_to_cpu(sqsh_ino->nlink); | ||
| 202 | inode->i_size = le64_to_cpu(sqsh_ino->file_size); | ||
| 203 | inode->i_fop = &generic_ro_fops; | ||
| 204 | inode->i_mode |= S_IFREG; | ||
| 205 | inode->i_blocks = ((inode->i_size - | ||
| 206 | le64_to_cpu(sqsh_ino->sparse) - 1) >> 9) + 1; | ||
| 207 | |||
| 208 | squashfs_i(inode)->fragment_block = frag_blk; | ||
| 209 | squashfs_i(inode)->fragment_size = frag_size; | ||
| 210 | squashfs_i(inode)->fragment_offset = frag_offset; | ||
| 211 | squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block); | ||
| 212 | squashfs_i(inode)->block_list_start = block; | ||
| 213 | squashfs_i(inode)->offset = offset; | ||
| 214 | inode->i_data.a_ops = &squashfs_aops; | ||
| 215 | |||
| 216 | TRACE("File inode %x:%x, start_block %llx, block_list_start " | ||
| 217 | "%llx, offset %x\n", SQUASHFS_INODE_BLK(ino), | ||
| 218 | offset, squashfs_i(inode)->start, block, offset); | ||
| 219 | break; | ||
| 220 | } | ||
| 221 | case SQUASHFS_DIR_TYPE: { | ||
| 222 | struct squashfs_dir_inode *sqsh_ino = &squashfs_ino.dir; | ||
| 223 | |||
| 224 | err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset, | ||
| 225 | sizeof(*sqsh_ino)); | ||
| 226 | if (err < 0) | ||
| 227 | goto failed_read; | ||
| 228 | |||
| 229 | inode->i_nlink = le32_to_cpu(sqsh_ino->nlink); | ||
| 230 | inode->i_size = le16_to_cpu(sqsh_ino->file_size); | ||
| 231 | inode->i_op = &squashfs_dir_inode_ops; | ||
| 232 | inode->i_fop = &squashfs_dir_ops; | ||
| 233 | inode->i_mode |= S_IFDIR; | ||
| 234 | squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block); | ||
| 235 | squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset); | ||
| 236 | squashfs_i(inode)->dir_idx_cnt = 0; | ||
| 237 | squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode); | ||
| 238 | |||
| 239 | TRACE("Directory inode %x:%x, start_block %llx, offset %x\n", | ||
| 240 | SQUASHFS_INODE_BLK(ino), offset, | ||
| 241 | squashfs_i(inode)->start, | ||
| 242 | le16_to_cpu(sqsh_ino->offset)); | ||
| 243 | break; | ||
| 244 | } | ||
| 245 | case SQUASHFS_LDIR_TYPE: { | ||
| 246 | struct squashfs_ldir_inode *sqsh_ino = &squashfs_ino.ldir; | ||
| 247 | |||
| 248 | err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset, | ||
| 249 | sizeof(*sqsh_ino)); | ||
| 250 | if (err < 0) | ||
| 251 | goto failed_read; | ||
| 252 | |||
| 253 | inode->i_nlink = le32_to_cpu(sqsh_ino->nlink); | ||
| 254 | inode->i_size = le32_to_cpu(sqsh_ino->file_size); | ||
| 255 | inode->i_op = &squashfs_dir_inode_ops; | ||
| 256 | inode->i_fop = &squashfs_dir_ops; | ||
| 257 | inode->i_mode |= S_IFDIR; | ||
| 258 | squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block); | ||
| 259 | squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset); | ||
| 260 | squashfs_i(inode)->dir_idx_start = block; | ||
| 261 | squashfs_i(inode)->dir_idx_offset = offset; | ||
| 262 | squashfs_i(inode)->dir_idx_cnt = le16_to_cpu(sqsh_ino->i_count); | ||
| 263 | squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode); | ||
| 264 | |||
| 265 | TRACE("Long directory inode %x:%x, start_block %llx, offset " | ||
| 266 | "%x\n", SQUASHFS_INODE_BLK(ino), offset, | ||
| 267 | squashfs_i(inode)->start, | ||
| 268 | le16_to_cpu(sqsh_ino->offset)); | ||
| 269 | break; | ||
| 270 | } | ||
| 271 | case SQUASHFS_SYMLINK_TYPE: | ||
| 272 | case SQUASHFS_LSYMLINK_TYPE: { | ||
| 273 | struct squashfs_symlink_inode *sqsh_ino = &squashfs_ino.symlink; | ||
| 274 | |||
| 275 | err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset, | ||
| 276 | sizeof(*sqsh_ino)); | ||
| 277 | if (err < 0) | ||
| 278 | goto failed_read; | ||
| 279 | |||
| 280 | inode->i_nlink = le32_to_cpu(sqsh_ino->nlink); | ||
| 281 | inode->i_size = le32_to_cpu(sqsh_ino->symlink_size); | ||
| 282 | inode->i_op = &page_symlink_inode_operations; | ||
| 283 | inode->i_data.a_ops = &squashfs_symlink_aops; | ||
| 284 | inode->i_mode |= S_IFLNK; | ||
| 285 | squashfs_i(inode)->start = block; | ||
| 286 | squashfs_i(inode)->offset = offset; | ||
| 287 | |||
| 288 | TRACE("Symbolic link inode %x:%x, start_block %llx, offset " | ||
| 289 | "%x\n", SQUASHFS_INODE_BLK(ino), offset, | ||
| 290 | block, offset); | ||
| 291 | break; | ||
| 292 | } | ||
| 293 | case SQUASHFS_BLKDEV_TYPE: | ||
| 294 | case SQUASHFS_CHRDEV_TYPE: | ||
| 295 | case SQUASHFS_LBLKDEV_TYPE: | ||
| 296 | case SQUASHFS_LCHRDEV_TYPE: { | ||
| 297 | struct squashfs_dev_inode *sqsh_ino = &squashfs_ino.dev; | ||
| 298 | unsigned int rdev; | ||
| 299 | |||
| 300 | err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset, | ||
| 301 | sizeof(*sqsh_ino)); | ||
| 302 | if (err < 0) | ||
| 303 | goto failed_read; | ||
| 304 | |||
| 305 | if (type == SQUASHFS_CHRDEV_TYPE) | ||
| 306 | inode->i_mode |= S_IFCHR; | ||
| 307 | else | ||
| 308 | inode->i_mode |= S_IFBLK; | ||
| 309 | inode->i_nlink = le32_to_cpu(sqsh_ino->nlink); | ||
| 310 | rdev = le32_to_cpu(sqsh_ino->rdev); | ||
| 311 | init_special_inode(inode, inode->i_mode, new_decode_dev(rdev)); | ||
| 312 | |||
| 313 | TRACE("Device inode %x:%x, rdev %x\n", | ||
| 314 | SQUASHFS_INODE_BLK(ino), offset, rdev); | ||
| 315 | break; | ||
| 316 | } | ||
| 317 | case SQUASHFS_FIFO_TYPE: | ||
| 318 | case SQUASHFS_SOCKET_TYPE: | ||
| 319 | case SQUASHFS_LFIFO_TYPE: | ||
| 320 | case SQUASHFS_LSOCKET_TYPE: { | ||
| 321 | struct squashfs_ipc_inode *sqsh_ino = &squashfs_ino.ipc; | ||
| 322 | |||
| 323 | err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset, | ||
| 324 | sizeof(*sqsh_ino)); | ||
| 325 | if (err < 0) | ||
| 326 | goto failed_read; | ||
| 327 | |||
| 328 | if (type == SQUASHFS_FIFO_TYPE) | ||
| 329 | inode->i_mode |= S_IFIFO; | ||
| 330 | else | ||
| 331 | inode->i_mode |= S_IFSOCK; | ||
| 332 | inode->i_nlink = le32_to_cpu(sqsh_ino->nlink); | ||
| 333 | init_special_inode(inode, inode->i_mode, 0); | ||
| 334 | break; | ||
| 335 | } | ||
| 336 | default: | ||
| 337 | ERROR("Unknown inode type %d in squashfs_iget!\n", type); | ||
| 338 | return -EINVAL; | ||
| 339 | } | ||
| 340 | |||
| 341 | return 0; | ||
| 342 | |||
| 343 | failed_read: | ||
| 344 | ERROR("Unable to read inode 0x%llx\n", ino); | ||
| 345 | return err; | ||
| 346 | } | ||
diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c new file mode 100644 index 000000000000..9e398653b22b --- /dev/null +++ b/fs/squashfs/namei.c | |||
| @@ -0,0 +1,242 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * namei.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This file implements code to do filename lookup in directories. | ||
| 26 | * | ||
| 27 | * Like inodes, directories are packed into compressed metadata blocks, stored | ||
| 28 | * in a directory table. Directories are accessed using the start address of | ||
| 29 | * the metablock containing the directory and the offset into the | ||
| 30 | * decompressed block (<block, offset>). | ||
| 31 | * | ||
| 32 | * Directories are organised in a slightly complex way, and are not simply | ||
| 33 | * a list of file names. The organisation takes advantage of the | ||
| 34 | * fact that (in most cases) the inodes of the files will be in the same | ||
| 35 | * compressed metadata block, and therefore, can share the start block. | ||
| 36 | * Directories are therefore organised in a two level list, a directory | ||
| 37 | * header containing the shared start block value, and a sequence of directory | ||
| 38 | * entries, each of which share the shared start block. A new directory header | ||
| 39 | * is written once/if the inode start block changes. The directory | ||
| 40 | * header/directory entry list is repeated as many times as necessary. | ||
| 41 | * | ||
| 42 | * Directories are sorted, and can contain a directory index to speed up | ||
| 43 | * file lookup. Directory indexes store one entry per metablock, each entry | ||
| 44 | * storing the index/filename mapping to the first directory header | ||
| 45 | * in each metadata block. Directories are sorted in alphabetical order, | ||
| 46 | * and at lookup the index is scanned linearly looking for the first filename | ||
| 47 | * alphabetically larger than the filename being looked up. At this point the | ||
| 48 | * location of the metadata block the filename is in has been found. | ||
| 49 | * The general idea of the index is ensure only one metadata block needs to be | ||
| 50 | * decompressed to do a lookup irrespective of the length of the directory. | ||
| 51 | * This scheme has the advantage that it doesn't require extra memory overhead | ||
| 52 | * and doesn't require much extra storage on disk. | ||
| 53 | */ | ||
| 54 | |||
| 55 | #include <linux/fs.h> | ||
| 56 | #include <linux/vfs.h> | ||
| 57 | #include <linux/slab.h> | ||
| 58 | #include <linux/string.h> | ||
| 59 | #include <linux/dcache.h> | ||
| 60 | #include <linux/zlib.h> | ||
| 61 | |||
| 62 | #include "squashfs_fs.h" | ||
| 63 | #include "squashfs_fs_sb.h" | ||
| 64 | #include "squashfs_fs_i.h" | ||
| 65 | #include "squashfs.h" | ||
| 66 | |||
| 67 | /* | ||
| 68 | * Lookup name in the directory index, returning the location of the metadata | ||
| 69 | * block containing it, and the directory index this represents. | ||
| 70 | * | ||
| 71 | * If we get an error reading the index then return the part of the index | ||
| 72 | * (if any) we have managed to read - the index isn't essential, just | ||
| 73 | * quicker. | ||
| 74 | */ | ||
| 75 | static int get_dir_index_using_name(struct super_block *sb, | ||
| 76 | u64 *next_block, int *next_offset, u64 index_start, | ||
| 77 | int index_offset, int i_count, const char *name, | ||
| 78 | int len) | ||
| 79 | { | ||
| 80 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 81 | int i, size, length = 0, err; | ||
| 82 | struct squashfs_dir_index *index; | ||
| 83 | char *str; | ||
| 84 | |||
| 85 | TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); | ||
| 86 | |||
| 87 | index = kmalloc(sizeof(*index) + SQUASHFS_NAME_LEN * 2 + 2, GFP_KERNEL); | ||
| 88 | if (index == NULL) { | ||
| 89 | ERROR("Failed to allocate squashfs_dir_index\n"); | ||
| 90 | goto out; | ||
| 91 | } | ||
| 92 | |||
| 93 | str = &index->name[SQUASHFS_NAME_LEN + 1]; | ||
| 94 | strncpy(str, name, len); | ||
| 95 | str[len] = '\0'; | ||
| 96 | |||
| 97 | for (i = 0; i < i_count; i++) { | ||
| 98 | err = squashfs_read_metadata(sb, index, &index_start, | ||
| 99 | &index_offset, sizeof(*index)); | ||
| 100 | if (err < 0) | ||
| 101 | break; | ||
| 102 | |||
| 103 | |||
| 104 | size = le32_to_cpu(index->size) + 1; | ||
| 105 | |||
| 106 | err = squashfs_read_metadata(sb, index->name, &index_start, | ||
| 107 | &index_offset, size); | ||
| 108 | if (err < 0) | ||
| 109 | break; | ||
| 110 | |||
| 111 | index->name[size] = '\0'; | ||
| 112 | |||
| 113 | if (strcmp(index->name, str) > 0) | ||
| 114 | break; | ||
| 115 | |||
| 116 | length = le32_to_cpu(index->index); | ||
| 117 | *next_block = le32_to_cpu(index->start_block) + | ||
| 118 | msblk->directory_table; | ||
| 119 | } | ||
| 120 | |||
| 121 | *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; | ||
| 122 | kfree(index); | ||
| 123 | |||
| 124 | out: | ||
| 125 | /* | ||
| 126 | * Return index (f_pos) of the looked up metadata block. Translate | ||
| 127 | * from internal f_pos to external f_pos which is offset by 3 because | ||
| 128 | * we invent "." and ".." entries which are not actually stored in the | ||
| 129 | * directory. | ||
| 130 | */ | ||
| 131 | return length + 3; | ||
| 132 | } | ||
| 133 | |||
| 134 | |||
| 135 | static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry, | ||
| 136 | struct nameidata *nd) | ||
| 137 | { | ||
| 138 | const unsigned char *name = dentry->d_name.name; | ||
| 139 | int len = dentry->d_name.len; | ||
| 140 | struct inode *inode = NULL; | ||
| 141 | struct squashfs_sb_info *msblk = dir->i_sb->s_fs_info; | ||
| 142 | struct squashfs_dir_header dirh; | ||
| 143 | struct squashfs_dir_entry *dire; | ||
| 144 | u64 block = squashfs_i(dir)->start + msblk->directory_table; | ||
| 145 | int offset = squashfs_i(dir)->offset; | ||
| 146 | int err, length = 0, dir_count, size; | ||
| 147 | |||
| 148 | TRACE("Entered squashfs_lookup [%llx:%x]\n", block, offset); | ||
| 149 | |||
| 150 | dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL); | ||
| 151 | if (dire == NULL) { | ||
| 152 | ERROR("Failed to allocate squashfs_dir_entry\n"); | ||
| 153 | return ERR_PTR(-ENOMEM); | ||
| 154 | } | ||
| 155 | |||
| 156 | if (len > SQUASHFS_NAME_LEN) { | ||
| 157 | err = -ENAMETOOLONG; | ||
| 158 | goto failed; | ||
| 159 | } | ||
| 160 | |||
| 161 | length = get_dir_index_using_name(dir->i_sb, &block, &offset, | ||
| 162 | squashfs_i(dir)->dir_idx_start, | ||
| 163 | squashfs_i(dir)->dir_idx_offset, | ||
| 164 | squashfs_i(dir)->dir_idx_cnt, name, len); | ||
| 165 | |||
| 166 | while (length < i_size_read(dir)) { | ||
| 167 | /* | ||
| 168 | * Read directory header. | ||
| 169 | */ | ||
| 170 | err = squashfs_read_metadata(dir->i_sb, &dirh, &block, | ||
| 171 | &offset, sizeof(dirh)); | ||
| 172 | if (err < 0) | ||
| 173 | goto read_failure; | ||
| 174 | |||
| 175 | length += sizeof(dirh); | ||
| 176 | |||
| 177 | dir_count = le32_to_cpu(dirh.count) + 1; | ||
| 178 | while (dir_count--) { | ||
| 179 | /* | ||
| 180 | * Read directory entry. | ||
| 181 | */ | ||
| 182 | err = squashfs_read_metadata(dir->i_sb, dire, &block, | ||
| 183 | &offset, sizeof(*dire)); | ||
| 184 | if (err < 0) | ||
| 185 | goto read_failure; | ||
| 186 | |||
| 187 | size = le16_to_cpu(dire->size) + 1; | ||
| 188 | |||
| 189 | err = squashfs_read_metadata(dir->i_sb, dire->name, | ||
| 190 | &block, &offset, size); | ||
| 191 | if (err < 0) | ||
| 192 | goto read_failure; | ||
| 193 | |||
| 194 | length += sizeof(*dire) + size; | ||
| 195 | |||
| 196 | if (name[0] < dire->name[0]) | ||
| 197 | goto exit_lookup; | ||
| 198 | |||
| 199 | if (len == size && !strncmp(name, dire->name, len)) { | ||
| 200 | unsigned int blk, off, ino_num; | ||
| 201 | long long ino; | ||
| 202 | blk = le32_to_cpu(dirh.start_block); | ||
| 203 | off = le16_to_cpu(dire->offset); | ||
| 204 | ino_num = le32_to_cpu(dirh.inode_number) + | ||
| 205 | (short) le16_to_cpu(dire->inode_number); | ||
| 206 | ino = SQUASHFS_MKINODE(blk, off); | ||
| 207 | |||
| 208 | TRACE("calling squashfs_iget for directory " | ||
| 209 | "entry %s, inode %x:%x, %d\n", name, | ||
| 210 | blk, off, ino_num); | ||
| 211 | |||
| 212 | inode = squashfs_iget(dir->i_sb, ino, ino_num); | ||
| 213 | if (IS_ERR(inode)) { | ||
| 214 | err = PTR_ERR(inode); | ||
| 215 | goto failed; | ||
| 216 | } | ||
| 217 | |||
| 218 | goto exit_lookup; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | exit_lookup: | ||
| 224 | kfree(dire); | ||
| 225 | if (inode) | ||
| 226 | return d_splice_alias(inode, dentry); | ||
| 227 | d_add(dentry, inode); | ||
| 228 | return ERR_PTR(0); | ||
| 229 | |||
| 230 | read_failure: | ||
| 231 | ERROR("Unable to read directory block [%llx:%x]\n", | ||
| 232 | squashfs_i(dir)->start + msblk->directory_table, | ||
| 233 | squashfs_i(dir)->offset); | ||
| 234 | failed: | ||
| 235 | kfree(dire); | ||
| 236 | return ERR_PTR(err); | ||
| 237 | } | ||
| 238 | |||
| 239 | |||
| 240 | const struct inode_operations squashfs_dir_inode_ops = { | ||
| 241 | .lookup = squashfs_lookup | ||
| 242 | }; | ||
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h new file mode 100644 index 000000000000..6b2515d027d5 --- /dev/null +++ b/fs/squashfs/squashfs.h | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * squashfs.h | ||
| 22 | */ | ||
| 23 | |||
| 24 | #define TRACE(s, args...) pr_debug("SQUASHFS: "s, ## args) | ||
| 25 | |||
| 26 | #define ERROR(s, args...) pr_err("SQUASHFS error: "s, ## args) | ||
| 27 | |||
| 28 | #define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args) | ||
| 29 | |||
| 30 | static inline struct squashfs_inode_info *squashfs_i(struct inode *inode) | ||
| 31 | { | ||
| 32 | return list_entry(inode, struct squashfs_inode_info, vfs_inode); | ||
| 33 | } | ||
| 34 | |||
| 35 | /* block.c */ | ||
| 36 | extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *, | ||
| 37 | int); | ||
| 38 | |||
| 39 | /* cache.c */ | ||
| 40 | extern struct squashfs_cache *squashfs_cache_init(char *, int, int); | ||
| 41 | extern void squashfs_cache_delete(struct squashfs_cache *); | ||
| 42 | extern struct squashfs_cache_entry *squashfs_cache_get(struct super_block *, | ||
| 43 | struct squashfs_cache *, u64, int); | ||
| 44 | extern void squashfs_cache_put(struct squashfs_cache_entry *); | ||
| 45 | extern int squashfs_copy_data(void *, struct squashfs_cache_entry *, int, int); | ||
| 46 | extern int squashfs_read_metadata(struct super_block *, void *, u64 *, | ||
| 47 | int *, int); | ||
| 48 | extern struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *, | ||
| 49 | u64, int); | ||
| 50 | extern struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *, | ||
| 51 | u64, int); | ||
| 52 | extern int squashfs_read_table(struct super_block *, void *, u64, int); | ||
| 53 | |||
| 54 | /* export.c */ | ||
| 55 | extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, | ||
| 56 | unsigned int); | ||
| 57 | |||
| 58 | /* fragment.c */ | ||
| 59 | extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *); | ||
| 60 | extern __le64 *squashfs_read_fragment_index_table(struct super_block *, | ||
| 61 | u64, unsigned int); | ||
| 62 | |||
| 63 | /* id.c */ | ||
| 64 | extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); | ||
| 65 | extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, | ||
| 66 | unsigned short); | ||
| 67 | |||
| 68 | /* inode.c */ | ||
| 69 | extern struct inode *squashfs_iget(struct super_block *, long long, | ||
| 70 | unsigned int); | ||
| 71 | extern int squashfs_read_inode(struct inode *, long long); | ||
| 72 | |||
| 73 | /* | ||
| 74 | * Inodes and files operations | ||
| 75 | */ | ||
| 76 | |||
| 77 | /* dir.c */ | ||
| 78 | extern const struct file_operations squashfs_dir_ops; | ||
| 79 | |||
| 80 | /* export.c */ | ||
| 81 | extern const struct export_operations squashfs_export_ops; | ||
| 82 | |||
| 83 | /* file.c */ | ||
| 84 | extern const struct address_space_operations squashfs_aops; | ||
| 85 | |||
| 86 | /* namei.c */ | ||
| 87 | extern const struct inode_operations squashfs_dir_inode_ops; | ||
| 88 | |||
| 89 | /* symlink.c */ | ||
| 90 | extern const struct address_space_operations squashfs_symlink_aops; | ||
diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h new file mode 100644 index 000000000000..6840da1bf21e --- /dev/null +++ b/fs/squashfs/squashfs_fs.h | |||
| @@ -0,0 +1,381 @@ | |||
| 1 | #ifndef SQUASHFS_FS | ||
| 2 | #define SQUASHFS_FS | ||
| 3 | /* | ||
| 4 | * Squashfs | ||
| 5 | * | ||
| 6 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 7 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License | ||
| 11 | * as published by the Free Software Foundation; either version 2, | ||
| 12 | * or (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to the Free Software | ||
| 21 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 22 | * | ||
| 23 | * squashfs_fs.h | ||
| 24 | */ | ||
| 25 | |||
| 26 | #define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE | ||
| 27 | #define SQUASHFS_MAJOR 4 | ||
| 28 | #define SQUASHFS_MINOR 0 | ||
| 29 | #define SQUASHFS_MAGIC 0x73717368 | ||
| 30 | #define SQUASHFS_START 0 | ||
| 31 | |||
| 32 | /* size of metadata (inode and directory) blocks */ | ||
| 33 | #define SQUASHFS_METADATA_SIZE 8192 | ||
| 34 | #define SQUASHFS_METADATA_LOG 13 | ||
| 35 | |||
| 36 | /* default size of data blocks */ | ||
| 37 | #define SQUASHFS_FILE_SIZE 131072 | ||
| 38 | #define SQUASHFS_FILE_LOG 17 | ||
| 39 | |||
| 40 | #define SQUASHFS_FILE_MAX_SIZE 1048576 | ||
| 41 | #define SQUASHFS_FILE_MAX_LOG 20 | ||
| 42 | |||
| 43 | /* Max number of uids and gids */ | ||
| 44 | #define SQUASHFS_IDS 65536 | ||
| 45 | |||
| 46 | /* Max length of filename (not 255) */ | ||
| 47 | #define SQUASHFS_NAME_LEN 256 | ||
| 48 | |||
| 49 | #define SQUASHFS_INVALID_FRAG (0xffffffffU) | ||
| 50 | #define SQUASHFS_INVALID_BLK (-1LL) | ||
| 51 | |||
| 52 | /* Filesystem flags */ | ||
| 53 | #define SQUASHFS_NOI 0 | ||
| 54 | #define SQUASHFS_NOD 1 | ||
| 55 | #define SQUASHFS_NOF 3 | ||
| 56 | #define SQUASHFS_NO_FRAG 4 | ||
| 57 | #define SQUASHFS_ALWAYS_FRAG 5 | ||
| 58 | #define SQUASHFS_DUPLICATE 6 | ||
| 59 | #define SQUASHFS_EXPORT 7 | ||
| 60 | |||
| 61 | #define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1) | ||
| 62 | |||
| 63 | #define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \ | ||
| 64 | SQUASHFS_NOI) | ||
| 65 | |||
| 66 | #define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \ | ||
| 67 | SQUASHFS_NOD) | ||
| 68 | |||
| 69 | #define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ | ||
| 70 | SQUASHFS_NOF) | ||
| 71 | |||
| 72 | #define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ | ||
| 73 | SQUASHFS_NO_FRAG) | ||
| 74 | |||
| 75 | #define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ | ||
| 76 | SQUASHFS_ALWAYS_FRAG) | ||
| 77 | |||
| 78 | #define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \ | ||
| 79 | SQUASHFS_DUPLICATE) | ||
| 80 | |||
| 81 | #define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \ | ||
| 82 | SQUASHFS_EXPORT) | ||
| 83 | |||
| 84 | /* Max number of types and file types */ | ||
| 85 | #define SQUASHFS_DIR_TYPE 1 | ||
| 86 | #define SQUASHFS_REG_TYPE 2 | ||
| 87 | #define SQUASHFS_SYMLINK_TYPE 3 | ||
| 88 | #define SQUASHFS_BLKDEV_TYPE 4 | ||
| 89 | #define SQUASHFS_CHRDEV_TYPE 5 | ||
| 90 | #define SQUASHFS_FIFO_TYPE 6 | ||
| 91 | #define SQUASHFS_SOCKET_TYPE 7 | ||
| 92 | #define SQUASHFS_LDIR_TYPE 8 | ||
| 93 | #define SQUASHFS_LREG_TYPE 9 | ||
| 94 | #define SQUASHFS_LSYMLINK_TYPE 10 | ||
| 95 | #define SQUASHFS_LBLKDEV_TYPE 11 | ||
| 96 | #define SQUASHFS_LCHRDEV_TYPE 12 | ||
| 97 | #define SQUASHFS_LFIFO_TYPE 13 | ||
| 98 | #define SQUASHFS_LSOCKET_TYPE 14 | ||
| 99 | |||
| 100 | /* Flag whether block is compressed or uncompressed, bit is set if block is | ||
| 101 | * uncompressed */ | ||
| 102 | #define SQUASHFS_COMPRESSED_BIT (1 << 15) | ||
| 103 | |||
| 104 | #define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \ | ||
| 105 | (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT) | ||
| 106 | |||
| 107 | #define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT)) | ||
| 108 | |||
| 109 | #define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) | ||
| 110 | |||
| 111 | #define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \ | ||
| 112 | ~SQUASHFS_COMPRESSED_BIT_BLOCK) | ||
| 113 | |||
| 114 | #define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) | ||
| 115 | |||
| 116 | /* | ||
| 117 | * Inode number ops. Inodes consist of a compressed block number, and an | ||
| 118 | * uncompressed offset within that block | ||
| 119 | */ | ||
| 120 | #define SQUASHFS_INODE_BLK(A) ((unsigned int) ((A) >> 16)) | ||
| 121 | |||
| 122 | #define SQUASHFS_INODE_OFFSET(A) ((unsigned int) ((A) & 0xffff)) | ||
| 123 | |||
| 124 | #define SQUASHFS_MKINODE(A, B) ((long long)(((long long) (A)\ | ||
| 125 | << 16) + (B))) | ||
| 126 | |||
| 127 | /* Translate between VFS mode and squashfs mode */ | ||
| 128 | #define SQUASHFS_MODE(A) ((A) & 0xfff) | ||
| 129 | |||
| 130 | /* fragment and fragment table defines */ | ||
| 131 | #define SQUASHFS_FRAGMENT_BYTES(A) \ | ||
| 132 | ((A) * sizeof(struct squashfs_fragment_entry)) | ||
| 133 | |||
| 134 | #define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \ | ||
| 135 | SQUASHFS_METADATA_SIZE) | ||
| 136 | |||
| 137 | #define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \ | ||
| 138 | SQUASHFS_METADATA_SIZE) | ||
| 139 | |||
| 140 | #define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \ | ||
| 141 | SQUASHFS_METADATA_SIZE - 1) / \ | ||
| 142 | SQUASHFS_METADATA_SIZE) | ||
| 143 | |||
| 144 | #define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\ | ||
| 145 | sizeof(u64)) | ||
| 146 | |||
| 147 | /* inode lookup table defines */ | ||
| 148 | #define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(u64)) | ||
| 149 | |||
| 150 | #define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \ | ||
| 151 | SQUASHFS_METADATA_SIZE) | ||
| 152 | |||
| 153 | #define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \ | ||
| 154 | SQUASHFS_METADATA_SIZE) | ||
| 155 | |||
| 156 | #define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \ | ||
| 157 | SQUASHFS_METADATA_SIZE - 1) / \ | ||
| 158 | SQUASHFS_METADATA_SIZE) | ||
| 159 | |||
| 160 | #define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\ | ||
| 161 | sizeof(u64)) | ||
| 162 | |||
| 163 | /* uid/gid lookup table defines */ | ||
| 164 | #define SQUASHFS_ID_BYTES(A) ((A) * sizeof(unsigned int)) | ||
| 165 | |||
| 166 | #define SQUASHFS_ID_BLOCK(A) (SQUASHFS_ID_BYTES(A) / \ | ||
| 167 | SQUASHFS_METADATA_SIZE) | ||
| 168 | |||
| 169 | #define SQUASHFS_ID_BLOCK_OFFSET(A) (SQUASHFS_ID_BYTES(A) % \ | ||
| 170 | SQUASHFS_METADATA_SIZE) | ||
| 171 | |||
| 172 | #define SQUASHFS_ID_BLOCKS(A) ((SQUASHFS_ID_BYTES(A) + \ | ||
| 173 | SQUASHFS_METADATA_SIZE - 1) / \ | ||
| 174 | SQUASHFS_METADATA_SIZE) | ||
| 175 | |||
| 176 | #define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\ | ||
| 177 | sizeof(u64)) | ||
| 178 | |||
| 179 | /* cached data constants for filesystem */ | ||
| 180 | #define SQUASHFS_CACHED_BLKS 8 | ||
| 181 | |||
| 182 | #define SQUASHFS_MAX_FILE_SIZE_LOG 64 | ||
| 183 | |||
| 184 | #define SQUASHFS_MAX_FILE_SIZE (1LL << \ | ||
| 185 | (SQUASHFS_MAX_FILE_SIZE_LOG - 2)) | ||
| 186 | |||
| 187 | #define SQUASHFS_MARKER_BYTE 0xff | ||
| 188 | |||
| 189 | /* meta index cache */ | ||
| 190 | #define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) | ||
| 191 | #define SQUASHFS_META_ENTRIES 127 | ||
| 192 | #define SQUASHFS_META_SLOTS 8 | ||
| 193 | |||
| 194 | struct meta_entry { | ||
| 195 | u64 data_block; | ||
| 196 | unsigned int index_block; | ||
| 197 | unsigned short offset; | ||
| 198 | unsigned short pad; | ||
| 199 | }; | ||
| 200 | |||
| 201 | struct meta_index { | ||
| 202 | unsigned int inode_number; | ||
| 203 | unsigned int offset; | ||
| 204 | unsigned short entries; | ||
| 205 | unsigned short skip; | ||
| 206 | unsigned short locked; | ||
| 207 | unsigned short pad; | ||
| 208 | struct meta_entry meta_entry[SQUASHFS_META_ENTRIES]; | ||
| 209 | }; | ||
| 210 | |||
| 211 | |||
| 212 | /* | ||
| 213 | * definitions for structures on disk | ||
| 214 | */ | ||
| 215 | #define ZLIB_COMPRESSION 1 | ||
| 216 | |||
| 217 | struct squashfs_super_block { | ||
| 218 | __le32 s_magic; | ||
| 219 | __le32 inodes; | ||
| 220 | __le32 mkfs_time; | ||
| 221 | __le32 block_size; | ||
| 222 | __le32 fragments; | ||
| 223 | __le16 compression; | ||
| 224 | __le16 block_log; | ||
| 225 | __le16 flags; | ||
| 226 | __le16 no_ids; | ||
| 227 | __le16 s_major; | ||
| 228 | __le16 s_minor; | ||
| 229 | __le64 root_inode; | ||
| 230 | __le64 bytes_used; | ||
| 231 | __le64 id_table_start; | ||
| 232 | __le64 xattr_table_start; | ||
| 233 | __le64 inode_table_start; | ||
| 234 | __le64 directory_table_start; | ||
| 235 | __le64 fragment_table_start; | ||
| 236 | __le64 lookup_table_start; | ||
| 237 | }; | ||
| 238 | |||
| 239 | struct squashfs_dir_index { | ||
| 240 | __le32 index; | ||
| 241 | __le32 start_block; | ||
| 242 | __le32 size; | ||
| 243 | unsigned char name[0]; | ||
| 244 | }; | ||
| 245 | |||
| 246 | struct squashfs_base_inode { | ||
| 247 | __le16 inode_type; | ||
| 248 | __le16 mode; | ||
| 249 | __le16 uid; | ||
| 250 | __le16 guid; | ||
| 251 | __le32 mtime; | ||
| 252 | __le32 inode_number; | ||
| 253 | }; | ||
| 254 | |||
| 255 | struct squashfs_ipc_inode { | ||
| 256 | __le16 inode_type; | ||
| 257 | __le16 mode; | ||
| 258 | __le16 uid; | ||
| 259 | __le16 guid; | ||
| 260 | __le32 mtime; | ||
| 261 | __le32 inode_number; | ||
| 262 | __le32 nlink; | ||
| 263 | }; | ||
| 264 | |||
| 265 | struct squashfs_dev_inode { | ||
| 266 | __le16 inode_type; | ||
| 267 | __le16 mode; | ||
| 268 | __le16 uid; | ||
| 269 | __le16 guid; | ||
| 270 | __le32 mtime; | ||
| 271 | __le32 inode_number; | ||
| 272 | __le32 nlink; | ||
| 273 | __le32 rdev; | ||
| 274 | }; | ||
| 275 | |||
| 276 | struct squashfs_symlink_inode { | ||
| 277 | __le16 inode_type; | ||
| 278 | __le16 mode; | ||
| 279 | __le16 uid; | ||
| 280 | __le16 guid; | ||
| 281 | __le32 mtime; | ||
| 282 | __le32 inode_number; | ||
| 283 | __le32 nlink; | ||
| 284 | __le32 symlink_size; | ||
| 285 | char symlink[0]; | ||
| 286 | }; | ||
| 287 | |||
| 288 | struct squashfs_reg_inode { | ||
| 289 | __le16 inode_type; | ||
| 290 | __le16 mode; | ||
| 291 | __le16 uid; | ||
| 292 | __le16 guid; | ||
| 293 | __le32 mtime; | ||
| 294 | __le32 inode_number; | ||
| 295 | __le32 start_block; | ||
| 296 | __le32 fragment; | ||
| 297 | __le32 offset; | ||
| 298 | __le32 file_size; | ||
| 299 | __le16 block_list[0]; | ||
| 300 | }; | ||
| 301 | |||
| 302 | struct squashfs_lreg_inode { | ||
| 303 | __le16 inode_type; | ||
| 304 | __le16 mode; | ||
| 305 | __le16 uid; | ||
| 306 | __le16 guid; | ||
| 307 | __le32 mtime; | ||
| 308 | __le32 inode_number; | ||
| 309 | __le64 start_block; | ||
| 310 | __le64 file_size; | ||
| 311 | __le64 sparse; | ||
| 312 | __le32 nlink; | ||
| 313 | __le32 fragment; | ||
| 314 | __le32 offset; | ||
| 315 | __le32 xattr; | ||
| 316 | __le16 block_list[0]; | ||
| 317 | }; | ||
| 318 | |||
| 319 | struct squashfs_dir_inode { | ||
| 320 | __le16 inode_type; | ||
| 321 | __le16 mode; | ||
| 322 | __le16 uid; | ||
| 323 | __le16 guid; | ||
| 324 | __le32 mtime; | ||
| 325 | __le32 inode_number; | ||
| 326 | __le32 start_block; | ||
| 327 | __le32 nlink; | ||
| 328 | __le16 file_size; | ||
| 329 | __le16 offset; | ||
| 330 | __le32 parent_inode; | ||
| 331 | }; | ||
| 332 | |||
| 333 | struct squashfs_ldir_inode { | ||
| 334 | __le16 inode_type; | ||
| 335 | __le16 mode; | ||
| 336 | __le16 uid; | ||
| 337 | __le16 guid; | ||
| 338 | __le32 mtime; | ||
| 339 | __le32 inode_number; | ||
| 340 | __le32 nlink; | ||
| 341 | __le32 file_size; | ||
| 342 | __le32 start_block; | ||
| 343 | __le32 parent_inode; | ||
| 344 | __le16 i_count; | ||
| 345 | __le16 offset; | ||
| 346 | __le32 xattr; | ||
| 347 | struct squashfs_dir_index index[0]; | ||
| 348 | }; | ||
| 349 | |||
| 350 | union squashfs_inode { | ||
| 351 | struct squashfs_base_inode base; | ||
| 352 | struct squashfs_dev_inode dev; | ||
| 353 | struct squashfs_symlink_inode symlink; | ||
| 354 | struct squashfs_reg_inode reg; | ||
| 355 | struct squashfs_lreg_inode lreg; | ||
| 356 | struct squashfs_dir_inode dir; | ||
| 357 | struct squashfs_ldir_inode ldir; | ||
| 358 | struct squashfs_ipc_inode ipc; | ||
| 359 | }; | ||
| 360 | |||
| 361 | struct squashfs_dir_entry { | ||
| 362 | __le16 offset; | ||
| 363 | __le16 inode_number; | ||
| 364 | __le16 type; | ||
| 365 | __le16 size; | ||
| 366 | char name[0]; | ||
| 367 | }; | ||
| 368 | |||
| 369 | struct squashfs_dir_header { | ||
| 370 | __le32 count; | ||
| 371 | __le32 start_block; | ||
| 372 | __le32 inode_number; | ||
| 373 | }; | ||
| 374 | |||
| 375 | struct squashfs_fragment_entry { | ||
| 376 | __le64 start_block; | ||
| 377 | __le32 size; | ||
| 378 | unsigned int unused; | ||
| 379 | }; | ||
| 380 | |||
| 381 | #endif | ||
diff --git a/fs/squashfs/squashfs_fs_i.h b/fs/squashfs/squashfs_fs_i.h new file mode 100644 index 000000000000..fbfca30c0c68 --- /dev/null +++ b/fs/squashfs/squashfs_fs_i.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | #ifndef SQUASHFS_FS_I | ||
| 2 | #define SQUASHFS_FS_I | ||
| 3 | /* | ||
| 4 | * Squashfs | ||
| 5 | * | ||
| 6 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 7 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License | ||
| 11 | * as published by the Free Software Foundation; either version 2, | ||
| 12 | * or (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to the Free Software | ||
| 21 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 22 | * | ||
| 23 | * squashfs_fs_i.h | ||
| 24 | */ | ||
| 25 | |||
| 26 | struct squashfs_inode_info { | ||
| 27 | u64 start; | ||
| 28 | int offset; | ||
| 29 | union { | ||
| 30 | struct { | ||
| 31 | u64 fragment_block; | ||
| 32 | int fragment_size; | ||
| 33 | int fragment_offset; | ||
| 34 | u64 block_list_start; | ||
| 35 | }; | ||
| 36 | struct { | ||
| 37 | u64 dir_idx_start; | ||
| 38 | int dir_idx_offset; | ||
| 39 | int dir_idx_cnt; | ||
| 40 | int parent; | ||
| 41 | }; | ||
| 42 | }; | ||
| 43 | struct inode vfs_inode; | ||
| 44 | }; | ||
| 45 | #endif | ||
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h new file mode 100644 index 000000000000..c8c65614dd1c --- /dev/null +++ b/fs/squashfs/squashfs_fs_sb.h | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #ifndef SQUASHFS_FS_SB | ||
| 2 | #define SQUASHFS_FS_SB | ||
| 3 | /* | ||
| 4 | * Squashfs | ||
| 5 | * | ||
| 6 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 7 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License | ||
| 11 | * as published by the Free Software Foundation; either version 2, | ||
| 12 | * or (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to the Free Software | ||
| 21 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 22 | * | ||
| 23 | * squashfs_fs_sb.h | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include "squashfs_fs.h" | ||
| 27 | |||
| 28 | struct squashfs_cache { | ||
| 29 | char *name; | ||
| 30 | int entries; | ||
| 31 | int next_blk; | ||
| 32 | int num_waiters; | ||
| 33 | int unused; | ||
| 34 | int block_size; | ||
| 35 | int pages; | ||
| 36 | spinlock_t lock; | ||
| 37 | wait_queue_head_t wait_queue; | ||
| 38 | struct squashfs_cache_entry *entry; | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct squashfs_cache_entry { | ||
| 42 | u64 block; | ||
| 43 | int length; | ||
| 44 | int refcount; | ||
| 45 | u64 next_index; | ||
| 46 | int pending; | ||
| 47 | int error; | ||
| 48 | int num_waiters; | ||
| 49 | wait_queue_head_t wait_queue; | ||
| 50 | struct squashfs_cache *cache; | ||
| 51 | void **data; | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct squashfs_sb_info { | ||
| 55 | int devblksize; | ||
| 56 | int devblksize_log2; | ||
| 57 | struct squashfs_cache *block_cache; | ||
| 58 | struct squashfs_cache *fragment_cache; | ||
| 59 | struct squashfs_cache *read_page; | ||
| 60 | int next_meta_index; | ||
| 61 | __le64 *id_table; | ||
| 62 | __le64 *fragment_index; | ||
| 63 | unsigned int *fragment_index_2; | ||
| 64 | struct mutex read_data_mutex; | ||
| 65 | struct mutex meta_index_mutex; | ||
| 66 | struct meta_index *meta_index; | ||
| 67 | z_stream stream; | ||
| 68 | __le64 *inode_lookup_table; | ||
| 69 | u64 inode_table; | ||
| 70 | u64 directory_table; | ||
| 71 | unsigned int block_size; | ||
| 72 | unsigned short block_log; | ||
| 73 | long long bytes_used; | ||
| 74 | unsigned int inodes; | ||
| 75 | }; | ||
| 76 | #endif | ||
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c new file mode 100644 index 000000000000..a0466d7467b2 --- /dev/null +++ b/fs/squashfs/super.c | |||
| @@ -0,0 +1,440 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * super.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This file implements code to read the superblock, read and initialise | ||
| 26 | * in-memory structures at mount time, and all the VFS glue code to register | ||
| 27 | * the filesystem. | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <linux/fs.h> | ||
| 31 | #include <linux/vfs.h> | ||
| 32 | #include <linux/slab.h> | ||
| 33 | #include <linux/mutex.h> | ||
| 34 | #include <linux/pagemap.h> | ||
| 35 | #include <linux/init.h> | ||
| 36 | #include <linux/module.h> | ||
| 37 | #include <linux/zlib.h> | ||
| 38 | |||
| 39 | #include "squashfs_fs.h" | ||
| 40 | #include "squashfs_fs_sb.h" | ||
| 41 | #include "squashfs_fs_i.h" | ||
| 42 | #include "squashfs.h" | ||
| 43 | |||
| 44 | static struct file_system_type squashfs_fs_type; | ||
| 45 | static struct super_operations squashfs_super_ops; | ||
| 46 | |||
| 47 | static int supported_squashfs_filesystem(short major, short minor, short comp) | ||
| 48 | { | ||
| 49 | if (major < SQUASHFS_MAJOR) { | ||
| 50 | ERROR("Major/Minor mismatch, older Squashfs %d.%d " | ||
| 51 | "filesystems are unsupported\n", major, minor); | ||
| 52 | return -EINVAL; | ||
| 53 | } else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) { | ||
| 54 | ERROR("Major/Minor mismatch, trying to mount newer " | ||
| 55 | "%d.%d filesystem\n", major, minor); | ||
| 56 | ERROR("Please update your kernel\n"); | ||
| 57 | return -EINVAL; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (comp != ZLIB_COMPRESSION) | ||
| 61 | return -EINVAL; | ||
| 62 | |||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | |||
| 67 | static int squashfs_fill_super(struct super_block *sb, void *data, int silent) | ||
| 68 | { | ||
| 69 | struct squashfs_sb_info *msblk; | ||
| 70 | struct squashfs_super_block *sblk = NULL; | ||
| 71 | char b[BDEVNAME_SIZE]; | ||
| 72 | struct inode *root; | ||
| 73 | long long root_inode; | ||
| 74 | unsigned short flags; | ||
| 75 | unsigned int fragments; | ||
| 76 | u64 lookup_table_start; | ||
| 77 | int err; | ||
| 78 | |||
| 79 | TRACE("Entered squashfs_fill_superblock\n"); | ||
| 80 | |||
| 81 | sb->s_fs_info = kzalloc(sizeof(*msblk), GFP_KERNEL); | ||
| 82 | if (sb->s_fs_info == NULL) { | ||
| 83 | ERROR("Failed to allocate squashfs_sb_info\n"); | ||
| 84 | return -ENOMEM; | ||
| 85 | } | ||
| 86 | msblk = sb->s_fs_info; | ||
| 87 | |||
| 88 | msblk->stream.workspace = kmalloc(zlib_inflate_workspacesize(), | ||
| 89 | GFP_KERNEL); | ||
| 90 | if (msblk->stream.workspace == NULL) { | ||
| 91 | ERROR("Failed to allocate zlib workspace\n"); | ||
| 92 | goto failure; | ||
| 93 | } | ||
| 94 | |||
| 95 | sblk = kzalloc(sizeof(*sblk), GFP_KERNEL); | ||
| 96 | if (sblk == NULL) { | ||
| 97 | ERROR("Failed to allocate squashfs_super_block\n"); | ||
| 98 | goto failure; | ||
| 99 | } | ||
| 100 | |||
| 101 | msblk->devblksize = sb_min_blocksize(sb, BLOCK_SIZE); | ||
| 102 | msblk->devblksize_log2 = ffz(~msblk->devblksize); | ||
| 103 | |||
| 104 | mutex_init(&msblk->read_data_mutex); | ||
| 105 | mutex_init(&msblk->meta_index_mutex); | ||
| 106 | |||
| 107 | /* | ||
| 108 | * msblk->bytes_used is checked in squashfs_read_table to ensure reads | ||
| 109 | * are not beyond filesystem end. But as we're using | ||
| 110 | * squashfs_read_table here to read the superblock (including the value | ||
| 111 | * of bytes_used) we need to set it to an initial sensible dummy value | ||
| 112 | */ | ||
| 113 | msblk->bytes_used = sizeof(*sblk); | ||
| 114 | err = squashfs_read_table(sb, sblk, SQUASHFS_START, sizeof(*sblk)); | ||
| 115 | |||
| 116 | if (err < 0) { | ||
| 117 | ERROR("unable to read squashfs_super_block\n"); | ||
| 118 | goto failed_mount; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Check it is a SQUASHFS superblock */ | ||
| 122 | sb->s_magic = le32_to_cpu(sblk->s_magic); | ||
| 123 | if (sb->s_magic != SQUASHFS_MAGIC) { | ||
| 124 | if (!silent) | ||
| 125 | ERROR("Can't find a SQUASHFS superblock on %s\n", | ||
| 126 | bdevname(sb->s_bdev, b)); | ||
| 127 | err = -EINVAL; | ||
| 128 | goto failed_mount; | ||
| 129 | } | ||
| 130 | |||
| 131 | /* Check the MAJOR & MINOR versions and compression type */ | ||
| 132 | err = supported_squashfs_filesystem(le16_to_cpu(sblk->s_major), | ||
| 133 | le16_to_cpu(sblk->s_minor), | ||
| 134 | le16_to_cpu(sblk->compression)); | ||
| 135 | if (err < 0) | ||
| 136 | goto failed_mount; | ||
| 137 | |||
| 138 | err = -EINVAL; | ||
| 139 | |||
| 140 | /* | ||
| 141 | * Check if there's xattrs in the filesystem. These are not | ||
| 142 | * supported in this version, so warn that they will be ignored. | ||
| 143 | */ | ||
| 144 | if (le64_to_cpu(sblk->xattr_table_start) != SQUASHFS_INVALID_BLK) | ||
| 145 | ERROR("Xattrs in filesystem, these will be ignored\n"); | ||
| 146 | |||
| 147 | /* Check the filesystem does not extend beyond the end of the | ||
| 148 | block device */ | ||
| 149 | msblk->bytes_used = le64_to_cpu(sblk->bytes_used); | ||
| 150 | if (msblk->bytes_used < 0 || msblk->bytes_used > | ||
| 151 | i_size_read(sb->s_bdev->bd_inode)) | ||
| 152 | goto failed_mount; | ||
| 153 | |||
| 154 | /* Check block size for sanity */ | ||
| 155 | msblk->block_size = le32_to_cpu(sblk->block_size); | ||
| 156 | if (msblk->block_size > SQUASHFS_FILE_MAX_SIZE) | ||
| 157 | goto failed_mount; | ||
| 158 | |||
| 159 | msblk->block_log = le16_to_cpu(sblk->block_log); | ||
| 160 | if (msblk->block_log > SQUASHFS_FILE_MAX_LOG) | ||
| 161 | goto failed_mount; | ||
| 162 | |||
| 163 | /* Check the root inode for sanity */ | ||
| 164 | root_inode = le64_to_cpu(sblk->root_inode); | ||
| 165 | if (SQUASHFS_INODE_OFFSET(root_inode) > SQUASHFS_METADATA_SIZE) | ||
| 166 | goto failed_mount; | ||
| 167 | |||
| 168 | msblk->inode_table = le64_to_cpu(sblk->inode_table_start); | ||
| 169 | msblk->directory_table = le64_to_cpu(sblk->directory_table_start); | ||
| 170 | msblk->inodes = le32_to_cpu(sblk->inodes); | ||
| 171 | flags = le16_to_cpu(sblk->flags); | ||
| 172 | |||
| 173 | TRACE("Found valid superblock on %s\n", bdevname(sb->s_bdev, b)); | ||
| 174 | TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags) | ||
| 175 | ? "un" : ""); | ||
| 176 | TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(flags) | ||
| 177 | ? "un" : ""); | ||
| 178 | TRACE("Filesystem size %lld bytes\n", msblk->bytes_used); | ||
| 179 | TRACE("Block size %d\n", msblk->block_size); | ||
| 180 | TRACE("Number of inodes %d\n", msblk->inodes); | ||
| 181 | TRACE("Number of fragments %d\n", le32_to_cpu(sblk->fragments)); | ||
| 182 | TRACE("Number of ids %d\n", le16_to_cpu(sblk->no_ids)); | ||
| 183 | TRACE("sblk->inode_table_start %llx\n", msblk->inode_table); | ||
| 184 | TRACE("sblk->directory_table_start %llx\n", msblk->directory_table); | ||
| 185 | TRACE("sblk->fragment_table_start %llx\n", | ||
| 186 | (u64) le64_to_cpu(sblk->fragment_table_start)); | ||
| 187 | TRACE("sblk->id_table_start %llx\n", | ||
| 188 | (u64) le64_to_cpu(sblk->id_table_start)); | ||
| 189 | |||
| 190 | sb->s_maxbytes = MAX_LFS_FILESIZE; | ||
| 191 | sb->s_flags |= MS_RDONLY; | ||
| 192 | sb->s_op = &squashfs_super_ops; | ||
| 193 | |||
| 194 | err = -ENOMEM; | ||
| 195 | |||
| 196 | msblk->block_cache = squashfs_cache_init("metadata", | ||
| 197 | SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE); | ||
| 198 | if (msblk->block_cache == NULL) | ||
| 199 | goto failed_mount; | ||
| 200 | |||
| 201 | /* Allocate read_page block */ | ||
| 202 | msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size); | ||
| 203 | if (msblk->read_page == NULL) { | ||
| 204 | ERROR("Failed to allocate read_page block\n"); | ||
| 205 | goto failed_mount; | ||
| 206 | } | ||
| 207 | |||
| 208 | /* Allocate and read id index table */ | ||
| 209 | msblk->id_table = squashfs_read_id_index_table(sb, | ||
| 210 | le64_to_cpu(sblk->id_table_start), le16_to_cpu(sblk->no_ids)); | ||
| 211 | if (IS_ERR(msblk->id_table)) { | ||
| 212 | err = PTR_ERR(msblk->id_table); | ||
| 213 | msblk->id_table = NULL; | ||
| 214 | goto failed_mount; | ||
| 215 | } | ||
| 216 | |||
| 217 | fragments = le32_to_cpu(sblk->fragments); | ||
| 218 | if (fragments == 0) | ||
| 219 | goto allocate_lookup_table; | ||
| 220 | |||
| 221 | msblk->fragment_cache = squashfs_cache_init("fragment", | ||
| 222 | SQUASHFS_CACHED_FRAGMENTS, msblk->block_size); | ||
| 223 | if (msblk->fragment_cache == NULL) { | ||
| 224 | err = -ENOMEM; | ||
| 225 | goto failed_mount; | ||
| 226 | } | ||
| 227 | |||
| 228 | /* Allocate and read fragment index table */ | ||
| 229 | msblk->fragment_index = squashfs_read_fragment_index_table(sb, | ||
| 230 | le64_to_cpu(sblk->fragment_table_start), fragments); | ||
| 231 | if (IS_ERR(msblk->fragment_index)) { | ||
| 232 | err = PTR_ERR(msblk->fragment_index); | ||
| 233 | msblk->fragment_index = NULL; | ||
| 234 | goto failed_mount; | ||
| 235 | } | ||
| 236 | |||
| 237 | allocate_lookup_table: | ||
| 238 | lookup_table_start = le64_to_cpu(sblk->lookup_table_start); | ||
| 239 | if (lookup_table_start == SQUASHFS_INVALID_BLK) | ||
| 240 | goto allocate_root; | ||
| 241 | |||
| 242 | /* Allocate and read inode lookup table */ | ||
| 243 | msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb, | ||
| 244 | lookup_table_start, msblk->inodes); | ||
| 245 | if (IS_ERR(msblk->inode_lookup_table)) { | ||
| 246 | err = PTR_ERR(msblk->inode_lookup_table); | ||
| 247 | msblk->inode_lookup_table = NULL; | ||
| 248 | goto failed_mount; | ||
| 249 | } | ||
| 250 | |||
| 251 | sb->s_export_op = &squashfs_export_ops; | ||
| 252 | |||
| 253 | allocate_root: | ||
| 254 | root = new_inode(sb); | ||
| 255 | if (!root) { | ||
| 256 | err = -ENOMEM; | ||
| 257 | goto failed_mount; | ||
| 258 | } | ||
| 259 | |||
| 260 | err = squashfs_read_inode(root, root_inode); | ||
| 261 | if (err) { | ||
| 262 | iget_failed(root); | ||
| 263 | goto failed_mount; | ||
| 264 | } | ||
| 265 | insert_inode_hash(root); | ||
| 266 | |||
| 267 | sb->s_root = d_alloc_root(root); | ||
| 268 | if (sb->s_root == NULL) { | ||
| 269 | ERROR("Root inode create failed\n"); | ||
| 270 | err = -ENOMEM; | ||
| 271 | iput(root); | ||
| 272 | goto failed_mount; | ||
| 273 | } | ||
| 274 | |||
| 275 | TRACE("Leaving squashfs_fill_super\n"); | ||
| 276 | kfree(sblk); | ||
| 277 | return 0; | ||
| 278 | |||
| 279 | failed_mount: | ||
| 280 | squashfs_cache_delete(msblk->block_cache); | ||
| 281 | squashfs_cache_delete(msblk->fragment_cache); | ||
| 282 | squashfs_cache_delete(msblk->read_page); | ||
| 283 | kfree(msblk->inode_lookup_table); | ||
| 284 | kfree(msblk->fragment_index); | ||
| 285 | kfree(msblk->id_table); | ||
| 286 | kfree(msblk->stream.workspace); | ||
| 287 | kfree(sb->s_fs_info); | ||
| 288 | sb->s_fs_info = NULL; | ||
| 289 | kfree(sblk); | ||
| 290 | return err; | ||
| 291 | |||
| 292 | failure: | ||
| 293 | kfree(msblk->stream.workspace); | ||
| 294 | kfree(sb->s_fs_info); | ||
| 295 | sb->s_fs_info = NULL; | ||
| 296 | return -ENOMEM; | ||
| 297 | } | ||
| 298 | |||
| 299 | |||
| 300 | static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) | ||
| 301 | { | ||
| 302 | struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info; | ||
| 303 | |||
| 304 | TRACE("Entered squashfs_statfs\n"); | ||
| 305 | |||
| 306 | buf->f_type = SQUASHFS_MAGIC; | ||
| 307 | buf->f_bsize = msblk->block_size; | ||
| 308 | buf->f_blocks = ((msblk->bytes_used - 1) >> msblk->block_log) + 1; | ||
| 309 | buf->f_bfree = buf->f_bavail = 0; | ||
| 310 | buf->f_files = msblk->inodes; | ||
| 311 | buf->f_ffree = 0; | ||
| 312 | buf->f_namelen = SQUASHFS_NAME_LEN; | ||
| 313 | |||
| 314 | return 0; | ||
| 315 | } | ||
| 316 | |||
| 317 | |||
| 318 | static int squashfs_remount(struct super_block *sb, int *flags, char *data) | ||
| 319 | { | ||
| 320 | *flags |= MS_RDONLY; | ||
| 321 | return 0; | ||
| 322 | } | ||
| 323 | |||
| 324 | |||
| 325 | static void squashfs_put_super(struct super_block *sb) | ||
| 326 | { | ||
| 327 | if (sb->s_fs_info) { | ||
| 328 | struct squashfs_sb_info *sbi = sb->s_fs_info; | ||
| 329 | squashfs_cache_delete(sbi->block_cache); | ||
| 330 | squashfs_cache_delete(sbi->fragment_cache); | ||
| 331 | squashfs_cache_delete(sbi->read_page); | ||
| 332 | kfree(sbi->id_table); | ||
| 333 | kfree(sbi->fragment_index); | ||
| 334 | kfree(sbi->meta_index); | ||
| 335 | kfree(sbi->stream.workspace); | ||
| 336 | kfree(sb->s_fs_info); | ||
| 337 | sb->s_fs_info = NULL; | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | |||
| 342 | static int squashfs_get_sb(struct file_system_type *fs_type, int flags, | ||
| 343 | const char *dev_name, void *data, | ||
| 344 | struct vfsmount *mnt) | ||
| 345 | { | ||
| 346 | return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super, | ||
| 347 | mnt); | ||
| 348 | } | ||
| 349 | |||
| 350 | |||
| 351 | static struct kmem_cache *squashfs_inode_cachep; | ||
| 352 | |||
| 353 | |||
| 354 | static void init_once(void *foo) | ||
| 355 | { | ||
| 356 | struct squashfs_inode_info *ei = foo; | ||
| 357 | |||
| 358 | inode_init_once(&ei->vfs_inode); | ||
| 359 | } | ||
| 360 | |||
| 361 | |||
| 362 | static int __init init_inodecache(void) | ||
| 363 | { | ||
| 364 | squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache", | ||
| 365 | sizeof(struct squashfs_inode_info), 0, | ||
| 366 | SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, init_once); | ||
| 367 | |||
| 368 | return squashfs_inode_cachep ? 0 : -ENOMEM; | ||
| 369 | } | ||
| 370 | |||
| 371 | |||
| 372 | static void destroy_inodecache(void) | ||
| 373 | { | ||
| 374 | kmem_cache_destroy(squashfs_inode_cachep); | ||
| 375 | } | ||
| 376 | |||
| 377 | |||
| 378 | static int __init init_squashfs_fs(void) | ||
| 379 | { | ||
| 380 | int err = init_inodecache(); | ||
| 381 | |||
| 382 | if (err) | ||
| 383 | return err; | ||
| 384 | |||
| 385 | err = register_filesystem(&squashfs_fs_type); | ||
| 386 | if (err) { | ||
| 387 | destroy_inodecache(); | ||
| 388 | return err; | ||
| 389 | } | ||
| 390 | |||
| 391 | printk(KERN_INFO "squashfs: version 4.0 (2009/01/03) " | ||
| 392 | "Phillip Lougher\n"); | ||
| 393 | |||
| 394 | return 0; | ||
| 395 | } | ||
| 396 | |||
| 397 | |||
| 398 | static void __exit exit_squashfs_fs(void) | ||
| 399 | { | ||
| 400 | unregister_filesystem(&squashfs_fs_type); | ||
| 401 | destroy_inodecache(); | ||
| 402 | } | ||
| 403 | |||
| 404 | |||
| 405 | static struct inode *squashfs_alloc_inode(struct super_block *sb) | ||
| 406 | { | ||
| 407 | struct squashfs_inode_info *ei = | ||
| 408 | kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL); | ||
| 409 | |||
| 410 | return ei ? &ei->vfs_inode : NULL; | ||
| 411 | } | ||
| 412 | |||
| 413 | |||
| 414 | static void squashfs_destroy_inode(struct inode *inode) | ||
| 415 | { | ||
| 416 | kmem_cache_free(squashfs_inode_cachep, squashfs_i(inode)); | ||
| 417 | } | ||
| 418 | |||
| 419 | |||
| 420 | static struct file_system_type squashfs_fs_type = { | ||
| 421 | .owner = THIS_MODULE, | ||
| 422 | .name = "squashfs", | ||
| 423 | .get_sb = squashfs_get_sb, | ||
| 424 | .kill_sb = kill_block_super, | ||
| 425 | .fs_flags = FS_REQUIRES_DEV | ||
| 426 | }; | ||
| 427 | |||
| 428 | static struct super_operations squashfs_super_ops = { | ||
| 429 | .alloc_inode = squashfs_alloc_inode, | ||
| 430 | .destroy_inode = squashfs_destroy_inode, | ||
| 431 | .statfs = squashfs_statfs, | ||
| 432 | .put_super = squashfs_put_super, | ||
| 433 | .remount_fs = squashfs_remount | ||
| 434 | }; | ||
| 435 | |||
| 436 | module_init(init_squashfs_fs); | ||
| 437 | module_exit(exit_squashfs_fs); | ||
| 438 | MODULE_DESCRIPTION("squashfs 4.0, a compressed read-only filesystem"); | ||
| 439 | MODULE_AUTHOR("Phillip Lougher <phillip@lougher.demon.co.uk>"); | ||
| 440 | MODULE_LICENSE("GPL"); | ||
diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c new file mode 100644 index 000000000000..83d87880aac8 --- /dev/null +++ b/fs/squashfs/symlink.c | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | /* | ||
| 2 | * Squashfs - a compressed read only filesystem for Linux | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | ||
| 5 | * Phillip Lougher <phillip@lougher.demon.co.uk> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2, | ||
| 10 | * or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 20 | * | ||
| 21 | * symlink.c | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This file implements code to handle symbolic links. | ||
| 26 | * | ||
| 27 | * The data contents of symbolic links are stored inside the symbolic | ||
| 28 | * link inode within the inode table. This allows the normally small symbolic | ||
| 29 | * link to be compressed as part of the inode table, achieving much greater | ||
| 30 | * compression than if the symbolic link was compressed individually. | ||
| 31 | */ | ||
| 32 | |||
| 33 | #include <linux/fs.h> | ||
| 34 | #include <linux/vfs.h> | ||
| 35 | #include <linux/kernel.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <linux/string.h> | ||
| 38 | #include <linux/pagemap.h> | ||
| 39 | #include <linux/zlib.h> | ||
| 40 | |||
| 41 | #include "squashfs_fs.h" | ||
| 42 | #include "squashfs_fs_sb.h" | ||
| 43 | #include "squashfs_fs_i.h" | ||
| 44 | #include "squashfs.h" | ||
| 45 | |||
| 46 | static int squashfs_symlink_readpage(struct file *file, struct page *page) | ||
| 47 | { | ||
| 48 | struct inode *inode = page->mapping->host; | ||
| 49 | struct super_block *sb = inode->i_sb; | ||
| 50 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 51 | int index = page->index << PAGE_CACHE_SHIFT; | ||
| 52 | u64 block = squashfs_i(inode)->start; | ||
| 53 | int offset = squashfs_i(inode)->offset; | ||
| 54 | int length = min_t(int, i_size_read(inode) - index, PAGE_CACHE_SIZE); | ||
| 55 | int bytes, copied; | ||
| 56 | void *pageaddr; | ||
| 57 | struct squashfs_cache_entry *entry; | ||
| 58 | |||
| 59 | TRACE("Entered squashfs_symlink_readpage, page index %ld, start block " | ||
| 60 | "%llx, offset %x\n", page->index, block, offset); | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Skip index bytes into symlink metadata. | ||
| 64 | */ | ||
| 65 | if (index) { | ||
| 66 | bytes = squashfs_read_metadata(sb, NULL, &block, &offset, | ||
| 67 | index); | ||
| 68 | if (bytes < 0) { | ||
| 69 | ERROR("Unable to read symlink [%llx:%x]\n", | ||
| 70 | squashfs_i(inode)->start, | ||
| 71 | squashfs_i(inode)->offset); | ||
| 72 | goto error_out; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /* | ||
| 77 | * Read length bytes from symlink metadata. Squashfs_read_metadata | ||
| 78 | * is not used here because it can sleep and we want to use | ||
| 79 | * kmap_atomic to map the page. Instead call the underlying | ||
| 80 | * squashfs_cache_get routine. As length bytes may overlap metadata | ||
| 81 | * blocks, we may need to call squashfs_cache_get multiple times. | ||
| 82 | */ | ||
| 83 | for (bytes = 0; bytes < length; offset = 0, bytes += copied) { | ||
| 84 | entry = squashfs_cache_get(sb, msblk->block_cache, block, 0); | ||
| 85 | if (entry->error) { | ||
| 86 | ERROR("Unable to read symlink [%llx:%x]\n", | ||
| 87 | squashfs_i(inode)->start, | ||
| 88 | squashfs_i(inode)->offset); | ||
| 89 | squashfs_cache_put(entry); | ||
| 90 | goto error_out; | ||
| 91 | } | ||
| 92 | |||
| 93 | pageaddr = kmap_atomic(page, KM_USER0); | ||
| 94 | copied = squashfs_copy_data(pageaddr + bytes, entry, offset, | ||
| 95 | length - bytes); | ||
| 96 | if (copied == length - bytes) | ||
| 97 | memset(pageaddr + length, 0, PAGE_CACHE_SIZE - length); | ||
| 98 | else | ||
| 99 | block = entry->next_index; | ||
| 100 | kunmap_atomic(pageaddr, KM_USER0); | ||
| 101 | squashfs_cache_put(entry); | ||
| 102 | } | ||
| 103 | |||
| 104 | flush_dcache_page(page); | ||
| 105 | SetPageUptodate(page); | ||
| 106 | unlock_page(page); | ||
| 107 | return 0; | ||
| 108 | |||
| 109 | error_out: | ||
| 110 | SetPageError(page); | ||
| 111 | unlock_page(page); | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | |||
| 116 | const struct address_space_operations squashfs_symlink_aops = { | ||
| 117 | .readpage = squashfs_symlink_readpage | ||
| 118 | }; | ||
diff --git a/init/do_mounts_rd.c b/init/do_mounts_rd.c index a7c748fa977a..0f0f0cf3ba9a 100644 --- a/init/do_mounts_rd.c +++ b/init/do_mounts_rd.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <linux/string.h> | 9 | #include <linux/string.h> |
| 10 | 10 | ||
| 11 | #include "do_mounts.h" | 11 | #include "do_mounts.h" |
| 12 | #include "../fs/squashfs/squashfs_fs.h" | ||
| 12 | 13 | ||
| 13 | int __initdata rd_prompt = 1;/* 1 = prompt for RAM disk, 0 = don't prompt */ | 14 | int __initdata rd_prompt = 1;/* 1 = prompt for RAM disk, 0 = don't prompt */ |
| 14 | 15 | ||
| @@ -41,6 +42,7 @@ static int __init crd_load(int in_fd, int out_fd); | |||
| 41 | * ext2 | 42 | * ext2 |
| 42 | * romfs | 43 | * romfs |
| 43 | * cramfs | 44 | * cramfs |
| 45 | * squashfs | ||
| 44 | * gzip | 46 | * gzip |
| 45 | */ | 47 | */ |
| 46 | static int __init | 48 | static int __init |
| @@ -51,6 +53,7 @@ identify_ramdisk_image(int fd, int start_block) | |||
| 51 | struct ext2_super_block *ext2sb; | 53 | struct ext2_super_block *ext2sb; |
| 52 | struct romfs_super_block *romfsb; | 54 | struct romfs_super_block *romfsb; |
| 53 | struct cramfs_super *cramfsb; | 55 | struct cramfs_super *cramfsb; |
| 56 | struct squashfs_super_block *squashfsb; | ||
| 54 | int nblocks = -1; | 57 | int nblocks = -1; |
| 55 | unsigned char *buf; | 58 | unsigned char *buf; |
| 56 | 59 | ||
| @@ -62,6 +65,7 @@ identify_ramdisk_image(int fd, int start_block) | |||
| 62 | ext2sb = (struct ext2_super_block *) buf; | 65 | ext2sb = (struct ext2_super_block *) buf; |
| 63 | romfsb = (struct romfs_super_block *) buf; | 66 | romfsb = (struct romfs_super_block *) buf; |
| 64 | cramfsb = (struct cramfs_super *) buf; | 67 | cramfsb = (struct cramfs_super *) buf; |
| 68 | squashfsb = (struct squashfs_super_block *) buf; | ||
| 65 | memset(buf, 0xe5, size); | 69 | memset(buf, 0xe5, size); |
| 66 | 70 | ||
| 67 | /* | 71 | /* |
| @@ -99,6 +103,16 @@ identify_ramdisk_image(int fd, int start_block) | |||
| 99 | goto done; | 103 | goto done; |
| 100 | } | 104 | } |
| 101 | 105 | ||
| 106 | /* squashfs is at block zero too */ | ||
| 107 | if (le32_to_cpu(squashfsb->s_magic) == SQUASHFS_MAGIC) { | ||
| 108 | printk(KERN_NOTICE | ||
| 109 | "RAMDISK: squashfs filesystem found at block %d\n", | ||
| 110 | start_block); | ||
| 111 | nblocks = (le64_to_cpu(squashfsb->bytes_used) + BLOCK_SIZE - 1) | ||
| 112 | >> BLOCK_SIZE_BITS; | ||
| 113 | goto done; | ||
| 114 | } | ||
| 115 | |||
| 102 | /* | 116 | /* |
| 103 | * Read block 1 to test for minix and ext2 superblock | 117 | * Read block 1 to test for minix and ext2 superblock |
| 104 | */ | 118 | */ |
