diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/jffs2/acl.c | 4 | ||||
| -rw-r--r-- | fs/jffs2/malloc.c | 6 | ||||
| -rw-r--r-- | fs/romfs/Kconfig | 48 | ||||
| -rw-r--r-- | fs/romfs/Makefile | 9 | ||||
| -rw-r--r-- | fs/romfs/inode.c | 665 | ||||
| -rw-r--r-- | fs/romfs/internal.h | 47 | ||||
| -rw-r--r-- | fs/romfs/mmap-nommu.c | 75 | ||||
| -rw-r--r-- | fs/romfs/storage.c | 261 | ||||
| -rw-r--r-- | fs/romfs/super.c | 648 |
9 files changed, 1089 insertions, 674 deletions
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 77ccf8cb0823..043740dde20c 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c | |||
| @@ -38,12 +38,12 @@ static int jffs2_acl_count(size_t size) | |||
| 38 | size_t s; | 38 | size_t s; |
| 39 | 39 | ||
| 40 | size -= sizeof(struct jffs2_acl_header); | 40 | size -= sizeof(struct jffs2_acl_header); |
| 41 | s = size - 4 * sizeof(struct jffs2_acl_entry_short); | 41 | if (size < 4 * sizeof(struct jffs2_acl_entry_short)) { |
| 42 | if (s < 0) { | ||
| 43 | if (size % sizeof(struct jffs2_acl_entry_short)) | 42 | if (size % sizeof(struct jffs2_acl_entry_short)) |
| 44 | return -1; | 43 | return -1; |
| 45 | return size / sizeof(struct jffs2_acl_entry_short); | 44 | return size / sizeof(struct jffs2_acl_entry_short); |
| 46 | } else { | 45 | } else { |
| 46 | s = size - 4 * sizeof(struct jffs2_acl_entry_short); | ||
| 47 | if (s % sizeof(struct jffs2_acl_entry)) | 47 | if (s % sizeof(struct jffs2_acl_entry)) |
| 48 | return -1; | 48 | return -1; |
| 49 | return s / sizeof(struct jffs2_acl_entry) + 4; | 49 | return s / sizeof(struct jffs2_acl_entry) + 4; |
diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index f9211252b5f1..9eff2bdae8a7 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c | |||
| @@ -284,10 +284,9 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *x) | |||
| 284 | struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void) | 284 | struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void) |
| 285 | { | 285 | { |
| 286 | struct jffs2_xattr_datum *xd; | 286 | struct jffs2_xattr_datum *xd; |
| 287 | xd = kmem_cache_alloc(xattr_datum_cache, GFP_KERNEL); | 287 | xd = kmem_cache_zalloc(xattr_datum_cache, GFP_KERNEL); |
| 288 | dbg_memalloc("%p\n", xd); | 288 | dbg_memalloc("%p\n", xd); |
| 289 | 289 | ||
| 290 | memset(xd, 0, sizeof(struct jffs2_xattr_datum)); | ||
| 291 | xd->class = RAWNODE_CLASS_XATTR_DATUM; | 290 | xd->class = RAWNODE_CLASS_XATTR_DATUM; |
| 292 | xd->node = (void *)xd; | 291 | xd->node = (void *)xd; |
| 293 | INIT_LIST_HEAD(&xd->xindex); | 292 | INIT_LIST_HEAD(&xd->xindex); |
| @@ -303,10 +302,9 @@ void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd) | |||
| 303 | struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void) | 302 | struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void) |
| 304 | { | 303 | { |
| 305 | struct jffs2_xattr_ref *ref; | 304 | struct jffs2_xattr_ref *ref; |
| 306 | ref = kmem_cache_alloc(xattr_ref_cache, GFP_KERNEL); | 305 | ref = kmem_cache_zalloc(xattr_ref_cache, GFP_KERNEL); |
| 307 | dbg_memalloc("%p\n", ref); | 306 | dbg_memalloc("%p\n", ref); |
| 308 | 307 | ||
| 309 | memset(ref, 0, sizeof(struct jffs2_xattr_ref)); | ||
| 310 | ref->class = RAWNODE_CLASS_XATTR_REF; | 308 | ref->class = RAWNODE_CLASS_XATTR_REF; |
| 311 | ref->node = (void *)ref; | 309 | ref->node = (void *)ref; |
| 312 | return ref; | 310 | return ref; |
diff --git a/fs/romfs/Kconfig b/fs/romfs/Kconfig index 1a17020f9faf..ce2d6bcc6266 100644 --- a/fs/romfs/Kconfig +++ b/fs/romfs/Kconfig | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | config ROMFS_FS | 1 | config ROMFS_FS |
| 2 | tristate "ROM file system support" | 2 | tristate "ROM file system support" |
| 3 | depends on BLOCK | 3 | depends on BLOCK || MTD |
| 4 | ---help--- | 4 | ---help--- |
| 5 | This is a very small read-only file system mainly intended for | 5 | This is a very small read-only file system mainly intended for |
| 6 | initial ram disks of installation disks, but it could be used for | 6 | initial ram disks of installation disks, but it could be used for |
| @@ -14,3 +14,49 @@ config ROMFS_FS | |||
| 14 | 14 | ||
| 15 | If you don't know whether you need it, then you don't need it: | 15 | If you don't know whether you need it, then you don't need it: |
| 16 | answer N. | 16 | answer N. |
| 17 | |||
| 18 | # | ||
| 19 | # Select the backing stores to be supported | ||
| 20 | # | ||
| 21 | choice | ||
| 22 | prompt "RomFS backing stores" | ||
| 23 | depends on ROMFS_FS | ||
| 24 | default ROMFS_BACKED_BY_BLOCK | ||
| 25 | help | ||
| 26 | Select the backing stores to be supported. | ||
| 27 | |||
| 28 | config ROMFS_BACKED_BY_BLOCK | ||
| 29 | bool "Block device-backed ROM file system support" | ||
| 30 | depends on BLOCK | ||
| 31 | help | ||
| 32 | This permits ROMFS to use block devices buffered through the page | ||
| 33 | cache as the medium from which to retrieve data. It does not allow | ||
| 34 | direct mapping of the medium. | ||
| 35 | |||
| 36 | If unsure, answer Y. | ||
| 37 | |||
| 38 | config ROMFS_BACKED_BY_MTD | ||
| 39 | bool "MTD-backed ROM file system support" | ||
| 40 | depends on MTD=y || (ROMFS_FS=m && MTD) | ||
| 41 | help | ||
| 42 | This permits ROMFS to use MTD based devices directly, without the | ||
| 43 | intercession of the block layer (which may have been disabled). It | ||
| 44 | also allows direct mapping of MTD devices through romfs files under | ||
| 45 | NOMMU conditions if the underlying device is directly addressable by | ||
| 46 | the CPU. | ||
| 47 | |||
| 48 | If unsure, answer Y. | ||
| 49 | |||
| 50 | config ROMFS_BACKED_BY_BOTH | ||
| 51 | bool "Both the above" | ||
| 52 | depends on BLOCK && (MTD=y || (ROMFS_FS=m && MTD)) | ||
| 53 | endchoice | ||
| 54 | |||
| 55 | |||
| 56 | config ROMFS_ON_BLOCK | ||
| 57 | bool | ||
| 58 | default y if ROMFS_BACKED_BY_BLOCK || ROMFS_BACKED_BY_BOTH | ||
| 59 | |||
| 60 | config ROMFS_ON_MTD | ||
| 61 | bool | ||
| 62 | default y if ROMFS_BACKED_BY_MTD || ROMFS_BACKED_BY_BOTH | ||
diff --git a/fs/romfs/Makefile b/fs/romfs/Makefile index c95b21cf49a3..420beb7d495c 100644 --- a/fs/romfs/Makefile +++ b/fs/romfs/Makefile | |||
| @@ -1,7 +1,12 @@ | |||
| 1 | # | 1 | # |
| 2 | # Makefile for the linux romfs filesystem routines. | 2 | # Makefile for the linux RomFS filesystem routines. |
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | obj-$(CONFIG_ROMFS_FS) += romfs.o | 5 | obj-$(CONFIG_ROMFS_FS) += romfs.o |
| 6 | 6 | ||
| 7 | romfs-objs := inode.o | 7 | romfs-y := storage.o super.o |
| 8 | |||
| 9 | ifneq ($(CONFIG_MMU),y) | ||
| 10 | romfs-$(CONFIG_ROMFS_ON_MTD) += mmap-nommu.o | ||
| 11 | endif | ||
| 12 | |||
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c deleted file mode 100644 index 98a232f7196b..000000000000 --- a/fs/romfs/inode.c +++ /dev/null | |||
| @@ -1,665 +0,0 @@ | |||
| 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 | static struct inode *romfs_iget(struct super_block *, unsigned long); | ||
| 88 | |||
| 89 | /* instead of private superblock data */ | ||
| 90 | static inline unsigned long romfs_maxsize(struct super_block *sb) | ||
| 91 | { | ||
| 92 | return (unsigned long)sb->s_fs_info; | ||
| 93 | } | ||
| 94 | |||
| 95 | static inline struct romfs_inode_info *ROMFS_I(struct inode *inode) | ||
| 96 | { | ||
| 97 | return container_of(inode, struct romfs_inode_info, vfs_inode); | ||
| 98 | } | ||
| 99 | |||
| 100 | static __u32 | ||
| 101 | romfs_checksum(void *data, int size) | ||
| 102 | { | ||
| 103 | __u32 sum; | ||
| 104 | __be32 *ptr; | ||
| 105 | |||
| 106 | sum = 0; ptr = data; | ||
| 107 | size>>=2; | ||
| 108 | while (size>0) { | ||
| 109 | sum += be32_to_cpu(*ptr++); | ||
| 110 | size--; | ||
| 111 | } | ||
| 112 | return sum; | ||
| 113 | } | ||
| 114 | |||
| 115 | static const struct super_operations romfs_ops; | ||
| 116 | |||
| 117 | static int romfs_fill_super(struct super_block *s, void *data, int silent) | ||
| 118 | { | ||
| 119 | struct buffer_head *bh; | ||
| 120 | struct romfs_super_block *rsb; | ||
| 121 | struct inode *root; | ||
| 122 | int sz, ret = -EINVAL; | ||
| 123 | |||
| 124 | /* I would parse the options here, but there are none.. :) */ | ||
| 125 | |||
| 126 | sb_set_blocksize(s, ROMBSIZE); | ||
| 127 | s->s_maxbytes = 0xFFFFFFFF; | ||
| 128 | |||
| 129 | bh = sb_bread(s, 0); | ||
| 130 | if (!bh) { | ||
| 131 | /* XXX merge with other printk? */ | ||
| 132 | printk ("romfs: unable to read superblock\n"); | ||
| 133 | goto outnobh; | ||
| 134 | } | ||
| 135 | |||
| 136 | rsb = (struct romfs_super_block *)bh->b_data; | ||
| 137 | sz = be32_to_cpu(rsb->size); | ||
| 138 | if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 | ||
| 139 | || sz < ROMFH_SIZE) { | ||
| 140 | if (!silent) | ||
| 141 | printk ("VFS: Can't find a romfs filesystem on dev " | ||
| 142 | "%s.\n", s->s_id); | ||
| 143 | goto out; | ||
| 144 | } | ||
| 145 | if (romfs_checksum(rsb, min_t(int, sz, 512))) { | ||
| 146 | printk ("romfs: bad initial checksum on dev " | ||
| 147 | "%s.\n", s->s_id); | ||
| 148 | goto out; | ||
| 149 | } | ||
| 150 | |||
| 151 | s->s_magic = ROMFS_MAGIC; | ||
| 152 | s->s_fs_info = (void *)(long)sz; | ||
| 153 | |||
| 154 | s->s_flags |= MS_RDONLY; | ||
| 155 | |||
| 156 | /* Find the start of the fs */ | ||
| 157 | sz = (ROMFH_SIZE + | ||
| 158 | strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD) | ||
| 159 | & ROMFH_MASK; | ||
| 160 | |||
| 161 | s->s_op = &romfs_ops; | ||
| 162 | root = romfs_iget(s, sz); | ||
| 163 | if (IS_ERR(root)) { | ||
| 164 | ret = PTR_ERR(root); | ||
| 165 | goto out; | ||
| 166 | } | ||
| 167 | |||
| 168 | ret = -ENOMEM; | ||
| 169 | s->s_root = d_alloc_root(root); | ||
| 170 | if (!s->s_root) | ||
| 171 | goto outiput; | ||
| 172 | |||
| 173 | brelse(bh); | ||
| 174 | return 0; | ||
| 175 | |||
| 176 | outiput: | ||
| 177 | iput(root); | ||
| 178 | out: | ||
| 179 | brelse(bh); | ||
| 180 | outnobh: | ||
| 181 | return ret; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* That's simple too. */ | ||
| 185 | |||
| 186 | static int | ||
| 187 | romfs_statfs(struct dentry *dentry, struct kstatfs *buf) | ||
| 188 | { | ||
| 189 | buf->f_type = ROMFS_MAGIC; | ||
| 190 | buf->f_bsize = ROMBSIZE; | ||
| 191 | buf->f_bfree = buf->f_bavail = buf->f_ffree; | ||
| 192 | buf->f_blocks = (romfs_maxsize(dentry->d_sb)+ROMBSIZE-1)>>ROMBSBITS; | ||
| 193 | buf->f_namelen = ROMFS_MAXFN; | ||
| 194 | return 0; | ||
| 195 | } | ||
| 196 | |||
| 197 | /* some helper routines */ | ||
| 198 | |||
| 199 | static int | ||
| 200 | romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count) | ||
| 201 | { | ||
| 202 | struct buffer_head *bh; | ||
| 203 | unsigned long avail, maxsize, res; | ||
| 204 | |||
| 205 | maxsize = romfs_maxsize(i->i_sb); | ||
| 206 | if (offset >= maxsize) | ||
| 207 | return -1; | ||
| 208 | |||
| 209 | /* strnlen is almost always valid */ | ||
| 210 | if (count > maxsize || offset+count > maxsize) | ||
| 211 | count = maxsize-offset; | ||
| 212 | |||
| 213 | bh = sb_bread(i->i_sb, offset>>ROMBSBITS); | ||
| 214 | if (!bh) | ||
| 215 | return -1; /* error */ | ||
| 216 | |||
| 217 | avail = ROMBSIZE - (offset & ROMBMASK); | ||
| 218 | maxsize = min_t(unsigned long, count, avail); | ||
| 219 | res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize); | ||
| 220 | brelse(bh); | ||
| 221 | |||
| 222 | if (res < maxsize) | ||
| 223 | return res; /* found all of it */ | ||
| 224 | |||
| 225 | while (res < count) { | ||
| 226 | offset += maxsize; | ||
| 227 | |||
| 228 | bh = sb_bread(i->i_sb, offset>>ROMBSBITS); | ||
| 229 | if (!bh) | ||
| 230 | return -1; | ||
| 231 | maxsize = min_t(unsigned long, count - res, ROMBSIZE); | ||
| 232 | avail = strnlen(bh->b_data, maxsize); | ||
| 233 | res += avail; | ||
| 234 | brelse(bh); | ||
| 235 | if (avail < maxsize) | ||
| 236 | return res; | ||
| 237 | } | ||
| 238 | return res; | ||
| 239 | } | ||
| 240 | |||
| 241 | static int | ||
| 242 | romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count) | ||
| 243 | { | ||
| 244 | struct buffer_head *bh; | ||
| 245 | unsigned long avail, maxsize, res; | ||
| 246 | |||
| 247 | maxsize = romfs_maxsize(i->i_sb); | ||
| 248 | if (offset >= maxsize || count > maxsize || offset+count>maxsize) | ||
| 249 | return -1; | ||
| 250 | |||
| 251 | bh = sb_bread(i->i_sb, offset>>ROMBSBITS); | ||
| 252 | if (!bh) | ||
| 253 | return -1; /* error */ | ||
| 254 | |||
| 255 | avail = ROMBSIZE - (offset & ROMBMASK); | ||
| 256 | maxsize = min_t(unsigned long, count, avail); | ||
| 257 | memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize); | ||
| 258 | brelse(bh); | ||
| 259 | |||
| 260 | res = maxsize; /* all of it */ | ||
| 261 | |||
| 262 | while (res < count) { | ||
| 263 | offset += maxsize; | ||
| 264 | dest += maxsize; | ||
| 265 | |||
| 266 | bh = sb_bread(i->i_sb, offset>>ROMBSBITS); | ||
| 267 | if (!bh) | ||
| 268 | return -1; | ||
| 269 | maxsize = min_t(unsigned long, count - res, ROMBSIZE); | ||
| 270 | memcpy(dest, bh->b_data, maxsize); | ||
| 271 | brelse(bh); | ||
| 272 | res += maxsize; | ||
| 273 | } | ||
| 274 | return res; | ||
| 275 | } | ||
| 276 | |||
| 277 | static unsigned char romfs_dtype_table[] = { | ||
| 278 | DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO | ||
| 279 | }; | ||
| 280 | |||
| 281 | static int | ||
| 282 | romfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
| 283 | { | ||
| 284 | struct inode *i = filp->f_path.dentry->d_inode; | ||
| 285 | struct romfs_inode ri; | ||
| 286 | unsigned long offset, maxoff; | ||
| 287 | int j, ino, nextfh; | ||
| 288 | int stored = 0; | ||
| 289 | char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ | ||
| 290 | |||
| 291 | lock_kernel(); | ||
| 292 | |||
| 293 | maxoff = romfs_maxsize(i->i_sb); | ||
| 294 | |||
| 295 | offset = filp->f_pos; | ||
| 296 | if (!offset) { | ||
| 297 | offset = i->i_ino & ROMFH_MASK; | ||
| 298 | if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0) | ||
| 299 | goto out; | ||
| 300 | offset = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
| 301 | } | ||
| 302 | |||
| 303 | /* Not really failsafe, but we are read-only... */ | ||
| 304 | for(;;) { | ||
| 305 | if (!offset || offset >= maxoff) { | ||
| 306 | offset = maxoff; | ||
| 307 | filp->f_pos = offset; | ||
| 308 | goto out; | ||
| 309 | } | ||
| 310 | filp->f_pos = offset; | ||
| 311 | |||
| 312 | /* Fetch inode info */ | ||
| 313 | if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0) | ||
| 314 | goto out; | ||
| 315 | |||
| 316 | j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1); | ||
| 317 | if (j < 0) | ||
| 318 | goto out; | ||
| 319 | |||
| 320 | fsname[j]=0; | ||
| 321 | romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j); | ||
| 322 | |||
| 323 | ino = offset; | ||
| 324 | nextfh = be32_to_cpu(ri.next); | ||
| 325 | if ((nextfh & ROMFH_TYPE) == ROMFH_HRD) | ||
| 326 | ino = be32_to_cpu(ri.spec); | ||
| 327 | if (filldir(dirent, fsname, j, offset, ino, | ||
| 328 | romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0) { | ||
| 329 | goto out; | ||
| 330 | } | ||
| 331 | stored++; | ||
| 332 | offset = nextfh & ROMFH_MASK; | ||
| 333 | } | ||
| 334 | out: | ||
| 335 | unlock_kernel(); | ||
| 336 | return stored; | ||
| 337 | } | ||
| 338 | |||
| 339 | static struct dentry * | ||
| 340 | romfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | ||
| 341 | { | ||
| 342 | unsigned long offset, maxoff; | ||
| 343 | long res; | ||
| 344 | int fslen; | ||
| 345 | struct inode *inode = NULL; | ||
| 346 | char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ | ||
| 347 | struct romfs_inode ri; | ||
| 348 | const char *name; /* got from dentry */ | ||
| 349 | int len; | ||
| 350 | |||
| 351 | res = -EACCES; /* placeholder for "no data here" */ | ||
| 352 | offset = dir->i_ino & ROMFH_MASK; | ||
| 353 | lock_kernel(); | ||
| 354 | if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) | ||
| 355 | goto error; | ||
| 356 | |||
| 357 | maxoff = romfs_maxsize(dir->i_sb); | ||
| 358 | offset = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
| 359 | |||
| 360 | /* OK, now find the file whose name is in "dentry" in the | ||
| 361 | * directory specified by "dir". */ | ||
| 362 | |||
| 363 | name = dentry->d_name.name; | ||
| 364 | len = dentry->d_name.len; | ||
| 365 | |||
| 366 | for(;;) { | ||
| 367 | if (!offset || offset >= maxoff) | ||
| 368 | goto success; /* negative success */ | ||
| 369 | if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) | ||
| 370 | goto error; | ||
| 371 | |||
| 372 | /* try to match the first 16 bytes of name */ | ||
| 373 | fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE); | ||
| 374 | if (len < ROMFH_SIZE) { | ||
| 375 | if (len == fslen) { | ||
| 376 | /* both are shorter, and same size */ | ||
| 377 | romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1); | ||
| 378 | if (strncmp (name, fsname, len) == 0) | ||
| 379 | break; | ||
| 380 | } | ||
| 381 | } else if (fslen >= ROMFH_SIZE) { | ||
| 382 | /* both are longer; XXX optimize max size */ | ||
| 383 | fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1); | ||
| 384 | if (len == fslen) { | ||
| 385 | romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1); | ||
| 386 | if (strncmp(name, fsname, len) == 0) | ||
| 387 | break; | ||
| 388 | } | ||
| 389 | } | ||
| 390 | /* next entry */ | ||
| 391 | offset = be32_to_cpu(ri.next) & ROMFH_MASK; | ||
| 392 | } | ||
| 393 | |||
| 394 | /* Hard link handling */ | ||
| 395 | if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD) | ||
| 396 | offset = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
| 397 | |||
| 398 | inode = romfs_iget(dir->i_sb, offset); | ||
| 399 | if (IS_ERR(inode)) { | ||
| 400 | res = PTR_ERR(inode); | ||
| 401 | goto error; | ||
| 402 | } | ||
| 403 | |||
| 404 | success: | ||
| 405 | d_add(dentry, inode); | ||
| 406 | res = 0; | ||
| 407 | error: | ||
| 408 | 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 | loff_t offset, size; | ||
| 422 | unsigned long filled; | ||
| 423 | void *buf; | ||
| 424 | int result = -EIO; | ||
| 425 | |||
| 426 | page_cache_get(page); | ||
| 427 | lock_kernel(); | ||
| 428 | buf = kmap(page); | ||
| 429 | if (!buf) | ||
| 430 | goto err_out; | ||
| 431 | |||
| 432 | /* 32 bit warning -- but not for us :) */ | ||
| 433 | offset = page_offset(page); | ||
| 434 | size = i_size_read(inode); | ||
| 435 | filled = 0; | ||
| 436 | result = 0; | ||
| 437 | if (offset < size) { | ||
| 438 | unsigned long readlen; | ||
| 439 | |||
| 440 | size -= offset; | ||
| 441 | readlen = size > PAGE_SIZE ? PAGE_SIZE : size; | ||
| 442 | |||
| 443 | filled = romfs_copyfrom(inode, buf, ROMFS_I(inode)->i_dataoffset+offset, readlen); | ||
| 444 | |||
| 445 | if (filled != readlen) { | ||
| 446 | SetPageError(page); | ||
| 447 | filled = 0; | ||
| 448 | result = -EIO; | ||
| 449 | } | ||
| 450 | } | ||
| 451 | |||
| 452 | if (filled < PAGE_SIZE) | ||
| 453 | memset(buf + filled, 0, PAGE_SIZE-filled); | ||
| 454 | |||
| 455 | if (!result) | ||
| 456 | SetPageUptodate(page); | ||
| 457 | flush_dcache_page(page); | ||
| 458 | |||
| 459 | unlock_page(page); | ||
| 460 | |||
| 461 | kunmap(page); | ||
| 462 | err_out: | ||
| 463 | page_cache_release(page); | ||
| 464 | unlock_kernel(); | ||
| 465 | |||
| 466 | return result; | ||
| 467 | } | ||
| 468 | |||
| 469 | /* Mapping from our types to the kernel */ | ||
| 470 | |||
| 471 | static const struct address_space_operations romfs_aops = { | ||
| 472 | .readpage = romfs_readpage | ||
| 473 | }; | ||
| 474 | |||
| 475 | static const struct file_operations romfs_dir_operations = { | ||
| 476 | .read = generic_read_dir, | ||
| 477 | .readdir = romfs_readdir, | ||
| 478 | }; | ||
| 479 | |||
| 480 | static const struct inode_operations romfs_dir_inode_operations = { | ||
| 481 | .lookup = romfs_lookup, | ||
| 482 | }; | ||
| 483 | |||
| 484 | static mode_t romfs_modemap[] = | ||
| 485 | { | ||
| 486 | 0, S_IFDIR+0644, S_IFREG+0644, S_IFLNK+0777, | ||
| 487 | S_IFBLK+0600, S_IFCHR+0600, S_IFSOCK+0644, S_IFIFO+0644 | ||
| 488 | }; | ||
| 489 | |||
| 490 | static struct inode * | ||
| 491 | romfs_iget(struct super_block *sb, unsigned long ino) | ||
| 492 | { | ||
| 493 | int nextfh, ret; | ||
| 494 | struct romfs_inode ri; | ||
| 495 | struct inode *i; | ||
| 496 | |||
| 497 | ino &= ROMFH_MASK; | ||
| 498 | i = iget_locked(sb, ino); | ||
| 499 | if (!i) | ||
| 500 | return ERR_PTR(-ENOMEM); | ||
| 501 | if (!(i->i_state & I_NEW)) | ||
| 502 | return i; | ||
| 503 | |||
| 504 | i->i_mode = 0; | ||
| 505 | |||
| 506 | /* Loop for finding the real hard link */ | ||
| 507 | for(;;) { | ||
| 508 | if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) { | ||
| 509 | printk(KERN_ERR "romfs: read error for inode 0x%lx\n", | ||
| 510 | ino); | ||
| 511 | iget_failed(i); | ||
| 512 | return ERR_PTR(-EIO); | ||
| 513 | } | ||
| 514 | /* XXX: do romfs_checksum here too (with name) */ | ||
| 515 | |||
| 516 | nextfh = be32_to_cpu(ri.next); | ||
| 517 | if ((nextfh & ROMFH_TYPE) != ROMFH_HRD) | ||
| 518 | break; | ||
| 519 | |||
| 520 | ino = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
| 521 | } | ||
| 522 | |||
| 523 | i->i_nlink = 1; /* Hard to decide.. */ | ||
| 524 | i->i_size = be32_to_cpu(ri.size); | ||
| 525 | i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0; | ||
| 526 | i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0; | ||
| 527 | |||
| 528 | /* Precalculate the data offset */ | ||
| 529 | ret = romfs_strnlen(i, ino + ROMFH_SIZE, ROMFS_MAXFN); | ||
| 530 | if (ret >= 0) | ||
| 531 | ino = (ROMFH_SIZE + ret + 1 + ROMFH_PAD) & ROMFH_MASK; | ||
| 532 | else | ||
| 533 | ino = 0; | ||
| 534 | |||
| 535 | ROMFS_I(i)->i_metasize = ino; | ||
| 536 | ROMFS_I(i)->i_dataoffset = ino+(i->i_ino&ROMFH_MASK); | ||
| 537 | |||
| 538 | /* Compute permissions */ | ||
| 539 | ino = romfs_modemap[nextfh & ROMFH_TYPE]; | ||
| 540 | /* only "normal" files have ops */ | ||
| 541 | switch (nextfh & ROMFH_TYPE) { | ||
| 542 | case 1: | ||
| 543 | i->i_size = ROMFS_I(i)->i_metasize; | ||
| 544 | i->i_op = &romfs_dir_inode_operations; | ||
| 545 | i->i_fop = &romfs_dir_operations; | ||
| 546 | if (nextfh & ROMFH_EXEC) | ||
| 547 | ino |= S_IXUGO; | ||
| 548 | i->i_mode = ino; | ||
| 549 | break; | ||
| 550 | case 2: | ||
| 551 | i->i_fop = &generic_ro_fops; | ||
| 552 | i->i_data.a_ops = &romfs_aops; | ||
| 553 | if (nextfh & ROMFH_EXEC) | ||
| 554 | ino |= S_IXUGO; | ||
| 555 | i->i_mode = ino; | ||
| 556 | break; | ||
| 557 | case 3: | ||
| 558 | i->i_op = &page_symlink_inode_operations; | ||
| 559 | i->i_data.a_ops = &romfs_aops; | ||
| 560 | i->i_mode = ino | S_IRWXUGO; | ||
| 561 | break; | ||
| 562 | default: | ||
| 563 | /* depending on MBZ for sock/fifos */ | ||
| 564 | nextfh = be32_to_cpu(ri.spec); | ||
| 565 | init_special_inode(i, ino, | ||
| 566 | MKDEV(nextfh>>16,nextfh&0xffff)); | ||
| 567 | } | ||
| 568 | unlock_new_inode(i); | ||
| 569 | return i; | ||
| 570 | } | ||
| 571 | |||
| 572 | static struct kmem_cache * romfs_inode_cachep; | ||
| 573 | |||
| 574 | static struct inode *romfs_alloc_inode(struct super_block *sb) | ||
| 575 | { | ||
| 576 | struct romfs_inode_info *ei; | ||
| 577 | ei = kmem_cache_alloc(romfs_inode_cachep, GFP_KERNEL); | ||
| 578 | if (!ei) | ||
| 579 | return NULL; | ||
| 580 | return &ei->vfs_inode; | ||
| 581 | } | ||
| 582 | |||
| 583 | static void romfs_destroy_inode(struct inode *inode) | ||
| 584 | { | ||
| 585 | kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode)); | ||
| 586 | } | ||
| 587 | |||
| 588 | static void init_once(void *foo) | ||
| 589 | { | ||
| 590 | struct romfs_inode_info *ei = foo; | ||
| 591 | |||
| 592 | inode_init_once(&ei->vfs_inode); | ||
| 593 | } | ||
| 594 | |||
| 595 | static int init_inodecache(void) | ||
| 596 | { | ||
| 597 | romfs_inode_cachep = kmem_cache_create("romfs_inode_cache", | ||
| 598 | sizeof(struct romfs_inode_info), | ||
| 599 | 0, (SLAB_RECLAIM_ACCOUNT| | ||
| 600 | SLAB_MEM_SPREAD), | ||
| 601 | init_once); | ||
| 602 | if (romfs_inode_cachep == NULL) | ||
| 603 | return -ENOMEM; | ||
| 604 | return 0; | ||
| 605 | } | ||
| 606 | |||
| 607 | static void destroy_inodecache(void) | ||
| 608 | { | ||
| 609 | kmem_cache_destroy(romfs_inode_cachep); | ||
| 610 | } | ||
| 611 | |||
| 612 | static int romfs_remount(struct super_block *sb, int *flags, char *data) | ||
| 613 | { | ||
| 614 | *flags |= MS_RDONLY; | ||
| 615 | return 0; | ||
| 616 | } | ||
| 617 | |||
| 618 | static const struct super_operations romfs_ops = { | ||
| 619 | .alloc_inode = romfs_alloc_inode, | ||
| 620 | .destroy_inode = romfs_destroy_inode, | ||
| 621 | .statfs = romfs_statfs, | ||
| 622 | .remount_fs = romfs_remount, | ||
| 623 | }; | ||
| 624 | |||
| 625 | static int romfs_get_sb(struct file_system_type *fs_type, | ||
| 626 | int flags, const char *dev_name, void *data, struct vfsmount *mnt) | ||
| 627 | { | ||
| 628 | return get_sb_bdev(fs_type, flags, dev_name, data, romfs_fill_super, | ||
| 629 | mnt); | ||
| 630 | } | ||
| 631 | |||
| 632 | static struct file_system_type romfs_fs_type = { | ||
| 633 | .owner = THIS_MODULE, | ||
| 634 | .name = "romfs", | ||
| 635 | .get_sb = romfs_get_sb, | ||
| 636 | .kill_sb = kill_block_super, | ||
| 637 | .fs_flags = FS_REQUIRES_DEV, | ||
| 638 | }; | ||
| 639 | |||
| 640 | static int __init init_romfs_fs(void) | ||
| 641 | { | ||
| 642 | int err = init_inodecache(); | ||
| 643 | if (err) | ||
| 644 | goto out1; | ||
| 645 | err = register_filesystem(&romfs_fs_type); | ||
| 646 | if (err) | ||
| 647 | goto out; | ||
| 648 | return 0; | ||
| 649 | out: | ||
| 650 | destroy_inodecache(); | ||
| 651 | out1: | ||
| 652 | return err; | ||
| 653 | } | ||
| 654 | |||
| 655 | static void __exit exit_romfs_fs(void) | ||
| 656 | { | ||
| 657 | unregister_filesystem(&romfs_fs_type); | ||
| 658 | destroy_inodecache(); | ||
| 659 | } | ||
| 660 | |||
| 661 | /* Yes, works even as a module... :) */ | ||
| 662 | |||
| 663 | module_init(init_romfs_fs) | ||
| 664 | module_exit(exit_romfs_fs) | ||
| 665 | MODULE_LICENSE("GPL"); | ||
diff --git a/fs/romfs/internal.h b/fs/romfs/internal.h new file mode 100644 index 000000000000..06044a9dc62d --- /dev/null +++ b/fs/romfs/internal.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | /* RomFS internal definitions | ||
| 2 | * | ||
| 3 | * Copyright © 2007 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the License, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/romfs_fs.h> | ||
| 13 | |||
| 14 | struct romfs_inode_info { | ||
| 15 | struct inode vfs_inode; | ||
| 16 | unsigned long i_metasize; /* size of non-data area */ | ||
| 17 | unsigned long i_dataoffset; /* from the start of fs */ | ||
| 18 | }; | ||
| 19 | |||
| 20 | static inline size_t romfs_maxsize(struct super_block *sb) | ||
| 21 | { | ||
| 22 | return (size_t) (unsigned long) sb->s_fs_info; | ||
| 23 | } | ||
| 24 | |||
| 25 | static inline struct romfs_inode_info *ROMFS_I(struct inode *inode) | ||
| 26 | { | ||
| 27 | return container_of(inode, struct romfs_inode_info, vfs_inode); | ||
| 28 | } | ||
| 29 | |||
| 30 | /* | ||
| 31 | * mmap-nommu.c | ||
| 32 | */ | ||
| 33 | #if !defined(CONFIG_MMU) && defined(CONFIG_ROMFS_ON_MTD) | ||
| 34 | extern const struct file_operations romfs_ro_fops; | ||
| 35 | #else | ||
| 36 | #define romfs_ro_fops generic_ro_fops | ||
| 37 | #endif | ||
| 38 | |||
| 39 | /* | ||
| 40 | * storage.c | ||
| 41 | */ | ||
| 42 | extern int romfs_dev_read(struct super_block *sb, unsigned long pos, | ||
| 43 | void *buf, size_t buflen); | ||
| 44 | extern ssize_t romfs_dev_strnlen(struct super_block *sb, | ||
| 45 | unsigned long pos, size_t maxlen); | ||
| 46 | extern int romfs_dev_strncmp(struct super_block *sb, unsigned long pos, | ||
| 47 | const char *str, size_t size); | ||
diff --git a/fs/romfs/mmap-nommu.c b/fs/romfs/mmap-nommu.c new file mode 100644 index 000000000000..f0511e816967 --- /dev/null +++ b/fs/romfs/mmap-nommu.c | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | /* NOMMU mmap support for RomFS on MTD devices | ||
| 2 | * | ||
| 3 | * Copyright © 2007 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the License, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/mm.h> | ||
| 13 | #include <linux/mtd/super.h> | ||
| 14 | #include "internal.h" | ||
| 15 | |||
| 16 | /* | ||
| 17 | * try to determine where a shared mapping can be made | ||
| 18 | * - only supported for NOMMU at the moment (MMU can't doesn't copy private | ||
| 19 | * mappings) | ||
| 20 | * - attempts to map through to the underlying MTD device | ||
| 21 | */ | ||
| 22 | static unsigned long romfs_get_unmapped_area(struct file *file, | ||
| 23 | unsigned long addr, | ||
| 24 | unsigned long len, | ||
| 25 | unsigned long pgoff, | ||
| 26 | unsigned long flags) | ||
| 27 | { | ||
| 28 | struct inode *inode = file->f_mapping->host; | ||
| 29 | struct mtd_info *mtd = inode->i_sb->s_mtd; | ||
| 30 | unsigned long isize, offset; | ||
| 31 | |||
| 32 | if (!mtd) | ||
| 33 | goto cant_map_directly; | ||
| 34 | |||
| 35 | isize = i_size_read(inode); | ||
| 36 | offset = pgoff << PAGE_SHIFT; | ||
| 37 | if (offset > isize || len > isize || offset > isize - len) | ||
| 38 | return (unsigned long) -EINVAL; | ||
| 39 | |||
| 40 | /* we need to call down to the MTD layer to do the actual mapping */ | ||
| 41 | if (mtd->get_unmapped_area) { | ||
| 42 | if (addr != 0) | ||
| 43 | return (unsigned long) -EINVAL; | ||
| 44 | |||
| 45 | if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT)) | ||
| 46 | return (unsigned long) -EINVAL; | ||
| 47 | |||
| 48 | offset += ROMFS_I(inode)->i_dataoffset; | ||
| 49 | if (offset > mtd->size - len) | ||
| 50 | return (unsigned long) -EINVAL; | ||
| 51 | |||
| 52 | return mtd->get_unmapped_area(mtd, len, offset, flags); | ||
| 53 | } | ||
| 54 | |||
| 55 | cant_map_directly: | ||
| 56 | return (unsigned long) -ENOSYS; | ||
| 57 | } | ||
| 58 | |||
| 59 | /* | ||
| 60 | * permit a R/O mapping to be made directly through onto an MTD device if | ||
| 61 | * possible | ||
| 62 | */ | ||
| 63 | static int romfs_mmap(struct file *file, struct vm_area_struct *vma) | ||
| 64 | { | ||
| 65 | return vma->vm_flags & (VM_SHARED | VM_MAYSHARE) ? 0 : -ENOSYS; | ||
| 66 | } | ||
| 67 | |||
| 68 | const struct file_operations romfs_ro_fops = { | ||
| 69 | .llseek = generic_file_llseek, | ||
| 70 | .read = do_sync_read, | ||
| 71 | .aio_read = generic_file_aio_read, | ||
| 72 | .splice_read = generic_file_splice_read, | ||
| 73 | .mmap = romfs_mmap, | ||
| 74 | .get_unmapped_area = romfs_get_unmapped_area, | ||
| 75 | }; | ||
diff --git a/fs/romfs/storage.c b/fs/romfs/storage.c new file mode 100644 index 000000000000..7e3e1e12a081 --- /dev/null +++ b/fs/romfs/storage.c | |||
| @@ -0,0 +1,261 @@ | |||
| 1 | /* RomFS storage access routines | ||
| 2 | * | ||
| 3 | * Copyright © 2007 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the License, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/fs.h> | ||
| 13 | #include <linux/mtd/super.h> | ||
| 14 | #include <linux/buffer_head.h> | ||
| 15 | #include "internal.h" | ||
| 16 | |||
| 17 | #if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK) | ||
| 18 | #error no ROMFS backing store interface configured | ||
| 19 | #endif | ||
| 20 | |||
| 21 | #ifdef CONFIG_ROMFS_ON_MTD | ||
| 22 | #define ROMFS_MTD_READ(sb, ...) ((sb)->s_mtd->read((sb)->s_mtd, ##__VA_ARGS__)) | ||
| 23 | |||
| 24 | /* | ||
| 25 | * read data from an romfs image on an MTD device | ||
| 26 | */ | ||
| 27 | static int romfs_mtd_read(struct super_block *sb, unsigned long pos, | ||
| 28 | void *buf, size_t buflen) | ||
| 29 | { | ||
| 30 | size_t rlen; | ||
| 31 | int ret; | ||
| 32 | |||
| 33 | ret = ROMFS_MTD_READ(sb, pos, buflen, &rlen, buf); | ||
| 34 | return (ret < 0 || rlen != buflen) ? -EIO : 0; | ||
| 35 | } | ||
| 36 | |||
| 37 | /* | ||
| 38 | * determine the length of a string in a romfs image on an MTD device | ||
| 39 | */ | ||
| 40 | static ssize_t romfs_mtd_strnlen(struct super_block *sb, | ||
| 41 | unsigned long pos, size_t maxlen) | ||
| 42 | { | ||
| 43 | ssize_t n = 0; | ||
| 44 | size_t segment; | ||
| 45 | u_char buf[16], *p; | ||
| 46 | size_t len; | ||
| 47 | int ret; | ||
| 48 | |||
| 49 | /* scan the string up to 16 bytes at a time */ | ||
| 50 | while (maxlen > 0) { | ||
| 51 | segment = min_t(size_t, maxlen, 16); | ||
| 52 | ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); | ||
| 53 | if (ret < 0) | ||
| 54 | return ret; | ||
| 55 | p = memchr(buf, 0, len); | ||
| 56 | if (p) | ||
| 57 | return n + (p - buf); | ||
| 58 | maxlen -= len; | ||
| 59 | pos += len; | ||
| 60 | n += len; | ||
| 61 | } | ||
| 62 | |||
| 63 | return n; | ||
| 64 | } | ||
| 65 | |||
| 66 | /* | ||
| 67 | * compare a string to one in a romfs image on MTD | ||
| 68 | * - return 1 if matched, 0 if differ, -ve if error | ||
| 69 | */ | ||
| 70 | static int romfs_mtd_strncmp(struct super_block *sb, unsigned long pos, | ||
| 71 | const char *str, size_t size) | ||
| 72 | { | ||
| 73 | u_char buf[16]; | ||
| 74 | size_t len, segment; | ||
| 75 | int ret; | ||
| 76 | |||
| 77 | /* scan the string up to 16 bytes at a time */ | ||
| 78 | while (size > 0) { | ||
| 79 | segment = min_t(size_t, size, 16); | ||
| 80 | ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf); | ||
| 81 | if (ret < 0) | ||
| 82 | return ret; | ||
| 83 | if (memcmp(buf, str, len) != 0) | ||
| 84 | return 0; | ||
| 85 | size -= len; | ||
| 86 | pos += len; | ||
| 87 | str += len; | ||
| 88 | } | ||
| 89 | |||
| 90 | return 1; | ||
| 91 | } | ||
| 92 | #endif /* CONFIG_ROMFS_ON_MTD */ | ||
| 93 | |||
| 94 | #ifdef CONFIG_ROMFS_ON_BLOCK | ||
| 95 | /* | ||
| 96 | * read data from an romfs image on a block device | ||
| 97 | */ | ||
| 98 | static int romfs_blk_read(struct super_block *sb, unsigned long pos, | ||
| 99 | void *buf, size_t buflen) | ||
| 100 | { | ||
| 101 | struct buffer_head *bh; | ||
| 102 | unsigned long offset; | ||
| 103 | size_t segment; | ||
| 104 | |||
| 105 | /* copy the string up to blocksize bytes at a time */ | ||
| 106 | while (buflen > 0) { | ||
| 107 | offset = pos & (ROMBSIZE - 1); | ||
| 108 | segment = min_t(size_t, buflen, ROMBSIZE - offset); | ||
| 109 | bh = sb_bread(sb, pos >> ROMBSBITS); | ||
| 110 | if (!bh) | ||
| 111 | return -EIO; | ||
| 112 | memcpy(buf, bh->b_data + offset, segment); | ||
| 113 | brelse(bh); | ||
| 114 | buflen -= segment; | ||
| 115 | pos += segment; | ||
| 116 | } | ||
| 117 | |||
| 118 | return 0; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* | ||
| 122 | * determine the length of a string in romfs on a block device | ||
| 123 | */ | ||
| 124 | static ssize_t romfs_blk_strnlen(struct super_block *sb, | ||
| 125 | unsigned long pos, size_t limit) | ||
| 126 | { | ||
| 127 | struct buffer_head *bh; | ||
| 128 | unsigned long offset; | ||
| 129 | ssize_t n = 0; | ||
| 130 | size_t segment; | ||
| 131 | u_char *buf, *p; | ||
| 132 | |||
| 133 | /* scan the string up to blocksize bytes at a time */ | ||
| 134 | while (limit > 0) { | ||
| 135 | offset = pos & (ROMBSIZE - 1); | ||
| 136 | segment = min_t(size_t, limit, ROMBSIZE - offset); | ||
| 137 | bh = sb_bread(sb, pos >> ROMBSBITS); | ||
| 138 | if (!bh) | ||
| 139 | return -EIO; | ||
| 140 | buf = bh->b_data + offset; | ||
| 141 | p = memchr(buf, 0, segment); | ||
| 142 | brelse(bh); | ||
| 143 | if (p) | ||
| 144 | return n + (p - buf); | ||
| 145 | limit -= segment; | ||
| 146 | pos += segment; | ||
| 147 | n += segment; | ||
| 148 | } | ||
| 149 | |||
| 150 | return n; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* | ||
| 154 | * compare a string to one in a romfs image on a block device | ||
| 155 | * - return 1 if matched, 0 if differ, -ve if error | ||
| 156 | */ | ||
| 157 | static int romfs_blk_strncmp(struct super_block *sb, unsigned long pos, | ||
| 158 | const char *str, size_t size) | ||
| 159 | { | ||
| 160 | struct buffer_head *bh; | ||
| 161 | unsigned long offset; | ||
| 162 | size_t segment; | ||
| 163 | bool x; | ||
| 164 | |||
| 165 | /* scan the string up to 16 bytes at a time */ | ||
| 166 | while (size > 0) { | ||
| 167 | offset = pos & (ROMBSIZE - 1); | ||
| 168 | segment = min_t(size_t, size, ROMBSIZE - offset); | ||
| 169 | bh = sb_bread(sb, pos >> ROMBSBITS); | ||
| 170 | if (!bh) | ||
| 171 | return -EIO; | ||
| 172 | x = (memcmp(bh->b_data + offset, str, segment) != 0); | ||
| 173 | brelse(bh); | ||
| 174 | if (x) | ||
| 175 | return 0; | ||
| 176 | size -= segment; | ||
| 177 | pos += segment; | ||
| 178 | str += segment; | ||
| 179 | } | ||
| 180 | |||
| 181 | return 1; | ||
| 182 | } | ||
| 183 | #endif /* CONFIG_ROMFS_ON_BLOCK */ | ||
| 184 | |||
| 185 | /* | ||
| 186 | * read data from the romfs image | ||
| 187 | */ | ||
| 188 | int romfs_dev_read(struct super_block *sb, unsigned long pos, | ||
| 189 | void *buf, size_t buflen) | ||
| 190 | { | ||
| 191 | size_t limit; | ||
| 192 | |||
| 193 | limit = romfs_maxsize(sb); | ||
| 194 | if (pos >= limit) | ||
| 195 | return -EIO; | ||
| 196 | if (buflen > limit - pos) | ||
| 197 | buflen = limit - pos; | ||
| 198 | |||
| 199 | #ifdef CONFIG_ROMFS_ON_MTD | ||
| 200 | if (sb->s_mtd) | ||
| 201 | return romfs_mtd_read(sb, pos, buf, buflen); | ||
| 202 | #endif | ||
| 203 | #ifdef CONFIG_ROMFS_ON_BLOCK | ||
| 204 | if (sb->s_bdev) | ||
| 205 | return romfs_blk_read(sb, pos, buf, buflen); | ||
| 206 | #endif | ||
| 207 | return -EIO; | ||
| 208 | } | ||
| 209 | |||
| 210 | /* | ||
| 211 | * determine the length of a string in romfs | ||
| 212 | */ | ||
| 213 | ssize_t romfs_dev_strnlen(struct super_block *sb, | ||
| 214 | unsigned long pos, size_t maxlen) | ||
| 215 | { | ||
| 216 | size_t limit; | ||
| 217 | |||
| 218 | limit = romfs_maxsize(sb); | ||
| 219 | if (pos >= limit) | ||
| 220 | return -EIO; | ||
| 221 | if (maxlen > limit - pos) | ||
| 222 | maxlen = limit - pos; | ||
| 223 | |||
| 224 | #ifdef CONFIG_ROMFS_ON_MTD | ||
| 225 | if (sb->s_mtd) | ||
| 226 | return romfs_mtd_strnlen(sb, pos, limit); | ||
| 227 | #endif | ||
| 228 | #ifdef CONFIG_ROMFS_ON_BLOCK | ||
| 229 | if (sb->s_bdev) | ||
| 230 | return romfs_blk_strnlen(sb, pos, limit); | ||
| 231 | #endif | ||
| 232 | return -EIO; | ||
| 233 | } | ||
| 234 | |||
| 235 | /* | ||
| 236 | * compare a string to one in romfs | ||
| 237 | * - return 1 if matched, 0 if differ, -ve if error | ||
| 238 | */ | ||
| 239 | int romfs_dev_strncmp(struct super_block *sb, unsigned long pos, | ||
| 240 | const char *str, size_t size) | ||
| 241 | { | ||
| 242 | size_t limit; | ||
| 243 | |||
| 244 | limit = romfs_maxsize(sb); | ||
| 245 | if (pos >= limit) | ||
| 246 | return -EIO; | ||
| 247 | if (size > ROMFS_MAXFN) | ||
| 248 | return -ENAMETOOLONG; | ||
| 249 | if (size > limit - pos) | ||
| 250 | return -EIO; | ||
| 251 | |||
| 252 | #ifdef CONFIG_ROMFS_ON_MTD | ||
| 253 | if (sb->s_mtd) | ||
| 254 | return romfs_mtd_strncmp(sb, pos, str, size); | ||
| 255 | #endif | ||
| 256 | #ifdef CONFIG_ROMFS_ON_BLOCK | ||
| 257 | if (sb->s_bdev) | ||
| 258 | return romfs_blk_strncmp(sb, pos, str, size); | ||
| 259 | #endif | ||
| 260 | return -EIO; | ||
| 261 | } | ||
diff --git a/fs/romfs/super.c b/fs/romfs/super.c new file mode 100644 index 000000000000..1e548a4975ba --- /dev/null +++ b/fs/romfs/super.c | |||
| @@ -0,0 +1,648 @@ | |||
| 1 | /* Block- or MTD-based romfs | ||
| 2 | * | ||
| 3 | * Copyright © 2007 Red Hat, Inc. All Rights Reserved. | ||
| 4 | * Written by David Howells (dhowells@redhat.com) | ||
| 5 | * | ||
| 6 | * Derived from: ROMFS file system, Linux implementation | ||
| 7 | * | ||
| 8 | * Copyright © 1997-1999 Janos Farkas <chexum@shadow.banki.hu> | ||
| 9 | * | ||
| 10 | * Using parts of the minix filesystem | ||
| 11 | * Copyright © 1991, 1992 Linus Torvalds | ||
| 12 | * | ||
| 13 | * and parts of the affs filesystem additionally | ||
| 14 | * Copyright © 1993 Ray Burr | ||
| 15 | * Copyright © 1996 Hans-Joachim Widmaier | ||
| 16 | * | ||
| 17 | * Changes | ||
| 18 | * Changed for 2.1.19 modules | ||
| 19 | * Jan 1997 Initial release | ||
| 20 | * Jun 1997 2.1.43+ changes | ||
| 21 | * Proper page locking in readpage | ||
| 22 | * Changed to work with 2.1.45+ fs | ||
| 23 | * Jul 1997 Fixed follow_link | ||
| 24 | * 2.1.47 | ||
| 25 | * lookup shouldn't return -ENOENT | ||
| 26 | * from Horst von Brand: | ||
| 27 | * fail on wrong checksum | ||
| 28 | * double unlock_super was possible | ||
| 29 | * correct namelen for statfs | ||
| 30 | * spotted by Bill Hawes: | ||
| 31 | * readlink shouldn't iput() | ||
| 32 | * Jun 1998 2.1.106 from Avery Pennarun: glibc scandir() | ||
| 33 | * exposed a problem in readdir | ||
| 34 | * 2.1.107 code-freeze spellchecker run | ||
| 35 | * Aug 1998 2.1.118+ VFS changes | ||
| 36 | * Sep 1998 2.1.122 another VFS change (follow_link) | ||
| 37 | * Apr 1999 2.2.7 no more EBADF checking in | ||
| 38 | * lookup/readdir, use ERR_PTR | ||
| 39 | * Jun 1999 2.3.6 d_alloc_root use changed | ||
| 40 | * 2.3.9 clean up usage of ENOENT/negative | ||
| 41 | * dentries in lookup | ||
| 42 | * clean up page flags setting | ||
| 43 | * (error, uptodate, locking) in | ||
| 44 | * in readpage | ||
| 45 | * use init_special_inode for | ||
| 46 | * fifos/sockets (and streamline) in | ||
| 47 | * read_inode, fix _ops table order | ||
| 48 | * Aug 1999 2.3.16 __initfunc() => __init change | ||
| 49 | * Oct 1999 2.3.24 page->owner hack obsoleted | ||
| 50 | * Nov 1999 2.3.27 2.3.25+ page->offset => index change | ||
| 51 | * | ||
| 52 | * | ||
| 53 | * This program is free software; you can redistribute it and/or | ||
| 54 | * modify it under the terms of the GNU General Public Licence | ||
| 55 | * as published by the Free Software Foundation; either version | ||
| 56 | * 2 of the Licence, or (at your option) any later version. | ||
| 57 | */ | ||
| 58 | |||
| 59 | #include <linux/module.h> | ||
| 60 | #include <linux/string.h> | ||
| 61 | #include <linux/fs.h> | ||
| 62 | #include <linux/time.h> | ||
| 63 | #include <linux/slab.h> | ||
| 64 | #include <linux/init.h> | ||
| 65 | #include <linux/blkdev.h> | ||
| 66 | #include <linux/parser.h> | ||
| 67 | #include <linux/mount.h> | ||
| 68 | #include <linux/namei.h> | ||
| 69 | #include <linux/statfs.h> | ||
| 70 | #include <linux/mtd/super.h> | ||
| 71 | #include <linux/ctype.h> | ||
| 72 | #include <linux/highmem.h> | ||
| 73 | #include <linux/pagemap.h> | ||
| 74 | #include <linux/uaccess.h> | ||
| 75 | #include "internal.h" | ||
| 76 | |||
| 77 | static struct kmem_cache *romfs_inode_cachep; | ||
| 78 | |||
| 79 | static const umode_t romfs_modemap[8] = { | ||
| 80 | 0, /* hard link */ | ||
| 81 | S_IFDIR | 0644, /* directory */ | ||
| 82 | S_IFREG | 0644, /* regular file */ | ||
| 83 | S_IFLNK | 0777, /* symlink */ | ||
| 84 | S_IFBLK | 0600, /* blockdev */ | ||
| 85 | S_IFCHR | 0600, /* chardev */ | ||
| 86 | S_IFSOCK | 0644, /* socket */ | ||
| 87 | S_IFIFO | 0644 /* FIFO */ | ||
| 88 | }; | ||
| 89 | |||
| 90 | static const unsigned char romfs_dtype_table[] = { | ||
| 91 | DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO | ||
| 92 | }; | ||
| 93 | |||
| 94 | static struct inode *romfs_iget(struct super_block *sb, unsigned long pos); | ||
| 95 | |||
| 96 | /* | ||
| 97 | * read a page worth of data from the image | ||
| 98 | */ | ||
| 99 | static int romfs_readpage(struct file *file, struct page *page) | ||
| 100 | { | ||
| 101 | struct inode *inode = page->mapping->host; | ||
| 102 | loff_t offset, size; | ||
| 103 | unsigned long fillsize, pos; | ||
| 104 | void *buf; | ||
| 105 | int ret; | ||
| 106 | |||
| 107 | buf = kmap(page); | ||
| 108 | if (!buf) | ||
| 109 | return -ENOMEM; | ||
| 110 | |||
| 111 | /* 32 bit warning -- but not for us :) */ | ||
| 112 | offset = page_offset(page); | ||
| 113 | size = i_size_read(inode); | ||
| 114 | fillsize = 0; | ||
| 115 | ret = 0; | ||
| 116 | if (offset < size) { | ||
| 117 | size -= offset; | ||
| 118 | fillsize = size > PAGE_SIZE ? PAGE_SIZE : size; | ||
| 119 | |||
| 120 | pos = ROMFS_I(inode)->i_dataoffset + offset; | ||
| 121 | |||
| 122 | ret = romfs_dev_read(inode->i_sb, pos, buf, fillsize); | ||
| 123 | if (ret < 0) { | ||
| 124 | SetPageError(page); | ||
| 125 | fillsize = 0; | ||
| 126 | ret = -EIO; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | if (fillsize < PAGE_SIZE) | ||
| 131 | memset(buf + fillsize, 0, PAGE_SIZE - fillsize); | ||
| 132 | if (ret == 0) | ||
| 133 | SetPageUptodate(page); | ||
| 134 | |||
| 135 | flush_dcache_page(page); | ||
| 136 | kunmap(page); | ||
| 137 | unlock_page(page); | ||
| 138 | return ret; | ||
| 139 | } | ||
| 140 | |||
| 141 | static const struct address_space_operations romfs_aops = { | ||
| 142 | .readpage = romfs_readpage | ||
| 143 | }; | ||
| 144 | |||
| 145 | /* | ||
| 146 | * read the entries from a directory | ||
| 147 | */ | ||
| 148 | static int romfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
| 149 | { | ||
| 150 | struct inode *i = filp->f_dentry->d_inode; | ||
| 151 | struct romfs_inode ri; | ||
| 152 | unsigned long offset, maxoff; | ||
| 153 | int j, ino, nextfh; | ||
| 154 | int stored = 0; | ||
| 155 | char fsname[ROMFS_MAXFN]; /* XXX dynamic? */ | ||
| 156 | int ret; | ||
| 157 | |||
| 158 | maxoff = romfs_maxsize(i->i_sb); | ||
| 159 | |||
| 160 | offset = filp->f_pos; | ||
| 161 | if (!offset) { | ||
| 162 | offset = i->i_ino & ROMFH_MASK; | ||
| 163 | ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE); | ||
| 164 | if (ret < 0) | ||
| 165 | goto out; | ||
| 166 | offset = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
| 167 | } | ||
| 168 | |||
| 169 | /* Not really failsafe, but we are read-only... */ | ||
| 170 | for (;;) { | ||
| 171 | if (!offset || offset >= maxoff) { | ||
| 172 | offset = maxoff; | ||
| 173 | filp->f_pos = offset; | ||
| 174 | goto out; | ||
| 175 | } | ||
| 176 | filp->f_pos = offset; | ||
| 177 | |||
| 178 | /* Fetch inode info */ | ||
| 179 | ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE); | ||
| 180 | if (ret < 0) | ||
| 181 | goto out; | ||
| 182 | |||
| 183 | j = romfs_dev_strnlen(i->i_sb, offset + ROMFH_SIZE, | ||
| 184 | sizeof(fsname) - 1); | ||
| 185 | if (j < 0) | ||
| 186 | goto out; | ||
| 187 | |||
| 188 | ret = romfs_dev_read(i->i_sb, offset + ROMFH_SIZE, fsname, j); | ||
| 189 | if (ret < 0) | ||
| 190 | goto out; | ||
| 191 | fsname[j] = '\0'; | ||
| 192 | |||
| 193 | ino = offset; | ||
| 194 | nextfh = be32_to_cpu(ri.next); | ||
| 195 | if ((nextfh & ROMFH_TYPE) == ROMFH_HRD) | ||
| 196 | ino = be32_to_cpu(ri.spec); | ||
| 197 | if (filldir(dirent, fsname, j, offset, ino, | ||
| 198 | romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0) | ||
| 199 | goto out; | ||
| 200 | |||
| 201 | stored++; | ||
| 202 | offset = nextfh & ROMFH_MASK; | ||
| 203 | } | ||
| 204 | |||
| 205 | out: | ||
| 206 | return stored; | ||
| 207 | } | ||
| 208 | |||
| 209 | /* | ||
| 210 | * look up an entry in a directory | ||
| 211 | */ | ||
| 212 | static struct dentry *romfs_lookup(struct inode *dir, struct dentry *dentry, | ||
| 213 | struct nameidata *nd) | ||
| 214 | { | ||
| 215 | unsigned long offset, maxoff; | ||
| 216 | struct inode *inode; | ||
| 217 | struct romfs_inode ri; | ||
| 218 | const char *name; /* got from dentry */ | ||
| 219 | int len, ret; | ||
| 220 | |||
| 221 | offset = dir->i_ino & ROMFH_MASK; | ||
| 222 | ret = romfs_dev_read(dir->i_sb, offset, &ri, ROMFH_SIZE); | ||
| 223 | if (ret < 0) | ||
| 224 | goto error; | ||
| 225 | |||
| 226 | /* search all the file entries in the list starting from the one | ||
| 227 | * pointed to by the directory's special data */ | ||
| 228 | maxoff = romfs_maxsize(dir->i_sb); | ||
| 229 | offset = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
| 230 | |||
| 231 | name = dentry->d_name.name; | ||
| 232 | len = dentry->d_name.len; | ||
| 233 | |||
| 234 | for (;;) { | ||
| 235 | if (!offset || offset >= maxoff) | ||
| 236 | goto out0; | ||
| 237 | |||
| 238 | ret = romfs_dev_read(dir->i_sb, offset, &ri, sizeof(ri)); | ||
| 239 | if (ret < 0) | ||
| 240 | goto error; | ||
| 241 | |||
| 242 | /* try to match the first 16 bytes of name */ | ||
| 243 | ret = romfs_dev_strncmp(dir->i_sb, offset + ROMFH_SIZE, name, | ||
| 244 | len); | ||
| 245 | if (ret < 0) | ||
| 246 | goto error; | ||
| 247 | if (ret == 1) | ||
| 248 | break; | ||
| 249 | |||
| 250 | /* next entry */ | ||
| 251 | offset = be32_to_cpu(ri.next) & ROMFH_MASK; | ||
| 252 | } | ||
| 253 | |||
| 254 | /* Hard link handling */ | ||
| 255 | if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD) | ||
| 256 | offset = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
| 257 | |||
| 258 | inode = romfs_iget(dir->i_sb, offset); | ||
| 259 | if (IS_ERR(inode)) { | ||
| 260 | ret = PTR_ERR(inode); | ||
| 261 | goto error; | ||
| 262 | } | ||
| 263 | goto outi; | ||
| 264 | |||
| 265 | /* | ||
| 266 | * it's a bit funky, _lookup needs to return an error code | ||
| 267 | * (negative) or a NULL, both as a dentry. ENOENT should not | ||
| 268 | * be returned, instead we need to create a negative dentry by | ||
| 269 | * d_add(dentry, NULL); and return 0 as no error. | ||
| 270 | * (Although as I see, it only matters on writable file | ||
| 271 | * systems). | ||
| 272 | */ | ||
| 273 | out0: | ||
| 274 | inode = NULL; | ||
| 275 | outi: | ||
| 276 | d_add(dentry, inode); | ||
| 277 | ret = 0; | ||
| 278 | error: | ||
| 279 | return ERR_PTR(ret); | ||
| 280 | } | ||
| 281 | |||
| 282 | static const struct file_operations romfs_dir_operations = { | ||
| 283 | .read = generic_read_dir, | ||
| 284 | .readdir = romfs_readdir, | ||
| 285 | }; | ||
| 286 | |||
| 287 | static struct inode_operations romfs_dir_inode_operations = { | ||
| 288 | .lookup = romfs_lookup, | ||
| 289 | }; | ||
| 290 | |||
| 291 | /* | ||
| 292 | * get a romfs inode based on its position in the image (which doubles as the | ||
| 293 | * inode number) | ||
| 294 | */ | ||
| 295 | static struct inode *romfs_iget(struct super_block *sb, unsigned long pos) | ||
| 296 | { | ||
| 297 | struct romfs_inode_info *inode; | ||
| 298 | struct romfs_inode ri; | ||
| 299 | struct inode *i; | ||
| 300 | unsigned long nlen; | ||
| 301 | unsigned nextfh, ret; | ||
| 302 | umode_t mode; | ||
| 303 | |||
| 304 | /* we might have to traverse a chain of "hard link" file entries to get | ||
| 305 | * to the actual file */ | ||
| 306 | for (;;) { | ||
| 307 | ret = romfs_dev_read(sb, pos, &ri, sizeof(ri)); | ||
| 308 | if (ret < 0) | ||
| 309 | goto error; | ||
| 310 | |||
| 311 | /* XXX: do romfs_checksum here too (with name) */ | ||
| 312 | |||
| 313 | nextfh = be32_to_cpu(ri.next); | ||
| 314 | if ((nextfh & ROMFH_TYPE) != ROMFH_HRD) | ||
| 315 | break; | ||
| 316 | |||
| 317 | pos = be32_to_cpu(ri.spec) & ROMFH_MASK; | ||
| 318 | } | ||
| 319 | |||
| 320 | /* determine the length of the filename */ | ||
| 321 | nlen = romfs_dev_strnlen(sb, pos + ROMFH_SIZE, ROMFS_MAXFN); | ||
| 322 | if (IS_ERR_VALUE(nlen)) | ||
| 323 | goto eio; | ||
| 324 | |||
| 325 | /* get an inode for this image position */ | ||
| 326 | i = iget_locked(sb, pos); | ||
| 327 | if (!i) | ||
| 328 | return ERR_PTR(-ENOMEM); | ||
| 329 | |||
| 330 | if (!(i->i_state & I_NEW)) | ||
| 331 | return i; | ||
| 332 | |||
| 333 | /* precalculate the data offset */ | ||
| 334 | inode = ROMFS_I(i); | ||
| 335 | inode->i_metasize = (ROMFH_SIZE + nlen + 1 + ROMFH_PAD) & ROMFH_MASK; | ||
| 336 | inode->i_dataoffset = pos + inode->i_metasize; | ||
| 337 | |||
| 338 | i->i_nlink = 1; /* Hard to decide.. */ | ||
| 339 | i->i_size = be32_to_cpu(ri.size); | ||
| 340 | i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0; | ||
| 341 | i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0; | ||
| 342 | |||
| 343 | /* set up mode and ops */ | ||
| 344 | mode = romfs_modemap[nextfh & ROMFH_TYPE]; | ||
| 345 | |||
| 346 | switch (nextfh & ROMFH_TYPE) { | ||
| 347 | case ROMFH_DIR: | ||
| 348 | i->i_size = ROMFS_I(i)->i_metasize; | ||
| 349 | i->i_op = &romfs_dir_inode_operations; | ||
| 350 | i->i_fop = &romfs_dir_operations; | ||
| 351 | if (nextfh & ROMFH_EXEC) | ||
| 352 | mode |= S_IXUGO; | ||
| 353 | break; | ||
| 354 | case ROMFH_REG: | ||
| 355 | i->i_fop = &romfs_ro_fops; | ||
| 356 | i->i_data.a_ops = &romfs_aops; | ||
| 357 | if (i->i_sb->s_mtd) | ||
| 358 | i->i_data.backing_dev_info = | ||
| 359 | i->i_sb->s_mtd->backing_dev_info; | ||
| 360 | if (nextfh & ROMFH_EXEC) | ||
| 361 | mode |= S_IXUGO; | ||
| 362 | break; | ||
| 363 | case ROMFH_SYM: | ||
| 364 | i->i_op = &page_symlink_inode_operations; | ||
| 365 | i->i_data.a_ops = &romfs_aops; | ||
| 366 | mode |= S_IRWXUGO; | ||
| 367 | break; | ||
| 368 | default: | ||
| 369 | /* depending on MBZ for sock/fifos */ | ||
| 370 | nextfh = be32_to_cpu(ri.spec); | ||
| 371 | init_special_inode(i, mode, MKDEV(nextfh >> 16, | ||
| 372 | nextfh & 0xffff)); | ||
| 373 | break; | ||
| 374 | } | ||
| 375 | |||
| 376 | i->i_mode = mode; | ||
| 377 | |||
| 378 | unlock_new_inode(i); | ||
| 379 | return i; | ||
| 380 | |||
| 381 | eio: | ||
| 382 | ret = -EIO; | ||
| 383 | error: | ||
| 384 | printk(KERN_ERR "ROMFS: read error for inode 0x%lx\n", pos); | ||
| 385 | return ERR_PTR(ret); | ||
| 386 | } | ||
| 387 | |||
| 388 | /* | ||
| 389 | * allocate a new inode | ||
| 390 | */ | ||
| 391 | static struct inode *romfs_alloc_inode(struct super_block *sb) | ||
| 392 | { | ||
| 393 | struct romfs_inode_info *inode; | ||
| 394 | inode = kmem_cache_alloc(romfs_inode_cachep, GFP_KERNEL); | ||
| 395 | return inode ? &inode->vfs_inode : NULL; | ||
| 396 | } | ||
| 397 | |||
| 398 | /* | ||
| 399 | * return a spent inode to the slab cache | ||
| 400 | */ | ||
| 401 | static void romfs_destroy_inode(struct inode *inode) | ||
| 402 | { | ||
| 403 | kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode)); | ||
| 404 | } | ||
| 405 | |||
| 406 | /* | ||
| 407 | * get filesystem statistics | ||
| 408 | */ | ||
| 409 | static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf) | ||
| 410 | { | ||
| 411 | buf->f_type = ROMFS_MAGIC; | ||
| 412 | buf->f_namelen = ROMFS_MAXFN; | ||
| 413 | buf->f_bsize = ROMBSIZE; | ||
| 414 | buf->f_bfree = buf->f_bavail = buf->f_ffree; | ||
| 415 | buf->f_blocks = | ||
| 416 | (romfs_maxsize(dentry->d_sb) + ROMBSIZE - 1) >> ROMBSBITS; | ||
| 417 | return 0; | ||
| 418 | } | ||
| 419 | |||
| 420 | /* | ||
| 421 | * remounting must involve read-only | ||
| 422 | */ | ||
| 423 | static int romfs_remount(struct super_block *sb, int *flags, char *data) | ||
| 424 | { | ||
| 425 | *flags |= MS_RDONLY; | ||
| 426 | return 0; | ||
| 427 | } | ||
| 428 | |||
| 429 | static const struct super_operations romfs_super_ops = { | ||
| 430 | .alloc_inode = romfs_alloc_inode, | ||
| 431 | .destroy_inode = romfs_destroy_inode, | ||
| 432 | .statfs = romfs_statfs, | ||
| 433 | .remount_fs = romfs_remount, | ||
| 434 | }; | ||
| 435 | |||
| 436 | /* | ||
| 437 | * checksum check on part of a romfs filesystem | ||
| 438 | */ | ||
| 439 | static __u32 romfs_checksum(const void *data, int size) | ||
| 440 | { | ||
| 441 | const __be32 *ptr = data; | ||
| 442 | __u32 sum; | ||
| 443 | |||
| 444 | sum = 0; | ||
| 445 | size >>= 2; | ||
| 446 | while (size > 0) { | ||
| 447 | sum += be32_to_cpu(*ptr++); | ||
| 448 | size--; | ||
| 449 | } | ||
| 450 | return sum; | ||
| 451 | } | ||
| 452 | |||
| 453 | /* | ||
| 454 | * fill in the superblock | ||
| 455 | */ | ||
| 456 | static int romfs_fill_super(struct super_block *sb, void *data, int silent) | ||
| 457 | { | ||
| 458 | struct romfs_super_block *rsb; | ||
| 459 | struct inode *root; | ||
| 460 | unsigned long pos, img_size; | ||
| 461 | const char *storage; | ||
| 462 | size_t len; | ||
| 463 | int ret; | ||
| 464 | |||
| 465 | #ifdef CONFIG_BLOCK | ||
| 466 | if (!sb->s_mtd) { | ||
| 467 | sb_set_blocksize(sb, ROMBSIZE); | ||
| 468 | } else { | ||
| 469 | sb->s_blocksize = ROMBSIZE; | ||
| 470 | sb->s_blocksize_bits = blksize_bits(ROMBSIZE); | ||
| 471 | } | ||
| 472 | #endif | ||
| 473 | |||
| 474 | sb->s_maxbytes = 0xFFFFFFFF; | ||
| 475 | sb->s_magic = ROMFS_MAGIC; | ||
| 476 | sb->s_flags |= MS_RDONLY | MS_NOATIME; | ||
| 477 | sb->s_op = &romfs_super_ops; | ||
| 478 | |||
| 479 | /* read the image superblock and check it */ | ||
| 480 | rsb = kmalloc(512, GFP_KERNEL); | ||
| 481 | if (!rsb) | ||
| 482 | return -ENOMEM; | ||
| 483 | |||
| 484 | sb->s_fs_info = (void *) 512; | ||
| 485 | ret = romfs_dev_read(sb, 0, rsb, 512); | ||
| 486 | if (ret < 0) | ||
| 487 | goto error_rsb; | ||
| 488 | |||
| 489 | img_size = be32_to_cpu(rsb->size); | ||
| 490 | |||
| 491 | if (sb->s_mtd && img_size > sb->s_mtd->size) | ||
| 492 | goto error_rsb_inval; | ||
| 493 | |||
| 494 | sb->s_fs_info = (void *) img_size; | ||
| 495 | |||
| 496 | if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 || | ||
| 497 | img_size < ROMFH_SIZE) { | ||
| 498 | if (!silent) | ||
| 499 | printk(KERN_WARNING "VFS:" | ||
| 500 | " Can't find a romfs filesystem on dev %s.\n", | ||
| 501 | sb->s_id); | ||
| 502 | goto error_rsb_inval; | ||
| 503 | } | ||
| 504 | |||
| 505 | if (romfs_checksum(rsb, min_t(size_t, img_size, 512))) { | ||
| 506 | printk(KERN_ERR "ROMFS: bad initial checksum on dev %s.\n", | ||
| 507 | sb->s_id); | ||
| 508 | goto error_rsb_inval; | ||
| 509 | } | ||
| 510 | |||
| 511 | storage = sb->s_mtd ? "MTD" : "the block layer"; | ||
| 512 | |||
| 513 | len = strnlen(rsb->name, ROMFS_MAXFN); | ||
| 514 | if (!silent) | ||
| 515 | printk(KERN_NOTICE "ROMFS: Mounting image '%*.*s' through %s\n", | ||
| 516 | (unsigned) len, (unsigned) len, rsb->name, storage); | ||
| 517 | |||
| 518 | kfree(rsb); | ||
| 519 | rsb = NULL; | ||
| 520 | |||
| 521 | /* find the root directory */ | ||
| 522 | pos = (ROMFH_SIZE + len + 1 + ROMFH_PAD) & ROMFH_MASK; | ||
| 523 | |||
| 524 | root = romfs_iget(sb, pos); | ||
| 525 | if (!root) | ||
| 526 | goto error; | ||
| 527 | |||
| 528 | sb->s_root = d_alloc_root(root); | ||
| 529 | if (!sb->s_root) | ||
| 530 | goto error_i; | ||
| 531 | |||
| 532 | return 0; | ||
| 533 | |||
| 534 | error_i: | ||
| 535 | iput(root); | ||
| 536 | error: | ||
| 537 | return -EINVAL; | ||
| 538 | error_rsb_inval: | ||
| 539 | ret = -EINVAL; | ||
| 540 | error_rsb: | ||
| 541 | return ret; | ||
| 542 | } | ||
| 543 | |||
| 544 | /* | ||
| 545 | * get a superblock for mounting | ||
| 546 | */ | ||
| 547 | static int romfs_get_sb(struct file_system_type *fs_type, | ||
| 548 | int flags, const char *dev_name, | ||
| 549 | void *data, struct vfsmount *mnt) | ||
| 550 | { | ||
| 551 | int ret = -EINVAL; | ||
| 552 | |||
| 553 | #ifdef CONFIG_ROMFS_ON_MTD | ||
| 554 | ret = get_sb_mtd(fs_type, flags, dev_name, data, romfs_fill_super, | ||
| 555 | mnt); | ||
| 556 | #endif | ||
| 557 | #ifdef CONFIG_ROMFS_ON_BLOCK | ||
| 558 | if (ret == -EINVAL) | ||
| 559 | ret = get_sb_bdev(fs_type, flags, dev_name, data, | ||
| 560 | romfs_fill_super, mnt); | ||
| 561 | #endif | ||
| 562 | return ret; | ||
| 563 | } | ||
| 564 | |||
| 565 | /* | ||
| 566 | * destroy a romfs superblock in the appropriate manner | ||
| 567 | */ | ||
| 568 | static void romfs_kill_sb(struct super_block *sb) | ||
| 569 | { | ||
| 570 | #ifdef CONFIG_ROMFS_ON_MTD | ||
| 571 | if (sb->s_mtd) { | ||
| 572 | kill_mtd_super(sb); | ||
| 573 | return; | ||
| 574 | } | ||
| 575 | #endif | ||
| 576 | #ifdef CONFIG_ROMFS_ON_BLOCK | ||
| 577 | if (sb->s_bdev) { | ||
| 578 | kill_block_super(sb); | ||
| 579 | return; | ||
| 580 | } | ||
| 581 | #endif | ||
| 582 | } | ||
| 583 | |||
| 584 | static struct file_system_type romfs_fs_type = { | ||
| 585 | .owner = THIS_MODULE, | ||
| 586 | .name = "romfs", | ||
| 587 | .get_sb = romfs_get_sb, | ||
| 588 | .kill_sb = romfs_kill_sb, | ||
| 589 | .fs_flags = FS_REQUIRES_DEV, | ||
| 590 | }; | ||
| 591 | |||
| 592 | /* | ||
| 593 | * inode storage initialiser | ||
| 594 | */ | ||
| 595 | static void romfs_i_init_once(void *_inode) | ||
| 596 | { | ||
| 597 | struct romfs_inode_info *inode = _inode; | ||
| 598 | |||
| 599 | inode_init_once(&inode->vfs_inode); | ||
| 600 | } | ||
| 601 | |||
| 602 | /* | ||
| 603 | * romfs module initialisation | ||
| 604 | */ | ||
| 605 | static int __init init_romfs_fs(void) | ||
| 606 | { | ||
| 607 | int ret; | ||
| 608 | |||
| 609 | printk(KERN_INFO "ROMFS MTD (C) 2007 Red Hat, Inc.\n"); | ||
| 610 | |||
| 611 | romfs_inode_cachep = | ||
| 612 | kmem_cache_create("romfs_i", | ||
| 613 | sizeof(struct romfs_inode_info), 0, | ||
| 614 | SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, | ||
| 615 | romfs_i_init_once); | ||
| 616 | |||
| 617 | if (!romfs_inode_cachep) { | ||
| 618 | printk(KERN_ERR | ||
| 619 | "ROMFS error: Failed to initialise inode cache\n"); | ||
| 620 | return -ENOMEM; | ||
| 621 | } | ||
| 622 | ret = register_filesystem(&romfs_fs_type); | ||
| 623 | if (ret) { | ||
| 624 | printk(KERN_ERR "ROMFS error: Failed to register filesystem\n"); | ||
| 625 | goto error_register; | ||
| 626 | } | ||
| 627 | return 0; | ||
| 628 | |||
| 629 | error_register: | ||
| 630 | kmem_cache_destroy(romfs_inode_cachep); | ||
| 631 | return ret; | ||
| 632 | } | ||
| 633 | |||
| 634 | /* | ||
| 635 | * romfs module removal | ||
| 636 | */ | ||
| 637 | static void __exit exit_romfs_fs(void) | ||
| 638 | { | ||
| 639 | unregister_filesystem(&romfs_fs_type); | ||
| 640 | kmem_cache_destroy(romfs_inode_cachep); | ||
| 641 | } | ||
| 642 | |||
| 643 | module_init(init_romfs_fs); | ||
| 644 | module_exit(exit_romfs_fs); | ||
| 645 | |||
| 646 | MODULE_DESCRIPTION("Direct-MTD Capable RomFS"); | ||
| 647 | MODULE_AUTHOR("Red Hat, Inc."); | ||
| 648 | MODULE_LICENSE("GPL"); /* Actually dual-licensed, but it doesn't matter for */ | ||
