diff options
Diffstat (limited to 'fs/romfs')
-rw-r--r-- | fs/romfs/Makefile | 7 | ||||
-rw-r--r-- | fs/romfs/inode.c | 648 |
2 files changed, 655 insertions, 0 deletions
diff --git a/fs/romfs/Makefile b/fs/romfs/Makefile new file mode 100644 index 000000000000..c95b21cf49a3 --- /dev/null +++ b/fs/romfs/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # | ||
2 | # Makefile for the linux romfs filesystem routines. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_ROMFS_FS) += romfs.o | ||
6 | |||
7 | romfs-objs := inode.o | ||
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c new file mode 100644 index 000000000000..c74f382dabba --- /dev/null +++ b/fs/romfs/inode.c | |||
@@ -0,0 +1,648 @@ | |||
1 | /* | ||
2 | * ROMFS file system, Linux implementation | ||
3 | * | ||
4 | * Copyright (C) 1997-1999 Janos Farkas <chexum@shadow.banki.hu> | ||
5 | * | ||
6 | * Using parts of the minix filesystem | ||
7 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
8 | * | ||
9 | * and parts of the affs filesystem additionally | ||
10 | * Copyright (C) 1993 Ray Burr | ||
11 | * Copyright (C) 1996 Hans-Joachim Widmaier | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version | ||
16 | * 2 of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * Changes | ||
19 | * Changed for 2.1.19 modules | ||
20 | * Jan 1997 Initial release | ||
21 | * Jun 1997 2.1.43+ changes | ||
22 | * Proper page locking in readpage | ||
23 | * Changed to work with 2.1.45+ fs | ||
24 | * Jul 1997 Fixed follow_link | ||
25 | * 2.1.47 | ||
26 | * lookup shouldn't return -ENOENT | ||
27 | * from Horst von Brand: | ||
28 | * fail on wrong checksum | ||
29 | * double unlock_super was possible | ||
30 | * correct namelen for statfs | ||
31 | * spotted by Bill Hawes: | ||
32 | * readlink shouldn't iput() | ||
33 | * Jun 1998 2.1.106 from Avery Pennarun: glibc scandir() | ||
34 | * exposed a problem in readdir | ||
35 | * 2.1.107 code-freeze spellchecker run | ||
36 | * Aug 1998 2.1.118+ VFS changes | ||
37 | * Sep 1998 2.1.122 another VFS change (follow_link) | ||
38 | * Apr 1999 2.2.7 no more EBADF checking in | ||
39 | * lookup/readdir, use ERR_PTR | ||
40 | * Jun 1999 2.3.6 d_alloc_root use changed | ||
41 | * 2.3.9 clean up usage of ENOENT/negative | ||
42 | * dentries in lookup | ||
43 | * clean up page flags setting | ||
44 | * (error, uptodate, locking) in | ||
45 | * in readpage | ||
46 | * use init_special_inode for | ||
47 | * fifos/sockets (and streamline) in | ||
48 | * read_inode, fix _ops table order | ||
49 | * Aug 1999 2.3.16 __initfunc() => __init change | ||
50 | * Oct 1999 2.3.24 page->owner hack obsoleted | ||
51 | * Nov 1999 2.3.27 2.3.25+ page->offset => index change | ||
52 | */ | ||
53 | |||
54 | /* todo: | ||
55 | * - see Documentation/filesystems/romfs.txt | ||
56 | * - use allocated, not stack memory for file names? | ||
57 | * - considering write access... | ||
58 | * - network (tftp) files? | ||
59 | * - merge back some _op tables | ||
60 | */ | ||
61 | |||
62 | /* | ||
63 | * Sorry about some optimizations and for some goto's. I just wanted | ||
64 | * to squeeze some more bytes out of this code.. :) | ||
65 | */ | ||
66 | |||
67 | #include <linux/module.h> | ||
68 | #include <linux/types.h> | ||
69 | #include <linux/errno.h> | ||
70 | #include <linux/slab.h> | ||
71 | #include <linux/romfs_fs.h> | ||
72 | #include <linux/fs.h> | ||
73 | #include <linux/init.h> | ||
74 | #include <linux/pagemap.h> | ||
75 | #include <linux/smp_lock.h> | ||
76 | #include <linux/buffer_head.h> | ||
77 | #include <linux/vfs.h> | ||
78 | |||
79 | #include <asm/uaccess.h> | ||
80 | |||
81 | struct romfs_inode_info { | ||
82 | unsigned long i_metasize; /* size of non-data area */ | ||
83 | unsigned long i_dataoffset; /* from the start of fs */ | ||
84 | struct inode vfs_inode; | ||
85 | }; | ||
86 | |||
87 | /* instead of private superblock data */ | ||
88 | static inline unsigned long romfs_maxsize(struct super_block *sb) | ||
89 | { | ||
90 | return (unsigned long)sb->s_fs_info; | ||
91 | } | ||
92 | |||
93 | static inline struct romfs_inode_info *ROMFS_I(struct inode *inode) | ||
94 | { | ||
95 | return list_entry(inode, struct romfs_inode_info, vfs_inode); | ||
96 | } | ||
97 | |||
98 | static __u32 | ||
99 | romfs_checksum(void *data, int size) | ||
100 | { | ||
101 | __u32 sum; | ||
102 | __be32 *ptr; | ||
103 | |||
104 | sum = 0; ptr = data; | ||
105 | size>>=2; | ||
106 | while (size>0) { | ||
107 | sum += be32_to_cpu(*ptr++); | ||
108 | size--; | ||
109 | } | ||
110 | return sum; | ||
111 | } | ||
112 | |||
113 | static struct super_operations romfs_ops; | ||
114 | |||
115 | static int romfs_fill_super(struct super_block *s, void *data, int silent) | ||
116 | { | ||
117 | struct buffer_head *bh; | ||
118 | struct romfs_super_block *rsb; | ||
119 | struct inode *root; | ||
120 | int sz; | ||
121 | |||
122 | /* I would parse the options here, but there are none.. :) */ | ||
123 | |||
124 | sb_set_blocksize(s, ROMBSIZE); | ||
125 | s->s_maxbytes = 0xFFFFFFFF; | ||
126 | |||
127 | bh = sb_bread(s, 0); | ||
128 | if (!bh) { | ||
129 | /* XXX merge with other printk? */ | ||
130 | printk ("romfs: unable to read superblock\n"); | ||
131 | goto outnobh; | ||
132 | } | ||
133 | |||
134 | rsb = (struct romfs_super_block *)bh->b_data; | ||
135 | sz = be32_to_cpu(rsb->size); | ||
136 | if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 | ||
137 | || sz < ROMFH_SIZE) { | ||
138 | if (!silent) | ||
139 | printk ("VFS: Can't find a romfs filesystem on dev " | ||
140 | "%s.\n", s->s_id); | ||
141 | goto out; | ||
142 | } | ||
143 | if (romfs_checksum(rsb, min_t(int, sz, 512))) { | ||
144 | printk ("romfs: bad initial checksum on dev " | ||
145 | "%s.\n", s->s_id); | ||
146 | goto out; | ||
147 | } | ||
148 | |||
149 | s->s_magic = ROMFS_MAGIC; | ||
150 | s->s_fs_info = (void *)(long)sz; | ||
151 | |||
152 | s->s_flags |= MS_RDONLY; | ||
153 | |||
154 | /* Find the start of the fs */ | ||
155 | sz = (ROMFH_SIZE + | ||
156 | strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD) | ||
157 | & ROMFH_MASK; | ||
158 | |||
159 | s->s_op = &romfs_ops; | ||
160 | root = iget(s, sz); | ||
161 | if (!root) | ||
162 | goto out; | ||
163 | |||
164 | s->s_root = d_alloc_root(root); | ||
165 | if (!s->s_root) | ||
166 | goto outiput; | ||
167 | |||
168 | brelse(bh); | ||
169 | return 0; | ||
170 | |||
171 | outiput: | ||
172 | iput(root); | ||
173 | out: | ||
174 | brelse(bh); | ||
175 | outnobh: | ||
176 | return -EINVAL; | ||
177 | } | ||
178 | |||
179 | /* That's simple too. */ | ||
180 | |||
181 | static int | ||
182 | romfs_statfs(struct super_block *sb, struct kstatfs *buf) | ||
183 | { | ||
184 | buf->f_type = ROMFS_MAGIC; | ||
185 | buf->f_bsize = ROMBSIZE; | ||
186 | buf->f_bfree = buf->f_bavail = buf->f_ffree; | ||
187 | buf->f_blocks = (romfs_maxsize(sb)+ROMBSIZE-1)>>ROMBSBITS; | ||
188 | buf->f_namelen = ROMFS_MAXFN; | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | /* some helper routines */ | ||
193 | |||
194 | static int | ||
195 | romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count) | ||
196 | { | ||
197 | struct buffer_head *bh; | ||
198 | unsigned long avail, maxsize, res; | ||
199 | |||
200 | maxsize = romfs_maxsize(i->i_sb); | ||
201 | if (offset >= maxsize) | ||
202 | return -1; | ||
203 | |||
204 | /* strnlen is almost always valid */ | ||
205 | if (count > maxsize || offset+count > maxsize) | ||
206 | count = maxsize-offset; | ||
207 | |||
208 | bh = sb_bread(i->i_sb, offset>>ROMBSBITS); | ||
209 | if (!bh) | ||
210 | return -1; /* error */ | ||
211 | |||
212 | avail = ROMBSIZE - (offset & ROMBMASK); | ||
213 | maxsize = min_t(unsigned long, count, avail); | ||
214 | res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize); | ||
215 | brelse(bh); | ||
216 | |||
217 | if (res < maxsize) | ||
218 | return res; /* found all of it */ | ||
219 | |||
220 | while (res < count) { | ||
221 | offset += maxsize; | ||
222 | |||
223 | bh = sb_bread(i->i_sb, offset>>ROMBSBITS); | ||
224 | if (!bh) | ||
225 | return -1; | ||
226 | maxsize = min_t(unsigned long, count - res, ROMBSIZE); | ||
227 | avail = strnlen(bh->b_data, maxsize); | ||
228 | res += avail; | ||
229 | brelse(bh); | ||
230 | if (avail < maxsize) | ||
231 | return res; | ||
232 | } | ||
233 | return res; | ||
234 | } | ||
235 | |||
236 | static int | ||
237 | romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count) | ||
238 | { | ||
239 | struct buffer_head *bh; | ||
240 | unsigned long avail, maxsize, res; | ||
241 | |||
242 | maxsize = romfs_maxsize(i->i_sb); | ||
243 | if (offset >= maxsize || count > maxsize || offset+count>maxsize) | ||
244 | return -1; | ||
245 | |||
246 | bh = sb_bread(i->i_sb, offset>>ROMBSBITS); | ||
247 | if (!bh) | ||
248 | return -1; /* error */ | ||
249 | |||
250 | avail = ROMBSIZE - (offset & ROMBMASK); | ||
251 | maxsize = min_t(unsigned long, count, avail); | ||
252 | memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize); | ||
253 | brelse(bh); | ||
254 | |||
255 | res = maxsize; /* all of it */ | ||
256 | |||
257 | while (res < count) { | ||
258 | offset += maxsize; | ||
259 | dest += maxsize; | ||
260 | |||
261 | bh = sb_bread(i->i_sb, offset>>ROMBSBITS); | ||
262 | if (!bh) | ||
263 | return -1; | ||
264 | maxsize = min_t(unsigned long, count - res, ROMBSIZE); | ||
265 | memcpy(dest, bh->b_data, maxsize); | ||
266 | brelse(bh); | ||
267 | res += maxsize; | ||
268 | } | ||
269 | return res; | ||
270 | } | ||
271 | |||
272 | static unsigned char romfs_dtype_table[] = { | ||
273 | DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO | ||
274 | }; | ||
275 | |||
276 | static int | ||
277 | romfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
278 | { | ||
279 | struct inode *i = filp->f_dentry->d_inode; | ||
280 | struct romfs_inode ri; | ||
281 | unsigned long offset, maxoff; | ||
282 | int j, ino, nextfh; | ||
283 | int stored = 0; | ||
284 | char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ | ||
285 | |||
286 | lock_kernel(); | ||
287 | |||
288 | maxoff = romfs_maxsize(i->i_sb); | ||
289 | |||
290 | offset = filp->f_pos; | ||
291 | if (!offset) { | ||
292 | offset = i->i_ino & ROMFH_MASK; | ||
293 | if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0) | ||
294 | goto out; | ||
295 | offset = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
296 | } | ||
297 | |||
298 | /* Not really failsafe, but we are read-only... */ | ||
299 | for(;;) { | ||
300 | if (!offset || offset >= maxoff) { | ||
301 | offset = maxoff; | ||
302 | filp->f_pos = offset; | ||
303 | goto out; | ||
304 | } | ||
305 | filp->f_pos = offset; | ||
306 | |||
307 | /* Fetch inode info */ | ||
308 | if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0) | ||
309 | goto out; | ||
310 | |||
311 | j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1); | ||
312 | if (j < 0) | ||
313 | goto out; | ||
314 | |||
315 | fsname[j]=0; | ||
316 | romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j); | ||
317 | |||
318 | ino = offset; | ||
319 | nextfh = be32_to_cpu(ri.next); | ||
320 | if ((nextfh & ROMFH_TYPE) == ROMFH_HRD) | ||
321 | ino = be32_to_cpu(ri.spec); | ||
322 | if (filldir(dirent, fsname, j, offset, ino, | ||
323 | romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0) { | ||
324 | goto out; | ||
325 | } | ||
326 | stored++; | ||
327 | offset = nextfh & ROMFH_MASK; | ||
328 | } | ||
329 | out: | ||
330 | unlock_kernel(); | ||
331 | return stored; | ||
332 | } | ||
333 | |||
334 | static struct dentry * | ||
335 | romfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | ||
336 | { | ||
337 | unsigned long offset, maxoff; | ||
338 | int fslen, res; | ||
339 | struct inode *inode; | ||
340 | char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ | ||
341 | struct romfs_inode ri; | ||
342 | const char *name; /* got from dentry */ | ||
343 | int len; | ||
344 | |||
345 | res = -EACCES; /* placeholder for "no data here" */ | ||
346 | offset = dir->i_ino & ROMFH_MASK; | ||
347 | lock_kernel(); | ||
348 | if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) | ||
349 | goto out; | ||
350 | |||
351 | maxoff = romfs_maxsize(dir->i_sb); | ||
352 | offset = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
353 | |||
354 | /* OK, now find the file whose name is in "dentry" in the | ||
355 | * directory specified by "dir". */ | ||
356 | |||
357 | name = dentry->d_name.name; | ||
358 | len = dentry->d_name.len; | ||
359 | |||
360 | for(;;) { | ||
361 | if (!offset || offset >= maxoff) | ||
362 | goto out0; | ||
363 | if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) | ||
364 | goto out; | ||
365 | |||
366 | /* try to match the first 16 bytes of name */ | ||
367 | fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE); | ||
368 | if (len < ROMFH_SIZE) { | ||
369 | if (len == fslen) { | ||
370 | /* both are shorter, and same size */ | ||
371 | romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1); | ||
372 | if (strncmp (name, fsname, len) == 0) | ||
373 | break; | ||
374 | } | ||
375 | } else if (fslen >= ROMFH_SIZE) { | ||
376 | /* both are longer; XXX optimize max size */ | ||
377 | fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1); | ||
378 | if (len == fslen) { | ||
379 | romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1); | ||
380 | if (strncmp(name, fsname, len) == 0) | ||
381 | break; | ||
382 | } | ||
383 | } | ||
384 | /* next entry */ | ||
385 | offset = be32_to_cpu(ri.next) & ROMFH_MASK; | ||
386 | } | ||
387 | |||
388 | /* Hard link handling */ | ||
389 | if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD) | ||
390 | offset = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
391 | |||
392 | if ((inode = iget(dir->i_sb, offset))) | ||
393 | goto outi; | ||
394 | |||
395 | /* | ||
396 | * it's a bit funky, _lookup needs to return an error code | ||
397 | * (negative) or a NULL, both as a dentry. ENOENT should not | ||
398 | * be returned, instead we need to create a negative dentry by | ||
399 | * d_add(dentry, NULL); and return 0 as no error. | ||
400 | * (Although as I see, it only matters on writable file | ||
401 | * systems). | ||
402 | */ | ||
403 | |||
404 | out0: inode = NULL; | ||
405 | outi: res = 0; | ||
406 | d_add (dentry, inode); | ||
407 | |||
408 | out: unlock_kernel(); | ||
409 | return ERR_PTR(res); | ||
410 | } | ||
411 | |||
412 | /* | ||
413 | * Ok, we do readpage, to be able to execute programs. Unfortunately, | ||
414 | * we can't use bmap, since we may have looser alignments. | ||
415 | */ | ||
416 | |||
417 | static int | ||
418 | romfs_readpage(struct file *file, struct page * page) | ||
419 | { | ||
420 | struct inode *inode = page->mapping->host; | ||
421 | unsigned long offset, avail, readlen; | ||
422 | void *buf; | ||
423 | int result = -EIO; | ||
424 | |||
425 | page_cache_get(page); | ||
426 | lock_kernel(); | ||
427 | buf = kmap(page); | ||
428 | if (!buf) | ||
429 | goto err_out; | ||
430 | |||
431 | /* 32 bit warning -- but not for us :) */ | ||
432 | offset = page->index << PAGE_CACHE_SHIFT; | ||
433 | if (offset < inode->i_size) { | ||
434 | avail = inode->i_size-offset; | ||
435 | readlen = min_t(unsigned long, avail, PAGE_SIZE); | ||
436 | if (romfs_copyfrom(inode, buf, ROMFS_I(inode)->i_dataoffset+offset, readlen) == readlen) { | ||
437 | if (readlen < PAGE_SIZE) { | ||
438 | memset(buf + readlen,0,PAGE_SIZE-readlen); | ||
439 | } | ||
440 | SetPageUptodate(page); | ||
441 | result = 0; | ||
442 | } | ||
443 | } | ||
444 | if (result) { | ||
445 | memset(buf, 0, PAGE_SIZE); | ||
446 | SetPageError(page); | ||
447 | } | ||
448 | flush_dcache_page(page); | ||
449 | |||
450 | unlock_page(page); | ||
451 | |||
452 | kunmap(page); | ||
453 | err_out: | ||
454 | page_cache_release(page); | ||
455 | unlock_kernel(); | ||
456 | |||
457 | return result; | ||
458 | } | ||
459 | |||
460 | /* Mapping from our types to the kernel */ | ||
461 | |||
462 | static struct address_space_operations romfs_aops = { | ||
463 | .readpage = romfs_readpage | ||
464 | }; | ||
465 | |||
466 | static struct file_operations romfs_dir_operations = { | ||
467 | .read = generic_read_dir, | ||
468 | .readdir = romfs_readdir, | ||
469 | }; | ||
470 | |||
471 | static struct inode_operations romfs_dir_inode_operations = { | ||
472 | .lookup = romfs_lookup, | ||
473 | }; | ||
474 | |||
475 | static mode_t romfs_modemap[] = | ||
476 | { | ||
477 | 0, S_IFDIR+0644, S_IFREG+0644, S_IFLNK+0777, | ||
478 | S_IFBLK+0600, S_IFCHR+0600, S_IFSOCK+0644, S_IFIFO+0644 | ||
479 | }; | ||
480 | |||
481 | static void | ||
482 | romfs_read_inode(struct inode *i) | ||
483 | { | ||
484 | int nextfh, ino; | ||
485 | struct romfs_inode ri; | ||
486 | |||
487 | ino = i->i_ino & ROMFH_MASK; | ||
488 | i->i_mode = 0; | ||
489 | |||
490 | /* Loop for finding the real hard link */ | ||
491 | for(;;) { | ||
492 | if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) { | ||
493 | printk("romfs: read error for inode 0x%x\n", ino); | ||
494 | return; | ||
495 | } | ||
496 | /* XXX: do romfs_checksum here too (with name) */ | ||
497 | |||
498 | nextfh = be32_to_cpu(ri.next); | ||
499 | if ((nextfh & ROMFH_TYPE) != ROMFH_HRD) | ||
500 | break; | ||
501 | |||
502 | ino = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
503 | } | ||
504 | |||
505 | i->i_nlink = 1; /* Hard to decide.. */ | ||
506 | i->i_size = be32_to_cpu(ri.size); | ||
507 | i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0; | ||
508 | i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0; | ||
509 | i->i_uid = i->i_gid = 0; | ||
510 | |||
511 | /* Precalculate the data offset */ | ||
512 | ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN); | ||
513 | if (ino >= 0) | ||
514 | ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK); | ||
515 | else | ||
516 | ino = 0; | ||
517 | |||
518 | ROMFS_I(i)->i_metasize = ino; | ||
519 | ROMFS_I(i)->i_dataoffset = ino+(i->i_ino&ROMFH_MASK); | ||
520 | |||
521 | /* Compute permissions */ | ||
522 | ino = romfs_modemap[nextfh & ROMFH_TYPE]; | ||
523 | /* only "normal" files have ops */ | ||
524 | switch (nextfh & ROMFH_TYPE) { | ||
525 | case 1: | ||
526 | i->i_size = ROMFS_I(i)->i_metasize; | ||
527 | i->i_op = &romfs_dir_inode_operations; | ||
528 | i->i_fop = &romfs_dir_operations; | ||
529 | if (nextfh & ROMFH_EXEC) | ||
530 | ino |= S_IXUGO; | ||
531 | i->i_mode = ino; | ||
532 | break; | ||
533 | case 2: | ||
534 | i->i_fop = &generic_ro_fops; | ||
535 | i->i_data.a_ops = &romfs_aops; | ||
536 | if (nextfh & ROMFH_EXEC) | ||
537 | ino |= S_IXUGO; | ||
538 | i->i_mode = ino; | ||
539 | break; | ||
540 | case 3: | ||
541 | i->i_op = &page_symlink_inode_operations; | ||
542 | i->i_data.a_ops = &romfs_aops; | ||
543 | i->i_mode = ino | S_IRWXUGO; | ||
544 | break; | ||
545 | default: | ||
546 | /* depending on MBZ for sock/fifos */ | ||
547 | nextfh = be32_to_cpu(ri.spec); | ||
548 | init_special_inode(i, ino, | ||
549 | MKDEV(nextfh>>16,nextfh&0xffff)); | ||
550 | } | ||
551 | } | ||
552 | |||
553 | static kmem_cache_t * romfs_inode_cachep; | ||
554 | |||
555 | static struct inode *romfs_alloc_inode(struct super_block *sb) | ||
556 | { | ||
557 | struct romfs_inode_info *ei; | ||
558 | ei = (struct romfs_inode_info *)kmem_cache_alloc(romfs_inode_cachep, SLAB_KERNEL); | ||
559 | if (!ei) | ||
560 | return NULL; | ||
561 | return &ei->vfs_inode; | ||
562 | } | ||
563 | |||
564 | static void romfs_destroy_inode(struct inode *inode) | ||
565 | { | ||
566 | kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode)); | ||
567 | } | ||
568 | |||
569 | static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) | ||
570 | { | ||
571 | struct romfs_inode_info *ei = (struct romfs_inode_info *) foo; | ||
572 | |||
573 | if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == | ||
574 | SLAB_CTOR_CONSTRUCTOR) | ||
575 | inode_init_once(&ei->vfs_inode); | ||
576 | } | ||
577 | |||
578 | static int init_inodecache(void) | ||
579 | { | ||
580 | romfs_inode_cachep = kmem_cache_create("romfs_inode_cache", | ||
581 | sizeof(struct romfs_inode_info), | ||
582 | 0, SLAB_RECLAIM_ACCOUNT, | ||
583 | init_once, NULL); | ||
584 | if (romfs_inode_cachep == NULL) | ||
585 | return -ENOMEM; | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static void destroy_inodecache(void) | ||
590 | { | ||
591 | if (kmem_cache_destroy(romfs_inode_cachep)) | ||
592 | printk(KERN_INFO "romfs_inode_cache: not all structures were freed\n"); | ||
593 | } | ||
594 | |||
595 | static int romfs_remount(struct super_block *sb, int *flags, char *data) | ||
596 | { | ||
597 | *flags |= MS_RDONLY; | ||
598 | return 0; | ||
599 | } | ||
600 | |||
601 | static struct super_operations romfs_ops = { | ||
602 | .alloc_inode = romfs_alloc_inode, | ||
603 | .destroy_inode = romfs_destroy_inode, | ||
604 | .read_inode = romfs_read_inode, | ||
605 | .statfs = romfs_statfs, | ||
606 | .remount_fs = romfs_remount, | ||
607 | }; | ||
608 | |||
609 | static struct super_block *romfs_get_sb(struct file_system_type *fs_type, | ||
610 | int flags, const char *dev_name, void *data) | ||
611 | { | ||
612 | return get_sb_bdev(fs_type, flags, dev_name, data, romfs_fill_super); | ||
613 | } | ||
614 | |||
615 | static struct file_system_type romfs_fs_type = { | ||
616 | .owner = THIS_MODULE, | ||
617 | .name = "romfs", | ||
618 | .get_sb = romfs_get_sb, | ||
619 | .kill_sb = kill_block_super, | ||
620 | .fs_flags = FS_REQUIRES_DEV, | ||
621 | }; | ||
622 | |||
623 | static int __init init_romfs_fs(void) | ||
624 | { | ||
625 | int err = init_inodecache(); | ||
626 | if (err) | ||
627 | goto out1; | ||
628 | err = register_filesystem(&romfs_fs_type); | ||
629 | if (err) | ||
630 | goto out; | ||
631 | return 0; | ||
632 | out: | ||
633 | destroy_inodecache(); | ||
634 | out1: | ||
635 | return err; | ||
636 | } | ||
637 | |||
638 | static void __exit exit_romfs_fs(void) | ||
639 | { | ||
640 | unregister_filesystem(&romfs_fs_type); | ||
641 | destroy_inodecache(); | ||
642 | } | ||
643 | |||
644 | /* Yes, works even as a module... :) */ | ||
645 | |||
646 | module_init(init_romfs_fs) | ||
647 | module_exit(exit_romfs_fs) | ||
648 | MODULE_LICENSE("GPL"); | ||