Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Compressed rom filesystem for Linux. | |
3 | * | |
4 | * Copyright (C) 1999 Linus Torvalds. | |
5 | * | |
6 | * This file is released under the GPL. | |
7 | */ | |
8 | ||
9 | /* | |
10 | * These are the VFS interfaces to the compressed rom filesystem. | |
11 | * The actual compression is based on zlib, see the other files. | |
12 | */ | |
13 | ||
4f21e1ea FF |
14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
15 | ||
1da177e4 LT |
16 | #include <linux/module.h> |
17 | #include <linux/fs.h> | |
18 | #include <linux/pagemap.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/string.h> | |
21 | #include <linux/blkdev.h> | |
99c18ce5 NP |
22 | #include <linux/mtd/mtd.h> |
23 | #include <linux/mtd/super.h> | |
1da177e4 | 24 | #include <linux/slab.h> |
1da177e4 | 25 | #include <linux/vfs.h> |
353ab6e9 | 26 | #include <linux/mutex.h> |
f7f4f4dd | 27 | #include <uapi/linux/cramfs_fs.h> |
1508f3eb | 28 | #include <linux/uaccess.h> |
1da177e4 | 29 | |
f7f4f4dd AV |
30 | #include "internal.h" |
31 | ||
32 | /* | |
33 | * cramfs super-block data in memory | |
34 | */ | |
35 | struct cramfs_sb_info { | |
36 | unsigned long magic; | |
37 | unsigned long size; | |
38 | unsigned long blocks; | |
39 | unsigned long files; | |
40 | unsigned long flags; | |
99c18ce5 NP |
41 | void *linear_virt_addr; |
42 | resource_size_t linear_phys_addr; | |
43 | size_t mtd_point_size; | |
f7f4f4dd AV |
44 | }; |
45 | ||
46 | static inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb) | |
47 | { | |
48 | return sb->s_fs_info; | |
49 | } | |
50 | ||
ee9b6d61 | 51 | static const struct super_operations cramfs_ops; |
754661f1 | 52 | static const struct inode_operations cramfs_dir_inode_operations; |
4b6f5d20 | 53 | static const struct file_operations cramfs_directory_operations; |
f5e54d6e | 54 | static const struct address_space_operations cramfs_aops; |
1da177e4 | 55 | |
353ab6e9 | 56 | static DEFINE_MUTEX(read_mutex); |
1da177e4 LT |
57 | |
58 | ||
6f772fe6 | 59 | /* These macros may change in future, to provide better st_ino semantics. */ |
1da177e4 LT |
60 | #define OFFSET(x) ((x)->i_ino) |
61 | ||
0577d1ba | 62 | static unsigned long cramino(const struct cramfs_inode *cino, unsigned int offset) |
a97c9bf3 | 63 | { |
6f772fe6 SS |
64 | if (!cino->offset) |
65 | return offset + 1; | |
66 | if (!cino->size) | |
67 | return offset + 1; | |
68 | ||
69 | /* | |
70 | * The file mode test fixes buggy mkcramfs implementations where | |
71 | * cramfs_inode->offset is set to a non zero value for entries | |
72 | * which did not contain data, like devices node and fifos. | |
73 | */ | |
74 | switch (cino->mode & S_IFMT) { | |
75 | case S_IFREG: | |
76 | case S_IFDIR: | |
77 | case S_IFLNK: | |
78 | return cino->offset << 2; | |
79 | default: | |
80 | break; | |
81 | } | |
82 | return offset + 1; | |
83 | } | |
84 | ||
85 | static struct inode *get_cramfs_inode(struct super_block *sb, | |
0577d1ba | 86 | const struct cramfs_inode *cramfs_inode, unsigned int offset) |
6f772fe6 SS |
87 | { |
88 | struct inode *inode; | |
77b8a75f | 89 | static struct timespec zerotime; |
6f772fe6 SS |
90 | |
91 | inode = iget_locked(sb, cramino(cramfs_inode, offset)); | |
92 | if (!inode) | |
93 | return ERR_PTR(-ENOMEM); | |
94 | if (!(inode->i_state & I_NEW)) | |
95 | return inode; | |
96 | ||
97 | switch (cramfs_inode->mode & S_IFMT) { | |
98 | case S_IFREG: | |
99 | inode->i_fop = &generic_ro_fops; | |
100 | inode->i_data.a_ops = &cramfs_aops; | |
101 | break; | |
102 | case S_IFDIR: | |
103 | inode->i_op = &cramfs_dir_inode_operations; | |
104 | inode->i_fop = &cramfs_directory_operations; | |
105 | break; | |
106 | case S_IFLNK: | |
107 | inode->i_op = &page_symlink_inode_operations; | |
21fc61c7 | 108 | inode_nohighmem(inode); |
6f772fe6 SS |
109 | inode->i_data.a_ops = &cramfs_aops; |
110 | break; | |
111 | default: | |
112 | init_special_inode(inode, cramfs_inode->mode, | |
113 | old_decode_dev(cramfs_inode->size)); | |
114 | } | |
115 | ||
77b8a75f | 116 | inode->i_mode = cramfs_inode->mode; |
a7d9cfe9 EB |
117 | i_uid_write(inode, cramfs_inode->uid); |
118 | i_gid_write(inode, cramfs_inode->gid); | |
6f772fe6 SS |
119 | |
120 | /* if the lower 2 bits are zero, the inode contains data */ | |
121 | if (!(inode->i_ino & 3)) { | |
122 | inode->i_size = cramfs_inode->size; | |
123 | inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1; | |
124 | } | |
125 | ||
77b8a75f AV |
126 | /* Struct copy intentional */ |
127 | inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime; | |
128 | /* inode->i_nlink is left 1 - arguably wrong for directories, | |
129 | but it's the best we can do without reading the directory | |
130 | contents. 1 yields the right result in GNU find, even | |
131 | without -noleaf option. */ | |
a97c9bf3 | 132 | |
6f772fe6 SS |
133 | unlock_new_inode(inode); |
134 | ||
1da177e4 LT |
135 | return inode; |
136 | } | |
137 | ||
138 | /* | |
139 | * We have our own block cache: don't fill up the buffer cache | |
140 | * with the rom-image, because the way the filesystem is set | |
141 | * up the accesses should be fairly regular and cached in the | |
142 | * page cache and dentry tree anyway.. | |
143 | * | |
144 | * This also acts as a way to guarantee contiguous areas of up to | |
ea1754a0 | 145 | * BLKS_PER_BUF*PAGE_SIZE, so that the caller doesn't need to |
1da177e4 LT |
146 | * worry about end-of-buffer issues even when decompressing a full |
147 | * page cache. | |
99c18ce5 NP |
148 | * |
149 | * Note: This is all optimized away at compile time when | |
150 | * CONFIG_CRAMFS_BLOCKDEV=n. | |
1da177e4 LT |
151 | */ |
152 | #define READ_BUFFERS (2) | |
153 | /* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */ | |
154 | #define NEXT_BUFFER(_ix) ((_ix) ^ 1) | |
155 | ||
156 | /* | |
157 | * BLKS_PER_BUF_SHIFT should be at least 2 to allow for "compressed" | |
158 | * data that takes up more space than the original and with unlucky | |
159 | * alignment. | |
160 | */ | |
161 | #define BLKS_PER_BUF_SHIFT (2) | |
162 | #define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT) | |
09cbfeaf | 163 | #define BUFFER_SIZE (BLKS_PER_BUF*PAGE_SIZE) |
1da177e4 LT |
164 | |
165 | static unsigned char read_buffers[READ_BUFFERS][BUFFER_SIZE]; | |
166 | static unsigned buffer_blocknr[READ_BUFFERS]; | |
31d92e55 | 167 | static struct super_block *buffer_dev[READ_BUFFERS]; |
1da177e4 LT |
168 | static int next_buffer; |
169 | ||
170 | /* | |
99c18ce5 | 171 | * Populate our block cache and return a pointer to it. |
1da177e4 | 172 | */ |
99c18ce5 NP |
173 | static void *cramfs_blkdev_read(struct super_block *sb, unsigned int offset, |
174 | unsigned int len) | |
1da177e4 LT |
175 | { |
176 | struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; | |
177 | struct page *pages[BLKS_PER_BUF]; | |
6bbfb077 | 178 | unsigned i, blocknr, buffer; |
1da177e4 LT |
179 | unsigned long devsize; |
180 | char *data; | |
181 | ||
182 | if (!len) | |
183 | return NULL; | |
09cbfeaf KS |
184 | blocknr = offset >> PAGE_SHIFT; |
185 | offset &= PAGE_SIZE - 1; | |
1da177e4 LT |
186 | |
187 | /* Check if an existing buffer already has the data.. */ | |
188 | for (i = 0; i < READ_BUFFERS; i++) { | |
189 | unsigned int blk_offset; | |
190 | ||
191 | if (buffer_dev[i] != sb) | |
192 | continue; | |
193 | if (blocknr < buffer_blocknr[i]) | |
194 | continue; | |
09cbfeaf | 195 | blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_SHIFT; |
1da177e4 LT |
196 | blk_offset += offset; |
197 | if (blk_offset + len > BUFFER_SIZE) | |
198 | continue; | |
199 | return read_buffers[i] + blk_offset; | |
200 | } | |
201 | ||
09cbfeaf | 202 | devsize = mapping->host->i_size >> PAGE_SHIFT; |
1da177e4 LT |
203 | |
204 | /* Ok, read in BLKS_PER_BUF pages completely first. */ | |
1da177e4 LT |
205 | for (i = 0; i < BLKS_PER_BUF; i++) { |
206 | struct page *page = NULL; | |
207 | ||
208 | if (blocknr + i < devsize) { | |
67f9fd91 | 209 | page = read_mapping_page(mapping, blocknr + i, NULL); |
1da177e4 LT |
210 | /* synchronous error? */ |
211 | if (IS_ERR(page)) | |
212 | page = NULL; | |
213 | } | |
214 | pages[i] = page; | |
215 | } | |
216 | ||
217 | for (i = 0; i < BLKS_PER_BUF; i++) { | |
218 | struct page *page = pages[i]; | |
31d92e55 | 219 | |
1da177e4 LT |
220 | if (page) { |
221 | wait_on_page_locked(page); | |
222 | if (!PageUptodate(page)) { | |
223 | /* asynchronous error */ | |
09cbfeaf | 224 | put_page(page); |
1da177e4 LT |
225 | pages[i] = NULL; |
226 | } | |
227 | } | |
228 | } | |
229 | ||
230 | buffer = next_buffer; | |
231 | next_buffer = NEXT_BUFFER(buffer); | |
232 | buffer_blocknr[buffer] = blocknr; | |
233 | buffer_dev[buffer] = sb; | |
234 | ||
235 | data = read_buffers[buffer]; | |
236 | for (i = 0; i < BLKS_PER_BUF; i++) { | |
237 | struct page *page = pages[i]; | |
31d92e55 | 238 | |
1da177e4 | 239 | if (page) { |
09cbfeaf | 240 | memcpy(data, kmap(page), PAGE_SIZE); |
1da177e4 | 241 | kunmap(page); |
09cbfeaf | 242 | put_page(page); |
1da177e4 | 243 | } else |
09cbfeaf KS |
244 | memset(data, 0, PAGE_SIZE); |
245 | data += PAGE_SIZE; | |
1da177e4 LT |
246 | } |
247 | return read_buffers[buffer] + offset; | |
248 | } | |
249 | ||
99c18ce5 NP |
250 | /* |
251 | * Return a pointer to the linearly addressed cramfs image in memory. | |
252 | */ | |
253 | static void *cramfs_direct_read(struct super_block *sb, unsigned int offset, | |
254 | unsigned int len) | |
255 | { | |
256 | struct cramfs_sb_info *sbi = CRAMFS_SB(sb); | |
257 | ||
258 | if (!len) | |
259 | return NULL; | |
260 | if (len > sbi->size || offset > sbi->size - len) | |
261 | return page_address(ZERO_PAGE(0)); | |
262 | return sbi->linear_virt_addr + offset; | |
263 | } | |
264 | ||
265 | /* | |
266 | * Returns a pointer to a buffer containing at least LEN bytes of | |
267 | * filesystem starting at byte offset OFFSET into the filesystem. | |
268 | */ | |
269 | static void *cramfs_read(struct super_block *sb, unsigned int offset, | |
270 | unsigned int len) | |
271 | { | |
272 | struct cramfs_sb_info *sbi = CRAMFS_SB(sb); | |
273 | ||
274 | if (IS_ENABLED(CONFIG_CRAMFS_MTD) && sbi->linear_virt_addr) | |
275 | return cramfs_direct_read(sb, offset, len); | |
276 | else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) | |
277 | return cramfs_blkdev_read(sb, offset, len); | |
278 | else | |
279 | return NULL; | |
280 | } | |
281 | ||
2309fb8e | 282 | static void cramfs_kill_sb(struct super_block *sb) |
1da177e4 | 283 | { |
f7f4f4dd | 284 | struct cramfs_sb_info *sbi = CRAMFS_SB(sb); |
31d92e55 | 285 | |
99c18ce5 NP |
286 | if (IS_ENABLED(CCONFIG_CRAMFS_MTD) && sb->s_mtd) { |
287 | if (sbi && sbi->mtd_point_size) | |
288 | mtd_unpoint(sb->s_mtd, 0, sbi->mtd_point_size); | |
289 | kill_mtd_super(sb); | |
290 | } else if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV) && sb->s_bdev) { | |
291 | kill_block_super(sb); | |
292 | } | |
2309fb8e | 293 | kfree(sbi); |
1da177e4 LT |
294 | } |
295 | ||
296 | static int cramfs_remount(struct super_block *sb, int *flags, char *data) | |
297 | { | |
02b9984d | 298 | sync_filesystem(sb); |
1da177e4 LT |
299 | *flags |= MS_RDONLY; |
300 | return 0; | |
301 | } | |
302 | ||
99c18ce5 NP |
303 | static int cramfs_read_super(struct super_block *sb, |
304 | struct cramfs_super *super, int silent) | |
1da177e4 | 305 | { |
99c18ce5 | 306 | struct cramfs_sb_info *sbi = CRAMFS_SB(sb); |
1da177e4 | 307 | unsigned long root_offset; |
1da177e4 | 308 | |
99c18ce5 NP |
309 | /* We don't know the real size yet */ |
310 | sbi->size = PAGE_SIZE; | |
1da177e4 LT |
311 | |
312 | /* Read the first block and get the superblock from it */ | |
99c18ce5 NP |
313 | mutex_lock(&read_mutex); |
314 | memcpy(super, cramfs_read(sb, 0, sizeof(*super)), sizeof(*super)); | |
353ab6e9 | 315 | mutex_unlock(&read_mutex); |
1da177e4 LT |
316 | |
317 | /* Do sanity checks on the superblock */ | |
99c18ce5 | 318 | if (super->magic != CRAMFS_MAGIC) { |
0cc785ec | 319 | /* check for wrong endianness */ |
99c18ce5 | 320 | if (super->magic == CRAMFS_MAGIC_WEND) { |
ac8d35c5 | 321 | if (!silent) |
4f21e1ea | 322 | pr_err("wrong endianness\n"); |
2309fb8e | 323 | return -EINVAL; |
ac8d35c5 AD |
324 | } |
325 | ||
1da177e4 | 326 | /* check at 512 byte offset */ |
353ab6e9 | 327 | mutex_lock(&read_mutex); |
99c18ce5 NP |
328 | memcpy(super, |
329 | cramfs_read(sb, 512, sizeof(*super)), | |
330 | sizeof(*super)); | |
353ab6e9 | 331 | mutex_unlock(&read_mutex); |
99c18ce5 NP |
332 | if (super->magic != CRAMFS_MAGIC) { |
333 | if (super->magic == CRAMFS_MAGIC_WEND && !silent) | |
4f21e1ea | 334 | pr_err("wrong endianness\n"); |
ac8d35c5 | 335 | else if (!silent) |
4f21e1ea | 336 | pr_err("wrong magic\n"); |
2309fb8e | 337 | return -EINVAL; |
1da177e4 LT |
338 | } |
339 | } | |
340 | ||
341 | /* get feature flags first */ | |
99c18ce5 | 342 | if (super->flags & ~CRAMFS_SUPPORTED_FLAGS) { |
4f21e1ea | 343 | pr_err("unsupported filesystem features\n"); |
2309fb8e | 344 | return -EINVAL; |
1da177e4 LT |
345 | } |
346 | ||
347 | /* Check that the root inode is in a sane state */ | |
99c18ce5 | 348 | if (!S_ISDIR(super->root.mode)) { |
4f21e1ea | 349 | pr_err("root is not a directory\n"); |
2309fb8e | 350 | return -EINVAL; |
1da177e4 | 351 | } |
6f772fe6 | 352 | /* correct strange, hard-coded permissions of mkcramfs */ |
99c18ce5 | 353 | super->root.mode |= 0555; |
6f772fe6 | 354 | |
99c18ce5 NP |
355 | root_offset = super->root.offset << 2; |
356 | if (super->flags & CRAMFS_FLAG_FSID_VERSION_2) { | |
357 | sbi->size = super->size; | |
358 | sbi->blocks = super->fsid.blocks; | |
359 | sbi->files = super->fsid.files; | |
1da177e4 | 360 | } else { |
31d92e55 FF |
361 | sbi->size = 1<<28; |
362 | sbi->blocks = 0; | |
363 | sbi->files = 0; | |
1da177e4 | 364 | } |
99c18ce5 NP |
365 | sbi->magic = super->magic; |
366 | sbi->flags = super->flags; | |
1da177e4 | 367 | if (root_offset == 0) |
4f21e1ea | 368 | pr_info("empty filesystem"); |
99c18ce5 | 369 | else if (!(super->flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && |
1da177e4 LT |
370 | ((root_offset != sizeof(struct cramfs_super)) && |
371 | (root_offset != 512 + sizeof(struct cramfs_super)))) | |
372 | { | |
4f21e1ea | 373 | pr_err("bad root offset %lu\n", root_offset); |
2309fb8e | 374 | return -EINVAL; |
1da177e4 LT |
375 | } |
376 | ||
99c18ce5 NP |
377 | return 0; |
378 | } | |
379 | ||
380 | static int cramfs_finalize_super(struct super_block *sb, | |
381 | struct cramfs_inode *cramfs_root) | |
382 | { | |
383 | struct inode *root; | |
384 | ||
1da177e4 | 385 | /* Set it all up.. */ |
99c18ce5 | 386 | sb->s_flags |= MS_RDONLY; |
1da177e4 | 387 | sb->s_op = &cramfs_ops; |
99c18ce5 | 388 | root = get_cramfs_inode(sb, cramfs_root, 0); |
0577d1ba | 389 | if (IS_ERR(root)) |
2309fb8e | 390 | return PTR_ERR(root); |
48fde701 AV |
391 | sb->s_root = d_make_root(root); |
392 | if (!sb->s_root) | |
2309fb8e | 393 | return -ENOMEM; |
1da177e4 | 394 | return 0; |
1da177e4 LT |
395 | } |
396 | ||
99c18ce5 NP |
397 | static int cramfs_blkdev_fill_super(struct super_block *sb, void *data, |
398 | int silent) | |
399 | { | |
400 | struct cramfs_sb_info *sbi; | |
401 | struct cramfs_super super; | |
402 | int i, err; | |
403 | ||
404 | sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); | |
405 | if (!sbi) | |
406 | return -ENOMEM; | |
407 | sb->s_fs_info = sbi; | |
408 | ||
409 | /* Invalidate the read buffers on mount: think disk change.. */ | |
410 | for (i = 0; i < READ_BUFFERS; i++) | |
411 | buffer_blocknr[i] = -1; | |
412 | ||
413 | err = cramfs_read_super(sb, &super, silent); | |
414 | if (err) | |
415 | return err; | |
416 | return cramfs_finalize_super(sb, &super.root); | |
417 | } | |
418 | ||
419 | static int cramfs_mtd_fill_super(struct super_block *sb, void *data, | |
420 | int silent) | |
421 | { | |
422 | struct cramfs_sb_info *sbi; | |
423 | struct cramfs_super super; | |
424 | int err; | |
425 | ||
426 | sbi = kzalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL); | |
427 | if (!sbi) | |
428 | return -ENOMEM; | |
429 | sb->s_fs_info = sbi; | |
430 | ||
431 | /* Map only one page for now. Will remap it when fs size is known. */ | |
432 | err = mtd_point(sb->s_mtd, 0, PAGE_SIZE, &sbi->mtd_point_size, | |
433 | &sbi->linear_virt_addr, &sbi->linear_phys_addr); | |
434 | if (err || sbi->mtd_point_size != PAGE_SIZE) { | |
435 | pr_err("unable to get direct memory access to mtd:%s\n", | |
436 | sb->s_mtd->name); | |
437 | return err ? : -ENODATA; | |
438 | } | |
439 | ||
440 | pr_info("checking physical address %pap for linear cramfs image\n", | |
441 | &sbi->linear_phys_addr); | |
442 | err = cramfs_read_super(sb, &super, silent); | |
443 | if (err) | |
444 | return err; | |
445 | ||
446 | /* Remap the whole filesystem now */ | |
447 | pr_info("linear cramfs image on mtd:%s appears to be %lu KB in size\n", | |
448 | sb->s_mtd->name, sbi->size/1024); | |
449 | mtd_unpoint(sb->s_mtd, 0, PAGE_SIZE); | |
450 | err = mtd_point(sb->s_mtd, 0, sbi->size, &sbi->mtd_point_size, | |
451 | &sbi->linear_virt_addr, &sbi->linear_phys_addr); | |
452 | if (err || sbi->mtd_point_size != sbi->size) { | |
453 | pr_err("unable to get direct memory access to mtd:%s\n", | |
454 | sb->s_mtd->name); | |
455 | return err ? : -ENODATA; | |
456 | } | |
457 | ||
458 | return cramfs_finalize_super(sb, &super.root); | |
459 | } | |
460 | ||
726c3342 | 461 | static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf) |
1da177e4 | 462 | { |
726c3342 | 463 | struct super_block *sb = dentry->d_sb; |
99c18ce5 NP |
464 | u64 id = 0; |
465 | ||
466 | if (sb->s_bdev) | |
467 | id = huge_encode_dev(sb->s_bdev->bd_dev); | |
468 | else if (sb->s_dev) | |
469 | id = huge_encode_dev(sb->s_dev); | |
726c3342 | 470 | |
1da177e4 | 471 | buf->f_type = CRAMFS_MAGIC; |
09cbfeaf | 472 | buf->f_bsize = PAGE_SIZE; |
1da177e4 LT |
473 | buf->f_blocks = CRAMFS_SB(sb)->blocks; |
474 | buf->f_bfree = 0; | |
475 | buf->f_bavail = 0; | |
476 | buf->f_files = CRAMFS_SB(sb)->files; | |
477 | buf->f_ffree = 0; | |
94ea77ac CL |
478 | buf->f_fsid.val[0] = (u32)id; |
479 | buf->f_fsid.val[1] = (u32)(id >> 32); | |
1da177e4 LT |
480 | buf->f_namelen = CRAMFS_MAXPATHLEN; |
481 | return 0; | |
482 | } | |
483 | ||
484 | /* | |
485 | * Read a cramfs directory entry. | |
486 | */ | |
6f7f231e | 487 | static int cramfs_readdir(struct file *file, struct dir_context *ctx) |
1da177e4 | 488 | { |
6f7f231e | 489 | struct inode *inode = file_inode(file); |
1da177e4 LT |
490 | struct super_block *sb = inode->i_sb; |
491 | char *buf; | |
492 | unsigned int offset; | |
1da177e4 LT |
493 | |
494 | /* Offset within the thing. */ | |
6f7f231e | 495 | if (ctx->pos >= inode->i_size) |
1da177e4 | 496 | return 0; |
6f7f231e | 497 | offset = ctx->pos; |
1da177e4 LT |
498 | /* Directory entries are always 4-byte aligned */ |
499 | if (offset & 3) | |
500 | return -EINVAL; | |
501 | ||
4176ed59 | 502 | buf = kmalloc(CRAMFS_MAXPATHLEN, GFP_KERNEL); |
1da177e4 LT |
503 | if (!buf) |
504 | return -ENOMEM; | |
505 | ||
1da177e4 LT |
506 | while (offset < inode->i_size) { |
507 | struct cramfs_inode *de; | |
508 | unsigned long nextoffset; | |
509 | char *name; | |
510 | ino_t ino; | |
175a4eb7 | 511 | umode_t mode; |
6f7f231e | 512 | int namelen; |
1da177e4 | 513 | |
353ab6e9 | 514 | mutex_lock(&read_mutex); |
4176ed59 | 515 | de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN); |
1da177e4 LT |
516 | name = (char *)(de+1); |
517 | ||
518 | /* | |
519 | * Namelengths on disk are shifted by two | |
520 | * and the name padded out to 4-byte boundaries | |
521 | * with zeroes. | |
522 | */ | |
523 | namelen = de->namelen << 2; | |
524 | memcpy(buf, name, namelen); | |
6f772fe6 | 525 | ino = cramino(de, OFFSET(inode) + offset); |
1da177e4 | 526 | mode = de->mode; |
353ab6e9 | 527 | mutex_unlock(&read_mutex); |
1da177e4 LT |
528 | nextoffset = offset + sizeof(*de) + namelen; |
529 | for (;;) { | |
530 | if (!namelen) { | |
531 | kfree(buf); | |
532 | return -EIO; | |
533 | } | |
534 | if (buf[namelen-1]) | |
535 | break; | |
536 | namelen--; | |
537 | } | |
6f7f231e | 538 | if (!dir_emit(ctx, buf, namelen, ino, mode >> 12)) |
1da177e4 LT |
539 | break; |
540 | ||
6f7f231e | 541 | ctx->pos = offset = nextoffset; |
1da177e4 LT |
542 | } |
543 | kfree(buf); | |
544 | return 0; | |
545 | } | |
546 | ||
547 | /* | |
548 | * Lookup and fill in the inode data.. | |
549 | */ | |
31d92e55 | 550 | static struct dentry *cramfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) |
1da177e4 LT |
551 | { |
552 | unsigned int offset = 0; | |
0577d1ba | 553 | struct inode *inode = NULL; |
1da177e4 LT |
554 | int sorted; |
555 | ||
353ab6e9 | 556 | mutex_lock(&read_mutex); |
1da177e4 LT |
557 | sorted = CRAMFS_SB(dir->i_sb)->flags & CRAMFS_FLAG_SORTED_DIRS; |
558 | while (offset < dir->i_size) { | |
559 | struct cramfs_inode *de; | |
560 | char *name; | |
561 | int namelen, retval; | |
6f772fe6 | 562 | int dir_off = OFFSET(dir) + offset; |
1da177e4 | 563 | |
6f772fe6 | 564 | de = cramfs_read(dir->i_sb, dir_off, sizeof(*de)+CRAMFS_MAXPATHLEN); |
1da177e4 LT |
565 | name = (char *)(de+1); |
566 | ||
567 | /* Try to take advantage of sorted directories */ | |
568 | if (sorted && (dentry->d_name.name[0] < name[0])) | |
569 | break; | |
570 | ||
571 | namelen = de->namelen << 2; | |
572 | offset += sizeof(*de) + namelen; | |
573 | ||
574 | /* Quick check that the name is roughly the right length */ | |
575 | if (((dentry->d_name.len + 3) & ~3) != namelen) | |
576 | continue; | |
577 | ||
578 | for (;;) { | |
579 | if (!namelen) { | |
0577d1ba AV |
580 | inode = ERR_PTR(-EIO); |
581 | goto out; | |
1da177e4 LT |
582 | } |
583 | if (name[namelen-1]) | |
584 | break; | |
585 | namelen--; | |
586 | } | |
587 | if (namelen != dentry->d_name.len) | |
588 | continue; | |
589 | retval = memcmp(dentry->d_name.name, name, namelen); | |
590 | if (retval > 0) | |
591 | continue; | |
592 | if (!retval) { | |
0577d1ba AV |
593 | inode = get_cramfs_inode(dir->i_sb, de, dir_off); |
594 | break; | |
1da177e4 LT |
595 | } |
596 | /* else (retval < 0) */ | |
597 | if (sorted) | |
598 | break; | |
599 | } | |
0577d1ba | 600 | out: |
353ab6e9 | 601 | mutex_unlock(&read_mutex); |
0577d1ba AV |
602 | if (IS_ERR(inode)) |
603 | return ERR_CAST(inode); | |
604 | d_add(dentry, inode); | |
1da177e4 LT |
605 | return NULL; |
606 | } | |
607 | ||
31d92e55 | 608 | static int cramfs_readpage(struct file *file, struct page *page) |
1da177e4 LT |
609 | { |
610 | struct inode *inode = page->mapping->host; | |
98310e58 DV |
611 | u32 maxblock; |
612 | int bytes_filled; | |
1da177e4 LT |
613 | void *pgdata; |
614 | ||
09cbfeaf | 615 | maxblock = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; |
1da177e4 | 616 | bytes_filled = 0; |
98310e58 DV |
617 | pgdata = kmap(page); |
618 | ||
1da177e4 LT |
619 | if (page->index < maxblock) { |
620 | struct super_block *sb = inode->i_sb; | |
fd4f6f2a NP |
621 | u32 blkptr_offset = OFFSET(inode) + page->index * 4; |
622 | u32 block_ptr, block_start, block_len; | |
623 | bool uncompressed, direct; | |
1da177e4 | 624 | |
353ab6e9 | 625 | mutex_lock(&read_mutex); |
fd4f6f2a NP |
626 | block_ptr = *(u32 *) cramfs_read(sb, blkptr_offset, 4); |
627 | uncompressed = (block_ptr & CRAMFS_BLK_FLAG_UNCOMPRESSED); | |
628 | direct = (block_ptr & CRAMFS_BLK_FLAG_DIRECT_PTR); | |
629 | block_ptr &= ~CRAMFS_BLK_FLAGS; | |
630 | ||
631 | if (direct) { | |
632 | /* | |
633 | * The block pointer is an absolute start pointer, | |
634 | * shifted by 2 bits. The size is included in the | |
635 | * first 2 bytes of the data block when compressed, | |
636 | * or PAGE_SIZE otherwise. | |
637 | */ | |
638 | block_start = block_ptr << CRAMFS_BLK_DIRECT_PTR_SHIFT; | |
639 | if (uncompressed) { | |
640 | block_len = PAGE_SIZE; | |
641 | /* if last block: cap to file length */ | |
642 | if (page->index == maxblock - 1) | |
643 | block_len = | |
644 | offset_in_page(inode->i_size); | |
645 | } else { | |
646 | block_len = *(u16 *) | |
647 | cramfs_read(sb, block_start, 2); | |
648 | block_start += 2; | |
649 | } | |
650 | } else { | |
651 | /* | |
652 | * The block pointer indicates one past the end of | |
653 | * the current block (start of next block). If this | |
654 | * is the first block then it starts where the block | |
655 | * pointer table ends, otherwise its start comes | |
656 | * from the previous block's pointer. | |
657 | */ | |
658 | block_start = OFFSET(inode) + maxblock * 4; | |
659 | if (page->index) | |
660 | block_start = *(u32 *) | |
661 | cramfs_read(sb, blkptr_offset - 4, 4); | |
662 | /* Beware... previous ptr might be a direct ptr */ | |
663 | if (unlikely(block_start & CRAMFS_BLK_FLAG_DIRECT_PTR)) { | |
664 | /* See comments on earlier code. */ | |
665 | u32 prev_start = block_start; | |
666 | block_start = prev_start & ~CRAMFS_BLK_FLAGS; | |
667 | block_start <<= CRAMFS_BLK_DIRECT_PTR_SHIFT; | |
668 | if (prev_start & CRAMFS_BLK_FLAG_UNCOMPRESSED) { | |
669 | block_start += PAGE_SIZE; | |
670 | } else { | |
671 | block_len = *(u16 *) | |
672 | cramfs_read(sb, block_start, 2); | |
673 | block_start += 2 + block_len; | |
674 | } | |
675 | } | |
676 | block_start &= ~CRAMFS_BLK_FLAGS; | |
677 | block_len = block_ptr - block_start; | |
678 | } | |
98310e58 | 679 | |
fd4f6f2a | 680 | if (block_len == 0) |
1da177e4 | 681 | ; /* hole */ |
fd4f6f2a NP |
682 | else if (unlikely(block_len > 2*PAGE_SIZE || |
683 | (uncompressed && block_len > PAGE_SIZE))) { | |
684 | mutex_unlock(&read_mutex); | |
685 | pr_err("bad data blocksize %u\n", block_len); | |
98310e58 | 686 | goto err; |
fd4f6f2a NP |
687 | } else if (uncompressed) { |
688 | memcpy(pgdata, | |
689 | cramfs_read(sb, block_start, block_len), | |
690 | block_len); | |
691 | bytes_filled = block_len; | |
98310e58 | 692 | } else { |
1da177e4 | 693 | bytes_filled = cramfs_uncompress_block(pgdata, |
09cbfeaf | 694 | PAGE_SIZE, |
fd4f6f2a NP |
695 | cramfs_read(sb, block_start, block_len), |
696 | block_len); | |
1da177e4 | 697 | } |
fd4f6f2a NP |
698 | mutex_unlock(&read_mutex); |
699 | if (unlikely(bytes_filled < 0)) | |
700 | goto err; | |
98310e58 DV |
701 | } |
702 | ||
09cbfeaf | 703 | memset(pgdata + bytes_filled, 0, PAGE_SIZE - bytes_filled); |
1da177e4 | 704 | flush_dcache_page(page); |
98310e58 | 705 | kunmap(page); |
1da177e4 LT |
706 | SetPageUptodate(page); |
707 | unlock_page(page); | |
708 | return 0; | |
98310e58 DV |
709 | |
710 | err: | |
711 | kunmap(page); | |
712 | ClearPageUptodate(page); | |
713 | SetPageError(page); | |
714 | unlock_page(page); | |
715 | return 0; | |
1da177e4 LT |
716 | } |
717 | ||
f5e54d6e | 718 | static const struct address_space_operations cramfs_aops = { |
1da177e4 LT |
719 | .readpage = cramfs_readpage |
720 | }; | |
721 | ||
722 | /* | |
723 | * Our operations: | |
724 | */ | |
725 | ||
726 | /* | |
727 | * A directory can only readdir | |
728 | */ | |
4b6f5d20 | 729 | static const struct file_operations cramfs_directory_operations = { |
1da177e4 LT |
730 | .llseek = generic_file_llseek, |
731 | .read = generic_read_dir, | |
c51da20c | 732 | .iterate_shared = cramfs_readdir, |
1da177e4 LT |
733 | }; |
734 | ||
754661f1 | 735 | static const struct inode_operations cramfs_dir_inode_operations = { |
1da177e4 LT |
736 | .lookup = cramfs_lookup, |
737 | }; | |
738 | ||
ee9b6d61 | 739 | static const struct super_operations cramfs_ops = { |
1da177e4 LT |
740 | .remount_fs = cramfs_remount, |
741 | .statfs = cramfs_statfs, | |
742 | }; | |
743 | ||
99c18ce5 NP |
744 | static struct dentry *cramfs_mount(struct file_system_type *fs_type, int flags, |
745 | const char *dev_name, void *data) | |
1da177e4 | 746 | { |
99c18ce5 NP |
747 | struct dentry *ret = ERR_PTR(-ENOPROTOOPT); |
748 | ||
749 | if (IS_ENABLED(CONFIG_CRAMFS_MTD)) { | |
750 | ret = mount_mtd(fs_type, flags, dev_name, data, | |
751 | cramfs_mtd_fill_super); | |
752 | if (!IS_ERR(ret)) | |
753 | return ret; | |
754 | } | |
755 | if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) { | |
756 | ret = mount_bdev(fs_type, flags, dev_name, data, | |
757 | cramfs_blkdev_fill_super); | |
758 | } | |
759 | return ret; | |
1da177e4 LT |
760 | } |
761 | ||
762 | static struct file_system_type cramfs_fs_type = { | |
763 | .owner = THIS_MODULE, | |
764 | .name = "cramfs", | |
152a0836 | 765 | .mount = cramfs_mount, |
2309fb8e | 766 | .kill_sb = cramfs_kill_sb, |
1da177e4 LT |
767 | .fs_flags = FS_REQUIRES_DEV, |
768 | }; | |
7f78e035 | 769 | MODULE_ALIAS_FS("cramfs"); |
1da177e4 LT |
770 | |
771 | static int __init init_cramfs_fs(void) | |
772 | { | |
50d44ed0 AD |
773 | int rv; |
774 | ||
775 | rv = cramfs_uncompress_init(); | |
776 | if (rv < 0) | |
777 | return rv; | |
778 | rv = register_filesystem(&cramfs_fs_type); | |
779 | if (rv < 0) | |
780 | cramfs_uncompress_exit(); | |
781 | return rv; | |
1da177e4 LT |
782 | } |
783 | ||
784 | static void __exit exit_cramfs_fs(void) | |
785 | { | |
786 | cramfs_uncompress_exit(); | |
787 | unregister_filesystem(&cramfs_fs_type); | |
788 | } | |
789 | ||
790 | module_init(init_cramfs_fs) | |
791 | module_exit(exit_cramfs_fs) | |
792 | MODULE_LICENSE("GPL"); |