aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cramfs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/cramfs
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'fs/cramfs')
-rw-r--r--fs/cramfs/Makefile7
-rw-r--r--fs/cramfs/README168
-rw-r--r--fs/cramfs/inode.c525
-rw-r--r--fs/cramfs/uncompress.c77
4 files changed, 777 insertions, 0 deletions
diff --git a/fs/cramfs/Makefile b/fs/cramfs/Makefile
new file mode 100644
index 000000000000..92ebb464a725
--- /dev/null
+++ b/fs/cramfs/Makefile
@@ -0,0 +1,7 @@
1#
2# Makefile for the linux cramfs routines.
3#
4
5obj-$(CONFIG_CRAMFS) += cramfs.o
6
7cramfs-objs := inode.o uncompress.o
diff --git a/fs/cramfs/README b/fs/cramfs/README
new file mode 100644
index 000000000000..445d1c2d7646
--- /dev/null
+++ b/fs/cramfs/README
@@ -0,0 +1,168 @@
1Notes on Filesystem Layout
2--------------------------
3
4These notes describe what mkcramfs generates. Kernel requirements are
5a bit looser, e.g. it doesn't care if the <file_data> items are
6swapped around (though it does care that directory entries (inodes) in
7a given directory are contiguous, as this is used by readdir).
8
9All data is currently in host-endian format; neither mkcramfs nor the
10kernel ever do swabbing. (See section `Block Size' below.)
11
12<filesystem>:
13 <superblock>
14 <directory_structure>
15 <data>
16
17<superblock>: struct cramfs_super (see cramfs_fs.h).
18
19<directory_structure>:
20 For each file:
21 struct cramfs_inode (see cramfs_fs.h).
22 Filename. Not generally null-terminated, but it is
23 null-padded to a multiple of 4 bytes.
24
25The order of inode traversal is described as "width-first" (not to be
26confused with breadth-first); i.e. like depth-first but listing all of
27a directory's entries before recursing down its subdirectories: the
28same order as `ls -AUR' (but without the /^\..*:$/ directory header
29lines); put another way, the same order as `find -type d -exec
30ls -AU1 {} \;'.
31
32Beginning in 2.4.7, directory entries are sorted. This optimization
33allows cramfs_lookup to return more quickly when a filename does not
34exist, speeds up user-space directory sorts, etc.
35
36<data>:
37 One <file_data> for each file that's either a symlink or a
38 regular file of non-zero st_size.
39
40<file_data>:
41 nblocks * <block_pointer>
42 (where nblocks = (st_size - 1) / blksize + 1)
43 nblocks * <block>
44 padding to multiple of 4 bytes
45
46The i'th <block_pointer> for a file stores the byte offset of the
47*end* of the i'th <block> (i.e. one past the last byte, which is the
48same as the start of the (i+1)'th <block> if there is one). The first
49<block> immediately follows the last <block_pointer> for the file.
50<block_pointer>s are each 32 bits long.
51
52The order of <file_data>'s is a depth-first descent of the directory
53tree, i.e. the same order as `find -size +0 \( -type f -o -type l \)
54-print'.
55
56
57<block>: The i'th <block> is the output of zlib's compress function
58applied to the i'th blksize-sized chunk of the input data.
59(For the last <block> of the file, the input may of course be smaller.)
60Each <block> may be a different size. (See <block_pointer> above.)
61<block>s are merely byte-aligned, not generally u32-aligned.
62
63
64Holes
65-----
66
67This kernel supports cramfs holes (i.e. [efficient representation of]
68blocks in uncompressed data consisting entirely of NUL bytes), but by
69default mkcramfs doesn't test for & create holes, since cramfs in
70kernels up to at least 2.3.39 didn't support holes. Run mkcramfs
71with -z if you want it to create files that can have holes in them.
72
73
74Tools
75-----
76
77The cramfs user-space tools, including mkcramfs and cramfsck, are
78located at <http://sourceforge.net/projects/cramfs/>.
79
80
81Future Development
82==================
83
84Block Size
85----------
86
87(Block size in cramfs refers to the size of input data that is
88compressed at a time. It's intended to be somewhere around
89PAGE_CACHE_SIZE for cramfs_readpage's convenience.)
90
91The superblock ought to indicate the block size that the fs was
92written for, since comments in <linux/pagemap.h> indicate that
93PAGE_CACHE_SIZE may grow in future (if I interpret the comment
94correctly).
95
96Currently, mkcramfs #define's PAGE_CACHE_SIZE as 4096 and uses that
97for blksize, whereas Linux-2.3.39 uses its PAGE_CACHE_SIZE, which in
98turn is defined as PAGE_SIZE (which can be as large as 32KB on arm).
99This discrepancy is a bug, though it's not clear which should be
100changed.
101
102One option is to change mkcramfs to take its PAGE_CACHE_SIZE from
103<asm/page.h>. Personally I don't like this option, but it does
104require the least amount of change: just change `#define
105PAGE_CACHE_SIZE (4096)' to `#include <asm/page.h>'. The disadvantage
106is that the generated cramfs cannot always be shared between different
107kernels, not even necessarily kernels of the same architecture if
108PAGE_CACHE_SIZE is subject to change between kernel versions
109(currently possible with arm and ia64).
110
111The remaining options try to make cramfs more sharable.
112
113One part of that is addressing endianness. The two options here are
114`always use little-endian' (like ext2fs) or `writer chooses
115endianness; kernel adapts at runtime'. Little-endian wins because of
116code simplicity and little CPU overhead even on big-endian machines.
117
118The cost of swabbing is changing the code to use the le32_to_cpu
119etc. macros as used by ext2fs. We don't need to swab the compressed
120data, only the superblock, inodes and block pointers.
121
122
123The other part of making cramfs more sharable is choosing a block
124size. The options are:
125
126 1. Always 4096 bytes.
127
128 2. Writer chooses blocksize; kernel adapts but rejects blocksize >
129 PAGE_CACHE_SIZE.
130
131 3. Writer chooses blocksize; kernel adapts even to blocksize >
132 PAGE_CACHE_SIZE.
133
134It's easy enough to change the kernel to use a smaller value than
135PAGE_CACHE_SIZE: just make cramfs_readpage read multiple blocks.
136
137The cost of option 1 is that kernels with a larger PAGE_CACHE_SIZE
138value don't get as good compression as they can.
139
140The cost of option 2 relative to option 1 is that the code uses
141variables instead of #define'd constants. The gain is that people
142with kernels having larger PAGE_CACHE_SIZE can make use of that if
143they don't mind their cramfs being inaccessible to kernels with
144smaller PAGE_CACHE_SIZE values.
145
146Option 3 is easy to implement if we don't mind being CPU-inefficient:
147e.g. get readpage to decompress to a buffer of size MAX_BLKSIZE (which
148must be no larger than 32KB) and discard what it doesn't need.
149Getting readpage to read into all the covered pages is harder.
150
151The main advantage of option 3 over 1, 2, is better compression. The
152cost is greater complexity. Probably not worth it, but I hope someone
153will disagree. (If it is implemented, then I'll re-use that code in
154e2compr.)
155
156
157Another cost of 2 and 3 over 1 is making mkcramfs use a different
158block size, but that just means adding and parsing a -b option.
159
160
161Inode Size
162----------
163
164Given that cramfs will probably be used for CDs etc. as well as just
165silicon ROMs, it might make sense to expand the inode a little from
166its current 12 bytes. Inodes other than the root inode are followed
167by filename, so the expansion doesn't even have to be a multiple of 4
168bytes.
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
new file mode 100644
index 000000000000..6c285efa2004
--- /dev/null
+++ b/fs/cramfs/inode.c
@@ -0,0 +1,525 @@
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
14#include <linux/module.h>
15#include <linux/fs.h>
16#include <linux/pagemap.h>
17#include <linux/init.h>
18#include <linux/string.h>
19#include <linux/blkdev.h>
20#include <linux/cramfs_fs.h>
21#include <linux/slab.h>
22#include <linux/cramfs_fs_sb.h>
23#include <linux/buffer_head.h>
24#include <linux/vfs.h>
25#include <asm/semaphore.h>
26
27#include <asm/uaccess.h>
28
29static struct super_operations cramfs_ops;
30static struct inode_operations cramfs_dir_inode_operations;
31static struct file_operations cramfs_directory_operations;
32static struct address_space_operations cramfs_aops;
33
34static DECLARE_MUTEX(read_mutex);
35
36
37/* These two macros may change in future, to provide better st_ino
38 semantics. */
39#define CRAMINO(x) ((x)->offset?(x)->offset<<2:1)
40#define OFFSET(x) ((x)->i_ino)
41
42static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inode * cramfs_inode)
43{
44 struct inode * inode = new_inode(sb);
45 static struct timespec zerotime;
46
47 if (inode) {
48 inode->i_mode = cramfs_inode->mode;
49 inode->i_uid = cramfs_inode->uid;
50 inode->i_size = cramfs_inode->size;
51 inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1;
52 inode->i_blksize = PAGE_CACHE_SIZE;
53 inode->i_gid = cramfs_inode->gid;
54 /* Struct copy intentional */
55 inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime;
56 inode->i_ino = CRAMINO(cramfs_inode);
57 /* inode->i_nlink is left 1 - arguably wrong for directories,
58 but it's the best we can do without reading the directory
59 contents. 1 yields the right result in GNU find, even
60 without -noleaf option. */
61 insert_inode_hash(inode);
62 if (S_ISREG(inode->i_mode)) {
63 inode->i_fop = &generic_ro_fops;
64 inode->i_data.a_ops = &cramfs_aops;
65 } else if (S_ISDIR(inode->i_mode)) {
66 inode->i_op = &cramfs_dir_inode_operations;
67 inode->i_fop = &cramfs_directory_operations;
68 } else if (S_ISLNK(inode->i_mode)) {
69 inode->i_op = &page_symlink_inode_operations;
70 inode->i_data.a_ops = &cramfs_aops;
71 } else {
72 inode->i_size = 0;
73 inode->i_blocks = 0;
74 init_special_inode(inode, inode->i_mode,
75 old_decode_dev(cramfs_inode->size));
76 }
77 }
78 return inode;
79}
80
81/*
82 * We have our own block cache: don't fill up the buffer cache
83 * with the rom-image, because the way the filesystem is set
84 * up the accesses should be fairly regular and cached in the
85 * page cache and dentry tree anyway..
86 *
87 * This also acts as a way to guarantee contiguous areas of up to
88 * BLKS_PER_BUF*PAGE_CACHE_SIZE, so that the caller doesn't need to
89 * worry about end-of-buffer issues even when decompressing a full
90 * page cache.
91 */
92#define READ_BUFFERS (2)
93/* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */
94#define NEXT_BUFFER(_ix) ((_ix) ^ 1)
95
96/*
97 * BLKS_PER_BUF_SHIFT should be at least 2 to allow for "compressed"
98 * data that takes up more space than the original and with unlucky
99 * alignment.
100 */
101#define BLKS_PER_BUF_SHIFT (2)
102#define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT)
103#define BUFFER_SIZE (BLKS_PER_BUF*PAGE_CACHE_SIZE)
104
105static unsigned char read_buffers[READ_BUFFERS][BUFFER_SIZE];
106static unsigned buffer_blocknr[READ_BUFFERS];
107static struct super_block * buffer_dev[READ_BUFFERS];
108static int next_buffer;
109
110/*
111 * Returns a pointer to a buffer containing at least LEN bytes of
112 * filesystem starting at byte offset OFFSET into the filesystem.
113 */
114static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len)
115{
116 struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;
117 struct page *pages[BLKS_PER_BUF];
118 unsigned i, blocknr, buffer, unread;
119 unsigned long devsize;
120 char *data;
121
122 if (!len)
123 return NULL;
124 blocknr = offset >> PAGE_CACHE_SHIFT;
125 offset &= PAGE_CACHE_SIZE - 1;
126
127 /* Check if an existing buffer already has the data.. */
128 for (i = 0; i < READ_BUFFERS; i++) {
129 unsigned int blk_offset;
130
131 if (buffer_dev[i] != sb)
132 continue;
133 if (blocknr < buffer_blocknr[i])
134 continue;
135 blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_CACHE_SHIFT;
136 blk_offset += offset;
137 if (blk_offset + len > BUFFER_SIZE)
138 continue;
139 return read_buffers[i] + blk_offset;
140 }
141
142 devsize = mapping->host->i_size >> PAGE_CACHE_SHIFT;
143
144 /* Ok, read in BLKS_PER_BUF pages completely first. */
145 unread = 0;
146 for (i = 0; i < BLKS_PER_BUF; i++) {
147 struct page *page = NULL;
148
149 if (blocknr + i < devsize) {
150 page = read_cache_page(mapping, blocknr + i,
151 (filler_t *)mapping->a_ops->readpage,
152 NULL);
153 /* synchronous error? */
154 if (IS_ERR(page))
155 page = NULL;
156 }
157 pages[i] = page;
158 }
159
160 for (i = 0; i < BLKS_PER_BUF; i++) {
161 struct page *page = pages[i];
162 if (page) {
163 wait_on_page_locked(page);
164 if (!PageUptodate(page)) {
165 /* asynchronous error */
166 page_cache_release(page);
167 pages[i] = NULL;
168 }
169 }
170 }
171
172 buffer = next_buffer;
173 next_buffer = NEXT_BUFFER(buffer);
174 buffer_blocknr[buffer] = blocknr;
175 buffer_dev[buffer] = sb;
176
177 data = read_buffers[buffer];
178 for (i = 0; i < BLKS_PER_BUF; i++) {
179 struct page *page = pages[i];
180 if (page) {
181 memcpy(data, kmap(page), PAGE_CACHE_SIZE);
182 kunmap(page);
183 page_cache_release(page);
184 } else
185 memset(data, 0, PAGE_CACHE_SIZE);
186 data += PAGE_CACHE_SIZE;
187 }
188 return read_buffers[buffer] + offset;
189}
190
191static void cramfs_put_super(struct super_block *sb)
192{
193 kfree(sb->s_fs_info);
194 sb->s_fs_info = NULL;
195}
196
197static int cramfs_remount(struct super_block *sb, int *flags, char *data)
198{
199 *flags |= MS_RDONLY;
200 return 0;
201}
202
203static int cramfs_fill_super(struct super_block *sb, void *data, int silent)
204{
205 int i;
206 struct cramfs_super super;
207 unsigned long root_offset;
208 struct cramfs_sb_info *sbi;
209 struct inode *root;
210
211 sb->s_flags |= MS_RDONLY;
212
213 sbi = kmalloc(sizeof(struct cramfs_sb_info), GFP_KERNEL);
214 if (!sbi)
215 return -ENOMEM;
216 sb->s_fs_info = sbi;
217 memset(sbi, 0, sizeof(struct cramfs_sb_info));
218
219 /* Invalidate the read buffers on mount: think disk change.. */
220 down(&read_mutex);
221 for (i = 0; i < READ_BUFFERS; i++)
222 buffer_blocknr[i] = -1;
223
224 /* Read the first block and get the superblock from it */
225 memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super));
226 up(&read_mutex);
227
228 /* Do sanity checks on the superblock */
229 if (super.magic != CRAMFS_MAGIC) {
230 /* check at 512 byte offset */
231 down(&read_mutex);
232 memcpy(&super, cramfs_read(sb, 512, sizeof(super)), sizeof(super));
233 up(&read_mutex);
234 if (super.magic != CRAMFS_MAGIC) {
235 if (!silent)
236 printk(KERN_ERR "cramfs: wrong magic\n");
237 goto out;
238 }
239 }
240
241 /* get feature flags first */
242 if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
243 printk(KERN_ERR "cramfs: unsupported filesystem features\n");
244 goto out;
245 }
246
247 /* Check that the root inode is in a sane state */
248 if (!S_ISDIR(super.root.mode)) {
249 printk(KERN_ERR "cramfs: root is not a directory\n");
250 goto out;
251 }
252 root_offset = super.root.offset << 2;
253 if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
254 sbi->size=super.size;
255 sbi->blocks=super.fsid.blocks;
256 sbi->files=super.fsid.files;
257 } else {
258 sbi->size=1<<28;
259 sbi->blocks=0;
260 sbi->files=0;
261 }
262 sbi->magic=super.magic;
263 sbi->flags=super.flags;
264 if (root_offset == 0)
265 printk(KERN_INFO "cramfs: empty filesystem");
266 else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
267 ((root_offset != sizeof(struct cramfs_super)) &&
268 (root_offset != 512 + sizeof(struct cramfs_super))))
269 {
270 printk(KERN_ERR "cramfs: bad root offset %lu\n", root_offset);
271 goto out;
272 }
273
274 /* Set it all up.. */
275 sb->s_op = &cramfs_ops;
276 root = get_cramfs_inode(sb, &super.root);
277 if (!root)
278 goto out;
279 sb->s_root = d_alloc_root(root);
280 if (!sb->s_root) {
281 iput(root);
282 goto out;
283 }
284 return 0;
285out:
286 kfree(sbi);
287 sb->s_fs_info = NULL;
288 return -EINVAL;
289}
290
291static int cramfs_statfs(struct super_block *sb, struct kstatfs *buf)
292{
293 buf->f_type = CRAMFS_MAGIC;
294 buf->f_bsize = PAGE_CACHE_SIZE;
295 buf->f_blocks = CRAMFS_SB(sb)->blocks;
296 buf->f_bfree = 0;
297 buf->f_bavail = 0;
298 buf->f_files = CRAMFS_SB(sb)->files;
299 buf->f_ffree = 0;
300 buf->f_namelen = CRAMFS_MAXPATHLEN;
301 return 0;
302}
303
304/*
305 * Read a cramfs directory entry.
306 */
307static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
308{
309 struct inode *inode = filp->f_dentry->d_inode;
310 struct super_block *sb = inode->i_sb;
311 char *buf;
312 unsigned int offset;
313 int copied;
314
315 /* Offset within the thing. */
316 offset = filp->f_pos;
317 if (offset >= inode->i_size)
318 return 0;
319 /* Directory entries are always 4-byte aligned */
320 if (offset & 3)
321 return -EINVAL;
322
323 buf = kmalloc(256, GFP_KERNEL);
324 if (!buf)
325 return -ENOMEM;
326
327 copied = 0;
328 while (offset < inode->i_size) {
329 struct cramfs_inode *de;
330 unsigned long nextoffset;
331 char *name;
332 ino_t ino;
333 mode_t mode;
334 int namelen, error;
335
336 down(&read_mutex);
337 de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+256);
338 name = (char *)(de+1);
339
340 /*
341 * Namelengths on disk are shifted by two
342 * and the name padded out to 4-byte boundaries
343 * with zeroes.
344 */
345 namelen = de->namelen << 2;
346 memcpy(buf, name, namelen);
347 ino = CRAMINO(de);
348 mode = de->mode;
349 up(&read_mutex);
350 nextoffset = offset + sizeof(*de) + namelen;
351 for (;;) {
352 if (!namelen) {
353 kfree(buf);
354 return -EIO;
355 }
356 if (buf[namelen-1])
357 break;
358 namelen--;
359 }
360 error = filldir(dirent, buf, namelen, offset, ino, mode >> 12);
361 if (error)
362 break;
363
364 offset = nextoffset;
365 filp->f_pos = offset;
366 copied++;
367 }
368 kfree(buf);
369 return 0;
370}
371
372/*
373 * Lookup and fill in the inode data..
374 */
375static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
376{
377 unsigned int offset = 0;
378 int sorted;
379
380 down(&read_mutex);
381 sorted = CRAMFS_SB(dir->i_sb)->flags & CRAMFS_FLAG_SORTED_DIRS;
382 while (offset < dir->i_size) {
383 struct cramfs_inode *de;
384 char *name;
385 int namelen, retval;
386
387 de = cramfs_read(dir->i_sb, OFFSET(dir) + offset, sizeof(*de)+256);
388 name = (char *)(de+1);
389
390 /* Try to take advantage of sorted directories */
391 if (sorted && (dentry->d_name.name[0] < name[0]))
392 break;
393
394 namelen = de->namelen << 2;
395 offset += sizeof(*de) + namelen;
396
397 /* Quick check that the name is roughly the right length */
398 if (((dentry->d_name.len + 3) & ~3) != namelen)
399 continue;
400
401 for (;;) {
402 if (!namelen) {
403 up(&read_mutex);
404 return ERR_PTR(-EIO);
405 }
406 if (name[namelen-1])
407 break;
408 namelen--;
409 }
410 if (namelen != dentry->d_name.len)
411 continue;
412 retval = memcmp(dentry->d_name.name, name, namelen);
413 if (retval > 0)
414 continue;
415 if (!retval) {
416 struct cramfs_inode entry = *de;
417 up(&read_mutex);
418 d_add(dentry, get_cramfs_inode(dir->i_sb, &entry));
419 return NULL;
420 }
421 /* else (retval < 0) */
422 if (sorted)
423 break;
424 }
425 up(&read_mutex);
426 d_add(dentry, NULL);
427 return NULL;
428}
429
430static int cramfs_readpage(struct file *file, struct page * page)
431{
432 struct inode *inode = page->mapping->host;
433 u32 maxblock, bytes_filled;
434 void *pgdata;
435
436 maxblock = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
437 bytes_filled = 0;
438 if (page->index < maxblock) {
439 struct super_block *sb = inode->i_sb;
440 u32 blkptr_offset = OFFSET(inode) + page->index*4;
441 u32 start_offset, compr_len;
442
443 start_offset = OFFSET(inode) + maxblock*4;
444 down(&read_mutex);
445 if (page->index)
446 start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4, 4);
447 compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) - start_offset);
448 up(&read_mutex);
449 pgdata = kmap(page);
450 if (compr_len == 0)
451 ; /* hole */
452 else {
453 down(&read_mutex);
454 bytes_filled = cramfs_uncompress_block(pgdata,
455 PAGE_CACHE_SIZE,
456 cramfs_read(sb, start_offset, compr_len),
457 compr_len);
458 up(&read_mutex);
459 }
460 } else
461 pgdata = kmap(page);
462 memset(pgdata + bytes_filled, 0, PAGE_CACHE_SIZE - bytes_filled);
463 kunmap(page);
464 flush_dcache_page(page);
465 SetPageUptodate(page);
466 unlock_page(page);
467 return 0;
468}
469
470static struct address_space_operations cramfs_aops = {
471 .readpage = cramfs_readpage
472};
473
474/*
475 * Our operations:
476 */
477
478/*
479 * A directory can only readdir
480 */
481static struct file_operations cramfs_directory_operations = {
482 .llseek = generic_file_llseek,
483 .read = generic_read_dir,
484 .readdir = cramfs_readdir,
485};
486
487static struct inode_operations cramfs_dir_inode_operations = {
488 .lookup = cramfs_lookup,
489};
490
491static struct super_operations cramfs_ops = {
492 .put_super = cramfs_put_super,
493 .remount_fs = cramfs_remount,
494 .statfs = cramfs_statfs,
495};
496
497static struct super_block *cramfs_get_sb(struct file_system_type *fs_type,
498 int flags, const char *dev_name, void *data)
499{
500 return get_sb_bdev(fs_type, flags, dev_name, data, cramfs_fill_super);
501}
502
503static struct file_system_type cramfs_fs_type = {
504 .owner = THIS_MODULE,
505 .name = "cramfs",
506 .get_sb = cramfs_get_sb,
507 .kill_sb = kill_block_super,
508 .fs_flags = FS_REQUIRES_DEV,
509};
510
511static int __init init_cramfs_fs(void)
512{
513 cramfs_uncompress_init();
514 return register_filesystem(&cramfs_fs_type);
515}
516
517static void __exit exit_cramfs_fs(void)
518{
519 cramfs_uncompress_exit();
520 unregister_filesystem(&cramfs_fs_type);
521}
522
523module_init(init_cramfs_fs)
524module_exit(exit_cramfs_fs)
525MODULE_LICENSE("GPL");
diff --git a/fs/cramfs/uncompress.c b/fs/cramfs/uncompress.c
new file mode 100644
index 000000000000..5034365b06a8
--- /dev/null
+++ b/fs/cramfs/uncompress.c
@@ -0,0 +1,77 @@
1/*
2 * uncompress.c
3 *
4 * (C) Copyright 1999 Linus Torvalds
5 *
6 * cramfs interfaces to the uncompression library. There's really just
7 * three entrypoints:
8 *
9 * - cramfs_uncompress_init() - called to initialize the thing.
10 * - cramfs_uncompress_exit() - tell me when you're done
11 * - cramfs_uncompress_block() - uncompress a block.
12 *
13 * NOTE NOTE NOTE! The uncompression is entirely single-threaded. We
14 * only have one stream, and we'll initialize it only once even if it
15 * then is used by multiple filesystems.
16 */
17
18#include <linux/kernel.h>
19#include <linux/errno.h>
20#include <linux/vmalloc.h>
21#include <linux/zlib.h>
22
23static z_stream stream;
24static int initialized;
25
26/* Returns length of decompressed data. */
27int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen)
28{
29 int err;
30
31 stream.next_in = src;
32 stream.avail_in = srclen;
33
34 stream.next_out = dst;
35 stream.avail_out = dstlen;
36
37 err = zlib_inflateReset(&stream);
38 if (err != Z_OK) {
39 printk("zlib_inflateReset error %d\n", err);
40 zlib_inflateEnd(&stream);
41 zlib_inflateInit(&stream);
42 }
43
44 err = zlib_inflate(&stream, Z_FINISH);
45 if (err != Z_STREAM_END)
46 goto err;
47 return stream.total_out;
48
49err:
50 printk("Error %d while decompressing!\n", err);
51 printk("%p(%d)->%p(%d)\n", src, srclen, dst, dstlen);
52 return 0;
53}
54
55int cramfs_uncompress_init(void)
56{
57 if (!initialized++) {
58 stream.workspace = vmalloc(zlib_inflate_workspacesize());
59 if ( !stream.workspace ) {
60 initialized = 0;
61 return -ENOMEM;
62 }
63 stream.next_in = NULL;
64 stream.avail_in = 0;
65 zlib_inflateInit(&stream);
66 }
67 return 0;
68}
69
70int cramfs_uncompress_exit(void)
71{
72 if (!--initialized) {
73 zlib_inflateEnd(&stream);
74 vfree(stream.workspace);
75 }
76 return 0;
77}