Commit 31aeb6c815549948571eec988ad9728c27d7a68d
Exists in
master
and in
4 other branches
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
Showing 21 changed files Side-by-side Diff
- Documentation/filesystems/squashfs.txt
- MAINTAINERS
- fs/Kconfig
- fs/Makefile
- fs/squashfs/Makefile
- fs/squashfs/block.c
- fs/squashfs/cache.c
- fs/squashfs/dir.c
- fs/squashfs/export.c
- fs/squashfs/file.c
- fs/squashfs/fragment.c
- fs/squashfs/id.c
- fs/squashfs/inode.c
- fs/squashfs/namei.c
- fs/squashfs/squashfs.h
- fs/squashfs/squashfs_fs.h
- fs/squashfs/squashfs_fs_i.h
- fs/squashfs/squashfs_fs_sb.h
- fs/squashfs/super.c
- fs/squashfs/symlink.c
- init/do_mounts_rd.c
Documentation/filesystems/squashfs.txt
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. |
MAINTAINERS
... | ... | @@ -4081,6 +4081,13 @@ |
4081 | 4081 | W: http://www.ibm.com/developerworks/power/cell/ |
4082 | 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 | 4091 | SRM (Alpha) environment access |
4085 | 4092 | P: Jan-Benedict Glaw |
4086 | 4093 | M: jbglaw@lug-owl.de |
fs/Kconfig
... | ... | @@ -932,6 +932,58 @@ |
932 | 932 | |
933 | 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 | 987 | config VXFS_FS |
936 | 988 | tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" |
937 | 989 | depends on BLOCK |
fs/Makefile
fs/squashfs/Makefile
fs/squashfs/block.c
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 | +} |
fs/squashfs/cache.c
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 | +} |
fs/squashfs/dir.c
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 | +}; |
fs/squashfs/export.c
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 | +}; |
fs/squashfs/file.c
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 | +}; |
fs/squashfs/fragment.c
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 | +} |
fs/squashfs/id.c
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 | +} |
fs/squashfs/inode.c
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 | +} |
fs/squashfs/namei.c
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 | +}; |
fs/squashfs/squashfs.h
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; |
fs/squashfs/squashfs_fs.h
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 |
fs/squashfs/squashfs_fs_i.h
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 |
fs/squashfs/squashfs_fs_sb.h
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 |
fs/squashfs/super.c
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"); |
fs/squashfs/symlink.c
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 | +}; |
init/do_mounts_rd.c
... | ... | @@ -9,6 +9,7 @@ |
9 | 9 | #include <linux/string.h> |
10 | 10 | |
11 | 11 | #include "do_mounts.h" |
12 | +#include "../fs/squashfs/squashfs_fs.h" | |
12 | 13 | |
13 | 14 | int __initdata rd_prompt = 1;/* 1 = prompt for RAM disk, 0 = don't prompt */ |
14 | 15 | |
... | ... | @@ -41,6 +42,7 @@ |
41 | 42 | * ext2 |
42 | 43 | * romfs |
43 | 44 | * cramfs |
45 | + * squashfs | |
44 | 46 | * gzip |
45 | 47 | */ |
46 | 48 | static int __init |
... | ... | @@ -51,6 +53,7 @@ |
51 | 53 | struct ext2_super_block *ext2sb; |
52 | 54 | struct romfs_super_block *romfsb; |
53 | 55 | struct cramfs_super *cramfsb; |
56 | + struct squashfs_super_block *squashfsb; | |
54 | 57 | int nblocks = -1; |
55 | 58 | unsigned char *buf; |
56 | 59 | |
... | ... | @@ -62,6 +65,7 @@ |
62 | 65 | ext2sb = (struct ext2_super_block *) buf; |
63 | 66 | romfsb = (struct romfs_super_block *) buf; |
64 | 67 | cramfsb = (struct cramfs_super *) buf; |
68 | + squashfsb = (struct squashfs_super_block *) buf; | |
65 | 69 | memset(buf, 0xe5, size); |
66 | 70 | |
67 | 71 | /* |
... | ... | @@ -96,6 +100,16 @@ |
96 | 100 | "RAMDISK: cramfs filesystem found at block %d\n", |
97 | 101 | start_block); |
98 | 102 | nblocks = (cramfsb->size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS; |
103 | + goto done; | |
104 | + } | |
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; | |
99 | 113 | goto done; |
100 | 114 | } |
101 | 115 |