/*
|
/*
|
* ROMFS file system, Linux implementation
|
* ROMFS file system, Linux implementation
|
*
|
*
|
* Copyright (C) 1997 Janos Farkas <chexum@shadow.banki.hu>
|
* Copyright (C) 1997 Janos Farkas <chexum@shadow.banki.hu>
|
*
|
*
|
* Using parts of the minix filesystem
|
* Using parts of the minix filesystem
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
*
|
*
|
* and parts of the affs filesystem additionally
|
* and parts of the affs filesystem additionally
|
* Copyright (C) 1993 Ray Burr
|
* Copyright (C) 1993 Ray Burr
|
* Copyright (C) 1996 Hans-Joachim Widmaier
|
* Copyright (C) 1996 Hans-Joachim Widmaier
|
*
|
*
|
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
* as published by the Free Software Foundation; either version
|
* as published by the Free Software Foundation; either version
|
* 2 of the License, or (at your option) any later version.
|
* 2 of the License, or (at your option) any later version.
|
*
|
*
|
* Changes
|
* Changes
|
* Changed for 2.1.19 modules
|
* Changed for 2.1.19 modules
|
* Jan 1997 Initial release
|
* Jan 1997 Initial release
|
* Nov 1997 2.0.32 Merging applicable 2.1 bugfixes
|
* Nov 1997 2.0.32 Merging applicable 2.1 bugfixes
|
*
|
*
|
*/
|
*/
|
|
|
/* todo:
|
/* todo:
|
* - see Documentation/filesystems/romfs.txt
|
* - see Documentation/filesystems/romfs.txt
|
* - use malloced memory for file names?
|
* - use malloced memory for file names?
|
* - quicklist routines from fs/namei.c, get_page is possibly not
|
* - quicklist routines from fs/namei.c, get_page is possibly not
|
* intended to be used now
|
* intended to be used now
|
* - considering write access...
|
* - considering write access...
|
* - network (tftp) files?
|
* - network (tftp) files?
|
* - in the ancient times something leaked to made umounts
|
* - in the ancient times something leaked to made umounts
|
* impossible, but I've not seen it in the last months
|
* impossible, but I've not seen it in the last months
|
*/
|
*/
|
|
|
/*
|
/*
|
* Sorry about some optimizations and for some goto's. I just wanted
|
* Sorry about some optimizations and for some goto's. I just wanted
|
* to squeeze some more bytes out of this code.. :)
|
* to squeeze some more bytes out of this code.. :)
|
*/
|
*/
|
|
|
#include <linux/module.h>
|
#include <linux/module.h>
|
#include <linux/types.h>
|
#include <linux/types.h>
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/malloc.h>
|
#include <linux/malloc.h>
|
#include <linux/romfs_fs.h>
|
#include <linux/romfs_fs.h>
|
#include <linux/fs.h>
|
#include <linux/fs.h>
|
#include <linux/locks.h>
|
#include <linux/locks.h>
|
|
|
#include <asm/byteorder.h>
|
#include <asm/byteorder.h>
|
|
|
static int inline min(int a, int b)
|
static int inline min(int a, int b)
|
{
|
{
|
return a<b ? a : b;
|
return a<b ? a : b;
|
}
|
}
|
|
|
static __s32
|
static __s32
|
romfs_checksum(void *data, int size)
|
romfs_checksum(void *data, int size)
|
{
|
{
|
__s32 sum, *ptr;
|
__s32 sum, *ptr;
|
|
|
sum = 0; ptr = data;
|
sum = 0; ptr = data;
|
size>>=2;
|
size>>=2;
|
while (size>0) {
|
while (size>0) {
|
sum += ntohl(*ptr++);
|
sum += ntohl(*ptr++);
|
size--;
|
size--;
|
}
|
}
|
return sum;
|
return sum;
|
}
|
}
|
|
|
static struct super_operations romfs_ops;
|
static struct super_operations romfs_ops;
|
|
|
static struct super_block *
|
static struct super_block *
|
romfs_read_super(struct super_block *s, void *data, int silent)
|
romfs_read_super(struct super_block *s, void *data, int silent)
|
{
|
{
|
struct buffer_head *bh;
|
struct buffer_head *bh;
|
kdev_t dev = s->s_dev;
|
kdev_t dev = s->s_dev;
|
struct romfs_super_block *rsb;
|
struct romfs_super_block *rsb;
|
int sz;
|
int sz;
|
|
|
MOD_INC_USE_COUNT;
|
MOD_INC_USE_COUNT;
|
|
|
/* I would parse the options here, but there are none.. :) */
|
/* I would parse the options here, but there are none.. :) */
|
|
|
lock_super(s);
|
lock_super(s);
|
set_blocksize(dev, ROMBSIZE);
|
set_blocksize(dev, ROMBSIZE);
|
s->s_blocksize = ROMBSIZE;
|
s->s_blocksize = ROMBSIZE;
|
s->s_blocksize_bits = ROMBSBITS;
|
s->s_blocksize_bits = ROMBSBITS;
|
bh = bread(dev, 0, ROMBSIZE);
|
bh = bread(dev, 0, ROMBSIZE);
|
if (!bh) {
|
if (!bh) {
|
/* XXX merge with other printk? */
|
/* XXX merge with other printk? */
|
printk ("romfs: unable to read superblock\n");
|
printk ("romfs: unable to read superblock\n");
|
goto outnobh;
|
goto outnobh;
|
}
|
}
|
|
|
rsb = (struct romfs_super_block *)bh->b_data;
|
rsb = (struct romfs_super_block *)bh->b_data;
|
sz = ntohl(rsb->size);
|
sz = ntohl(rsb->size);
|
if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1
|
if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1
|
|| sz < ROMFH_SIZE) {
|
|| sz < ROMFH_SIZE) {
|
if (!silent)
|
if (!silent)
|
printk ("VFS: Can't find a romfs filesystem on dev "
|
printk ("VFS: Can't find a romfs filesystem on dev "
|
"%s.\n", kdevname(dev));
|
"%s.\n", kdevname(dev));
|
goto out;
|
goto out;
|
}
|
}
|
if (romfs_checksum(rsb, min(sz,512))) {
|
if (romfs_checksum(rsb, min(sz,512))) {
|
printk ("romfs: bad initial checksum on dev "
|
printk ("romfs: bad initial checksum on dev "
|
"%s.\n", kdevname(dev));
|
"%s.\n", kdevname(dev));
|
goto out;
|
goto out;
|
}
|
}
|
|
|
s->s_magic = ROMFS_MAGIC;
|
s->s_magic = ROMFS_MAGIC;
|
s->u.romfs_sb.s_maxsize = sz;
|
s->u.romfs_sb.s_maxsize = sz;
|
|
|
s->s_flags |= MS_RDONLY;
|
s->s_flags |= MS_RDONLY;
|
|
|
/* Find the start of the fs */
|
/* Find the start of the fs */
|
sz = (ROMFH_SIZE +
|
sz = (ROMFH_SIZE +
|
strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD)
|
strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD)
|
& ROMFH_MASK;
|
& ROMFH_MASK;
|
|
|
brelse(bh);
|
brelse(bh);
|
|
|
s->s_op = &romfs_ops;
|
s->s_op = &romfs_ops;
|
|
|
if (!(s->s_mounted = iget(s, sz)))
|
if (!(s->s_mounted = iget(s, sz)))
|
goto outnobh;
|
goto outnobh;
|
|
|
unlock_super(s);
|
unlock_super(s);
|
|
|
/* Ehrhm; sorry.. :) And thanks to Hans-Joachim Widmaier :) */
|
/* Ehrhm; sorry.. :) And thanks to Hans-Joachim Widmaier :) */
|
if (0) {
|
if (0) {
|
out:
|
out:
|
brelse(bh);
|
brelse(bh);
|
outnobh:
|
outnobh:
|
s->s_dev = 0;
|
s->s_dev = 0;
|
unlock_super(s);
|
unlock_super(s);
|
MOD_DEC_USE_COUNT;
|
MOD_DEC_USE_COUNT;
|
s = NULL;
|
s = NULL;
|
}
|
}
|
|
|
return s;
|
return s;
|
}
|
}
|
|
|
/* Nothing to do.. */
|
/* Nothing to do.. */
|
|
|
static void
|
static void
|
romfs_put_super(struct super_block *sb)
|
romfs_put_super(struct super_block *sb)
|
{
|
{
|
lock_super(sb);
|
lock_super(sb);
|
sb->s_dev = 0;
|
sb->s_dev = 0;
|
unlock_super(sb);
|
unlock_super(sb);
|
MOD_DEC_USE_COUNT;
|
MOD_DEC_USE_COUNT;
|
return;
|
return;
|
}
|
}
|
|
|
/* That's simple too. */
|
/* That's simple too. */
|
|
|
static void
|
static void
|
romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
|
romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
|
{
|
{
|
struct statfs tmp;
|
struct statfs tmp;
|
|
|
memset(&tmp, 0, sizeof(tmp));
|
memset(&tmp, 0, sizeof(tmp));
|
tmp.f_type = ROMFS_MAGIC;
|
tmp.f_type = ROMFS_MAGIC;
|
tmp.f_bsize = ROMBSIZE;
|
tmp.f_bsize = ROMBSIZE;
|
tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
|
tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
|
memcpy_tofs(buf, &tmp, bufsize);
|
memcpy_tofs(buf, &tmp, bufsize);
|
}
|
}
|
|
|
/* some helper routines */
|
/* some helper routines */
|
|
|
static int
|
static int
|
romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
|
romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
|
{
|
{
|
struct buffer_head *bh;
|
struct buffer_head *bh;
|
unsigned long avail, maxsize, res;
|
unsigned long avail, maxsize, res;
|
|
|
maxsize = i->i_sb->u.romfs_sb.s_maxsize;
|
maxsize = i->i_sb->u.romfs_sb.s_maxsize;
|
if (offset >= maxsize)
|
if (offset >= maxsize)
|
return -1;
|
return -1;
|
|
|
/* strnlen is almost always valid */
|
/* strnlen is almost always valid */
|
if (count > maxsize || offset+count > maxsize)
|
if (count > maxsize || offset+count > maxsize)
|
count = maxsize-offset;
|
count = maxsize-offset;
|
|
|
bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
|
bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
|
if (!bh)
|
if (!bh)
|
return -1; /* error */
|
return -1; /* error */
|
|
|
avail = ROMBSIZE - (offset & ROMBMASK);
|
avail = ROMBSIZE - (offset & ROMBMASK);
|
maxsize = min(count, avail);
|
maxsize = min(count, avail);
|
res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize);
|
res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize);
|
brelse(bh);
|
brelse(bh);
|
|
|
if (res < maxsize)
|
if (res < maxsize)
|
return res; /* found all of it */
|
return res; /* found all of it */
|
|
|
while (res < count) {
|
while (res < count) {
|
offset += maxsize;
|
offset += maxsize;
|
|
|
bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
|
bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
|
if (!bh)
|
if (!bh)
|
return -1;
|
return -1;
|
maxsize = min(count-res, ROMBSIZE);
|
maxsize = min(count-res, ROMBSIZE);
|
avail = strnlen(bh->b_data, maxsize);
|
avail = strnlen(bh->b_data, maxsize);
|
res += avail;
|
res += avail;
|
brelse(bh);
|
brelse(bh);
|
if (avail < maxsize)
|
if (avail < maxsize)
|
return res;
|
return res;
|
}
|
}
|
return res;
|
return res;
|
}
|
}
|
|
|
static int
|
static int
|
romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count)
|
romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count)
|
{
|
{
|
struct buffer_head *bh;
|
struct buffer_head *bh;
|
unsigned long avail, maxsize, res;
|
unsigned long avail, maxsize, res;
|
|
|
maxsize = i->i_sb->u.romfs_sb.s_maxsize;
|
maxsize = i->i_sb->u.romfs_sb.s_maxsize;
|
if (offset >= maxsize || count > maxsize || offset+count>maxsize)
|
if (offset >= maxsize || count > maxsize || offset+count>maxsize)
|
return -1;
|
return -1;
|
|
|
bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
|
bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
|
if (!bh)
|
if (!bh)
|
return -1; /* error */
|
return -1; /* error */
|
|
|
avail = ROMBSIZE - (offset & ROMBMASK);
|
avail = ROMBSIZE - (offset & ROMBMASK);
|
maxsize = min(count, avail);
|
maxsize = min(count, avail);
|
memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize);
|
memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize);
|
brelse(bh);
|
brelse(bh);
|
|
|
res = maxsize; /* all of it */
|
res = maxsize; /* all of it */
|
|
|
while (res < count) {
|
while (res < count) {
|
offset += maxsize;
|
offset += maxsize;
|
dest += maxsize;
|
dest += maxsize;
|
|
|
bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
|
bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
|
if (!bh)
|
if (!bh)
|
return -1;
|
return -1;
|
maxsize = min(count-res, ROMBSIZE);
|
maxsize = min(count-res, ROMBSIZE);
|
memcpy(dest, bh->b_data, maxsize);
|
memcpy(dest, bh->b_data, maxsize);
|
brelse(bh);
|
brelse(bh);
|
res += maxsize;
|
res += maxsize;
|
}
|
}
|
return res;
|
return res;
|
}
|
}
|
|
|
/* Directory operations */
|
/* Directory operations */
|
|
|
static int
|
static int
|
romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldir)
|
romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldir)
|
{
|
{
|
struct romfs_inode ri;
|
struct romfs_inode ri;
|
unsigned long offset, maxoff;
|
unsigned long offset, maxoff;
|
int j, ino, nextfh;
|
int j, ino, nextfh;
|
int stored = 0;
|
int stored = 0;
|
char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
|
char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
|
|
|
if (!i || !S_ISDIR(i->i_mode))
|
if (!i || !S_ISDIR(i->i_mode))
|
return -EBADF;
|
return -EBADF;
|
|
|
maxoff = i->i_sb->u.romfs_sb.s_maxsize;
|
maxoff = i->i_sb->u.romfs_sb.s_maxsize;
|
|
|
offset = filp->f_pos;
|
offset = filp->f_pos;
|
if (!offset) {
|
if (!offset) {
|
offset = i->i_ino & ROMFH_MASK;
|
offset = i->i_ino & ROMFH_MASK;
|
if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
|
if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
|
return stored;
|
return stored;
|
offset = ntohl(ri.spec) & ROMFH_MASK;
|
offset = ntohl(ri.spec) & ROMFH_MASK;
|
}
|
}
|
|
|
/* Not really failsafe, but we are read-only... */
|
/* Not really failsafe, but we are read-only... */
|
for(;;) {
|
for(;;) {
|
if (!offset || offset >= maxoff) {
|
if (!offset || offset >= maxoff) {
|
offset = 0xffffffff;
|
offset = 0xffffffff;
|
filp->f_pos = offset;
|
filp->f_pos = offset;
|
return stored;
|
return stored;
|
}
|
}
|
filp->f_pos = offset;
|
filp->f_pos = offset;
|
|
|
/* Fetch inode info */
|
/* Fetch inode info */
|
if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
|
if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
|
return stored;
|
return stored;
|
|
|
j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1);
|
j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1);
|
if (j < 0)
|
if (j < 0)
|
return stored;
|
return stored;
|
|
|
fsname[j]=0;
|
fsname[j]=0;
|
romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j);
|
romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j);
|
|
|
ino = offset;
|
ino = offset;
|
nextfh = ntohl(ri.next);
|
nextfh = ntohl(ri.next);
|
if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
|
if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
|
ino = ntohl(ri.spec);
|
ino = ntohl(ri.spec);
|
if (filldir(dirent, fsname, j, offset, ino) < 0) {
|
if (filldir(dirent, fsname, j, offset, ino) < 0) {
|
return stored;
|
return stored;
|
}
|
}
|
stored++;
|
stored++;
|
offset = nextfh & ROMFH_MASK;
|
offset = nextfh & ROMFH_MASK;
|
}
|
}
|
}
|
}
|
|
|
static int
|
static int
|
romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
|
romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
|
{
|
{
|
unsigned long offset, maxoff;
|
unsigned long offset, maxoff;
|
int fslen, res;
|
int fslen, res;
|
char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
|
char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
|
struct romfs_inode ri;
|
struct romfs_inode ri;
|
|
|
*result = NULL;
|
*result = NULL;
|
if (!dir || !S_ISDIR(dir->i_mode)) {
|
if (!dir || !S_ISDIR(dir->i_mode)) {
|
res = -EBADF;
|
res = -EBADF;
|
goto out;
|
goto out;
|
}
|
}
|
|
|
offset = dir->i_ino & ROMFH_MASK;
|
offset = dir->i_ino & ROMFH_MASK;
|
if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
|
if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
|
res = -ENOENT;
|
res = -ENOENT;
|
goto out;
|
goto out;
|
}
|
}
|
|
|
maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
|
maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
|
offset = ntohl(ri.spec) & ROMFH_MASK;
|
offset = ntohl(ri.spec) & ROMFH_MASK;
|
|
|
for(;;) {
|
for(;;) {
|
if (!offset || offset >= maxoff
|
if (!offset || offset >= maxoff
|
|| romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
|
|| romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
|
res = -ENOENT;
|
res = -ENOENT;
|
goto out;
|
goto out;
|
}
|
}
|
|
|
/* try to match the first 16 bytes of name */
|
/* try to match the first 16 bytes of name */
|
fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE);
|
fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE);
|
if (len < ROMFH_SIZE) {
|
if (len < ROMFH_SIZE) {
|
if (len == fslen) {
|
if (len == fslen) {
|
/* both are shorter, and same size */
|
/* both are shorter, and same size */
|
romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
|
romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
|
if (strncmp (name, fsname, len) == 0)
|
if (strncmp (name, fsname, len) == 0)
|
break;
|
break;
|
}
|
}
|
} else if (fslen >= ROMFH_SIZE) {
|
} else if (fslen >= ROMFH_SIZE) {
|
/* both are longer; XXX optimize max size */
|
/* both are longer; XXX optimize max size */
|
fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1);
|
fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1);
|
if (len == fslen) {
|
if (len == fslen) {
|
romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
|
romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
|
if (strncmp(name, fsname, len) == 0)
|
if (strncmp(name, fsname, len) == 0)
|
break;
|
break;
|
}
|
}
|
}
|
}
|
/* next entry */
|
/* next entry */
|
offset = ntohl(ri.next) & ROMFH_MASK;
|
offset = ntohl(ri.next) & ROMFH_MASK;
|
}
|
}
|
|
|
/* Hard link handling */
|
/* Hard link handling */
|
if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
|
if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
|
offset = ntohl(ri.spec) & ROMFH_MASK;
|
offset = ntohl(ri.spec) & ROMFH_MASK;
|
|
|
res = 0;
|
res = 0;
|
if (!(*result = iget(dir->i_sb, offset)))
|
if (!(*result = iget(dir->i_sb, offset)))
|
res = -EACCES;
|
res = -EACCES;
|
|
|
out:
|
out:
|
iput(dir);
|
iput(dir);
|
return res;
|
return res;
|
}
|
}
|
|
|
/*
|
/*
|
* Ok, we do readpage, to be able to execute programs. Unfortunately,
|
* Ok, we do readpage, to be able to execute programs. Unfortunately,
|
* we can't use bmap, since we have looser alignments.
|
* we can't use bmap, since we have looser alignments.
|
*/
|
*/
|
|
|
static int
|
static int
|
romfs_readpage(struct inode * inode, struct page * page)
|
romfs_readpage(struct inode * inode, struct page * page)
|
{
|
{
|
unsigned long buf;
|
unsigned long buf;
|
unsigned long offset, avail, readlen;
|
unsigned long offset, avail, readlen;
|
int result = -EIO;
|
int result = -EIO;
|
|
|
page->count++;
|
page->count++;
|
set_bit(PG_locked, &page->flags);
|
set_bit(PG_locked, &page->flags);
|
|
|
buf = page_address(page);
|
buf = page_address(page);
|
clear_bit(PG_uptodate, &page->flags);
|
clear_bit(PG_uptodate, &page->flags);
|
clear_bit(PG_error, &page->flags);
|
clear_bit(PG_error, &page->flags);
|
offset = page->offset;
|
offset = page->offset;
|
if (offset < inode->i_size) {
|
if (offset < inode->i_size) {
|
avail = inode->i_size-offset;
|
avail = inode->i_size-offset;
|
readlen = min(avail, PAGE_SIZE);
|
readlen = min(avail, PAGE_SIZE);
|
if (romfs_copyfrom(inode, (void *)buf, inode->u.romfs_i.i_dataoffset+offset, readlen) == readlen) {
|
if (romfs_copyfrom(inode, (void *)buf, inode->u.romfs_i.i_dataoffset+offset, readlen) == readlen) {
|
if (readlen < PAGE_SIZE) {
|
if (readlen < PAGE_SIZE) {
|
memset((void *)(buf+readlen),0,PAGE_SIZE-readlen);
|
memset((void *)(buf+readlen),0,PAGE_SIZE-readlen);
|
}
|
}
|
set_bit(PG_uptodate, &page->flags);
|
set_bit(PG_uptodate, &page->flags);
|
result = 0;
|
result = 0;
|
}
|
}
|
}
|
}
|
if (result) {
|
if (result) {
|
set_bit(PG_error, &page->flags);
|
set_bit(PG_error, &page->flags);
|
memset((void *)buf, 0, PAGE_SIZE);
|
memset((void *)buf, 0, PAGE_SIZE);
|
}
|
}
|
|
|
clear_bit(PG_locked, &page->flags);
|
clear_bit(PG_locked, &page->flags);
|
wake_up(&page->wait);
|
wake_up(&page->wait);
|
free_page(buf);
|
free_page(buf);
|
|
|
return result;
|
return result;
|
}
|
}
|
|
|
#ifdef MAGIC_ROM_PTR
|
#ifdef MAGIC_ROM_PTR
|
static int
|
static int
|
romfs_romptr(struct inode * inode, struct file * filp, struct vm_area_struct * vma)
|
romfs_romptr(struct inode * inode, struct file * filp, struct vm_area_struct * vma)
|
{
|
{
|
vma->vm_offset += inode->u.romfs_i.i_dataoffset;
|
vma->vm_offset += inode->u.romfs_i.i_dataoffset;
|
|
|
if ((vma->vm_flags & VM_WRITE) || bromptr(inode->i_dev, vma))
|
if ((vma->vm_flags & VM_WRITE) || bromptr(inode->i_dev, vma))
|
return -ENOSYS;
|
return -ENOSYS;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
#endif
|
#endif
|
|
|
static int
|
static int
|
romfs_readlink(struct inode *inode, char *buffer, int len)
|
romfs_readlink(struct inode *inode, char *buffer, int len)
|
{
|
{
|
int mylen;
|
int mylen;
|
char buf[ROMFS_MAXFN]; /* XXX dynamic */
|
char buf[ROMFS_MAXFN]; /* XXX dynamic */
|
|
|
if (!inode || !S_ISLNK(inode->i_mode)) {
|
if (!inode || !S_ISLNK(inode->i_mode)) {
|
mylen = -EBADF;
|
mylen = -EBADF;
|
goto out;
|
goto out;
|
}
|
}
|
|
|
mylen = min(sizeof(buf), inode->i_size);
|
mylen = min(sizeof(buf), inode->i_size);
|
|
|
if (romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset, mylen) <= 0) {
|
if (romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset, mylen) <= 0) {
|
mylen = -EIO;
|
mylen = -EIO;
|
goto out;
|
goto out;
|
}
|
}
|
memcpy_tofs(buffer, buf, mylen);
|
memcpy_tofs(buffer, buf, mylen);
|
|
|
out:
|
out:
|
iput(inode);
|
iput(inode);
|
return mylen;
|
return mylen;
|
}
|
}
|
|
|
static int
|
static int
|
romfs_follow_link(struct inode *dir, struct inode *inode,
|
romfs_follow_link(struct inode *dir, struct inode *inode,
|
int flag, int mode, struct inode **res_inode)
|
int flag, int mode, struct inode **res_inode)
|
{
|
{
|
int error, len;
|
int error, len;
|
char *buf;
|
char *buf;
|
|
|
*res_inode = NULL;
|
*res_inode = NULL;
|
if (!dir) {
|
if (!dir) {
|
dir = current->fs->root;
|
dir = current->fs->root;
|
dir->i_count++;
|
dir->i_count++;
|
}
|
}
|
|
|
if (!inode) {
|
if (!inode) {
|
iput(dir);
|
iput(dir);
|
return -ENOENT;
|
return -ENOENT;
|
}
|
}
|
if (!S_ISLNK(inode->i_mode)) {
|
if (!S_ISLNK(inode->i_mode)) {
|
*res_inode = inode;
|
*res_inode = inode;
|
iput(dir);
|
iput(dir);
|
return 0;
|
return 0;
|
}
|
}
|
if (current->link_count > 5) {
|
if (current->link_count > 5) {
|
iput(inode);
|
iput(inode);
|
iput(dir);
|
iput(dir);
|
return -ELOOP;
|
return -ELOOP;
|
}
|
}
|
|
|
/* Eek. Short enough. */
|
/* Eek. Short enough. */
|
len = inode->i_size;
|
len = inode->i_size;
|
if (!(buf = kmalloc(len+1, GFP_KERNEL))) {
|
if (!(buf = kmalloc(len+1, GFP_KERNEL))) {
|
iput(inode);
|
iput(inode);
|
iput(dir);
|
iput(dir);
|
/* correct? spin? */
|
/* correct? spin? */
|
return -EAGAIN;
|
return -EAGAIN;
|
}
|
}
|
error = romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset, len);
|
error = romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset, len);
|
if (error != len) {
|
if (error != len) {
|
iput(inode);
|
iput(inode);
|
iput(dir);
|
iput(dir);
|
error = -EIO;
|
error = -EIO;
|
} else {
|
} else {
|
iput(inode);
|
iput(inode);
|
buf[len] = 0;
|
buf[len] = 0;
|
current->link_count++;
|
current->link_count++;
|
error = open_namei(buf, flag, mode, res_inode, dir);
|
error = open_namei(buf, flag, mode, res_inode, dir);
|
current->link_count--;
|
current->link_count--;
|
}
|
}
|
|
|
kfree(buf);
|
kfree(buf);
|
return error;
|
return error;
|
}
|
}
|
|
|
/* Mapping from our types to the kernel */
|
/* Mapping from our types to the kernel */
|
|
|
static struct file_operations romfs_file_operations = {
|
static struct file_operations romfs_file_operations = {
|
NULL, /* lseek - default */
|
NULL, /* lseek - default */
|
generic_file_read, /* read */
|
generic_file_read, /* read */
|
NULL, /* write - bad */
|
NULL, /* write - bad */
|
NULL, /* readdir */
|
NULL, /* readdir */
|
NULL, /* select - default */
|
NULL, /* select - default */
|
NULL, /* ioctl */
|
NULL, /* ioctl */
|
generic_file_mmap, /* mmap */
|
generic_file_mmap, /* mmap */
|
NULL, /* open */
|
NULL, /* open */
|
NULL, /* release */
|
NULL, /* release */
|
NULL, /* fsync */
|
NULL, /* fsync */
|
NULL, /* fasync */
|
NULL, /* fasync */
|
NULL, /* check_media_change */
|
NULL, /* check_media_change */
|
NULL, /* revalidate */
|
NULL, /* revalidate */
|
#ifdef MAGIC_ROM_PTR
|
#ifdef MAGIC_ROM_PTR
|
romfs_romptr, /* romptr */
|
romfs_romptr, /* romptr */
|
#endif
|
#endif
|
};
|
};
|
|
|
static struct inode_operations romfs_file_inode_operations = {
|
static struct inode_operations romfs_file_inode_operations = {
|
&romfs_file_operations,
|
&romfs_file_operations,
|
NULL, /* create */
|
NULL, /* create */
|
NULL, /* lookup */
|
NULL, /* lookup */
|
NULL, /* link */
|
NULL, /* link */
|
NULL, /* unlink */
|
NULL, /* unlink */
|
NULL, /* symlink */
|
NULL, /* symlink */
|
NULL, /* mkdir */
|
NULL, /* mkdir */
|
NULL, /* rmdir */
|
NULL, /* rmdir */
|
NULL, /* mknod */
|
NULL, /* mknod */
|
NULL, /* rename */
|
NULL, /* rename */
|
NULL, /* readlink */
|
NULL, /* readlink */
|
NULL, /* follow_link */
|
NULL, /* follow_link */
|
romfs_readpage, /* readpage */
|
romfs_readpage, /* readpage */
|
NULL, /* writepage */
|
NULL, /* writepage */
|
NULL, /* bmap -- not really */
|
NULL, /* bmap -- not really */
|
NULL, /* truncate */
|
NULL, /* truncate */
|
NULL, /* permission */
|
NULL, /* permission */
|
NULL, /* smap */
|
NULL, /* smap */
|
};
|
};
|
|
|
static struct file_operations romfs_dir_operations = {
|
static struct file_operations romfs_dir_operations = {
|
NULL, /* lseek - default */
|
NULL, /* lseek - default */
|
NULL, /* read */
|
NULL, /* read */
|
NULL, /* write - bad */
|
NULL, /* write - bad */
|
romfs_readdir, /* readdir */
|
romfs_readdir, /* readdir */
|
NULL, /* select - default */
|
NULL, /* select - default */
|
NULL, /* ioctl */
|
NULL, /* ioctl */
|
NULL, /* mmap */
|
NULL, /* mmap */
|
NULL, /* open */
|
NULL, /* open */
|
NULL, /* release */
|
NULL, /* release */
|
NULL, /* fsync */
|
NULL, /* fsync */
|
NULL, /* fasync */
|
NULL, /* fasync */
|
NULL, /* check_media_change */
|
NULL, /* check_media_change */
|
NULL /* revalidate */
|
NULL /* revalidate */
|
};
|
};
|
|
|
/* Merged dir/symlink op table. readdir/lookup/readlink/follow_link
|
/* Merged dir/symlink op table. readdir/lookup/readlink/follow_link
|
* will protect from type mismatch.
|
* will protect from type mismatch.
|
*/
|
*/
|
|
|
static struct inode_operations romfs_dirlink_inode_operations = {
|
static struct inode_operations romfs_dirlink_inode_operations = {
|
&romfs_dir_operations,
|
&romfs_dir_operations,
|
NULL, /* create */
|
NULL, /* create */
|
romfs_lookup, /* lookup */
|
romfs_lookup, /* lookup */
|
NULL, /* link */
|
NULL, /* link */
|
NULL, /* unlink */
|
NULL, /* unlink */
|
NULL, /* symlink */
|
NULL, /* symlink */
|
NULL, /* mkdir */
|
NULL, /* mkdir */
|
NULL, /* rmdir */
|
NULL, /* rmdir */
|
NULL, /* mknod */
|
NULL, /* mknod */
|
NULL, /* rename */
|
NULL, /* rename */
|
romfs_readlink, /* readlink */
|
romfs_readlink, /* readlink */
|
romfs_follow_link, /* follow_link */
|
romfs_follow_link, /* follow_link */
|
NULL, /* readpage */
|
NULL, /* readpage */
|
NULL, /* writepage */
|
NULL, /* writepage */
|
NULL, /* bmap */
|
NULL, /* bmap */
|
NULL, /* truncate */
|
NULL, /* truncate */
|
NULL, /* permission */
|
NULL, /* permission */
|
NULL, /* smap */
|
NULL, /* smap */
|
};
|
};
|
|
|
static mode_t romfs_modemap[] =
|
static mode_t romfs_modemap[] =
|
{
|
{
|
0, S_IFDIR, S_IFREG, S_IFLNK+0777,
|
0, S_IFDIR, S_IFREG, S_IFLNK+0777,
|
S_IFBLK, S_IFCHR, S_IFSOCK, S_IFIFO
|
S_IFBLK, S_IFCHR, S_IFSOCK, S_IFIFO
|
};
|
};
|
|
|
static struct inode_operations *romfs_inoops[] =
|
static struct inode_operations *romfs_inoops[] =
|
{
|
{
|
NULL, /* hardlink, handled elsewhere */
|
NULL, /* hardlink, handled elsewhere */
|
&romfs_dirlink_inode_operations,
|
&romfs_dirlink_inode_operations,
|
&romfs_file_inode_operations,
|
&romfs_file_inode_operations,
|
&romfs_dirlink_inode_operations,
|
&romfs_dirlink_inode_operations,
|
&blkdev_inode_operations, /* standard handlers */
|
&blkdev_inode_operations, /* standard handlers */
|
&chrdev_inode_operations,
|
&chrdev_inode_operations,
|
NULL, /* socket */
|
NULL, /* socket */
|
NULL, /* fifo */
|
NULL, /* fifo */
|
};
|
};
|
|
|
static void
|
static void
|
romfs_read_inode(struct inode *i)
|
romfs_read_inode(struct inode *i)
|
{
|
{
|
int nextfh, ino;
|
int nextfh, ino;
|
struct romfs_inode ri;
|
struct romfs_inode ri;
|
|
|
ino = i->i_ino & ROMFH_MASK;
|
ino = i->i_ino & ROMFH_MASK;
|
|
|
i->i_op = NULL;
|
i->i_op = NULL;
|
i->i_mode = 0;
|
i->i_mode = 0;
|
|
|
/* Loop for finding the real hard link */
|
/* Loop for finding the real hard link */
|
for(;;) {
|
for(;;) {
|
if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) {
|
if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) {
|
printk("romfs: read error for inode 0x%x\n", ino);
|
printk("romfs: read error for inode 0x%x\n", ino);
|
return;
|
return;
|
}
|
}
|
/* XXX: do romfs_checksum here too (with name) */
|
/* XXX: do romfs_checksum here too (with name) */
|
|
|
nextfh = ntohl(ri.next);
|
nextfh = ntohl(ri.next);
|
if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
|
if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
|
break;
|
break;
|
|
|
ino = ntohl(ri.spec) & ROMFH_MASK;
|
ino = ntohl(ri.spec) & ROMFH_MASK;
|
}
|
}
|
|
|
i->i_nlink = 1; /* Hard to decide.. */
|
i->i_nlink = 1; /* Hard to decide.. */
|
i->i_size = ntohl(ri.size);
|
i->i_size = ntohl(ri.size);
|
i->i_mtime = i->i_atime = i->i_ctime = 0;
|
i->i_mtime = i->i_atime = i->i_ctime = 0;
|
i->i_uid = i->i_gid = 0;
|
i->i_uid = i->i_gid = 0;
|
|
|
i->i_op = romfs_inoops[nextfh & ROMFH_TYPE];
|
i->i_op = romfs_inoops[nextfh & ROMFH_TYPE];
|
|
|
/* Precalculate the data offset */
|
/* Precalculate the data offset */
|
ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN);
|
ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN);
|
if (ino >= 0)
|
if (ino >= 0)
|
ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK);
|
ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK);
|
else
|
else
|
ino = 0;
|
ino = 0;
|
|
|
i->u.romfs_i.i_metasize = ino;
|
i->u.romfs_i.i_metasize = ino;
|
i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
|
i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
|
|
|
/* Compute permissions */
|
/* Compute permissions */
|
ino = S_IRUGO|S_IWUSR;
|
ino = S_IRUGO|S_IWUSR;
|
ino |= romfs_modemap[nextfh & ROMFH_TYPE];
|
ino |= romfs_modemap[nextfh & ROMFH_TYPE];
|
if (nextfh & ROMFH_EXEC) {
|
if (nextfh & ROMFH_EXEC) {
|
ino |= S_IXUGO;
|
ino |= S_IXUGO;
|
}
|
}
|
i->i_mode = ino;
|
i->i_mode = ino;
|
|
|
if (S_ISFIFO(ino))
|
if (S_ISFIFO(ino))
|
init_fifo(i);
|
init_fifo(i);
|
else if (S_ISDIR(ino))
|
else if (S_ISDIR(ino))
|
i->i_size = i->u.romfs_i.i_metasize;
|
i->i_size = i->u.romfs_i.i_metasize;
|
else if (S_ISBLK(ino) || S_ISCHR(ino)) {
|
else if (S_ISBLK(ino) || S_ISCHR(ino)) {
|
i->i_mode &= ~(S_IRWXG|S_IRWXO);
|
i->i_mode &= ~(S_IRWXG|S_IRWXO);
|
ino = ntohl(ri.spec);
|
ino = ntohl(ri.spec);
|
i->i_rdev = MKDEV(ino>>16,ino&0xffff);
|
i->i_rdev = MKDEV(ino>>16,ino&0xffff);
|
}
|
}
|
}
|
}
|
|
|
static struct super_operations romfs_ops = {
|
static struct super_operations romfs_ops = {
|
romfs_read_inode, /* read inode */
|
romfs_read_inode, /* read inode */
|
NULL, /* notify change */
|
NULL, /* notify change */
|
NULL, /* write inode */
|
NULL, /* write inode */
|
NULL, /* put inode */
|
NULL, /* put inode */
|
romfs_put_super, /* put super */
|
romfs_put_super, /* put super */
|
NULL, /* write super */
|
NULL, /* write super */
|
romfs_statfs, /* statfs */
|
romfs_statfs, /* statfs */
|
NULL /* remount */
|
NULL /* remount */
|
};
|
};
|
|
|
static struct file_system_type romfs_fs_type = {
|
static struct file_system_type romfs_fs_type = {
|
romfs_read_super, "romfs", 1, NULL
|
romfs_read_super, "romfs", 1, NULL
|
};
|
};
|
|
|
int
|
int
|
init_romfs_fs(void)
|
init_romfs_fs(void)
|
{
|
{
|
return register_filesystem(&romfs_fs_type);
|
return register_filesystem(&romfs_fs_type);
|
}
|
}
|
|
|
#ifdef MODULE
|
#ifdef MODULE
|
|
|
/* Yes, works even as a module... :) */
|
/* Yes, works even as a module... :) */
|
|
|
int
|
int
|
init_module(void)
|
init_module(void)
|
{
|
{
|
int status;
|
int status;
|
|
|
if ((status = init_romfs_fs()) == 0)
|
if ((status = init_romfs_fs()) == 0)
|
register_symtab(0);
|
register_symtab(0);
|
return status;
|
return status;
|
}
|
}
|
|
|
void
|
void
|
cleanup_module(void)
|
cleanup_module(void)
|
{
|
{
|
unregister_filesystem(&romfs_fs_type);
|
unregister_filesystem(&romfs_fs_type);
|
}
|
}
|
#endif
|
#endif
|
|
|