LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH 2/7] omfs: add inode routines
@ 2008-04-12 22:58 Bob Copeland
2008-04-13 8:05 ` Christoph Hellwig
` (3 more replies)
0 siblings, 4 replies; 15+ messages in thread
From: Bob Copeland @ 2008-04-12 22:58 UTC (permalink / raw)
To: linux-kernel; +Cc: linux-fsdevel, akpm, Bob Copeland
Add basic superblock and inode handling routines for OMFS
Signed-off-by: Bob Copeland <me@bobcopeland.com>
---
fs/omfs/inode.c | 439 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 439 insertions(+), 0 deletions(-)
create mode 100644 fs/omfs/inode.c
diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c
new file mode 100644
index 0000000..92d794f
--- /dev/null
+++ b/fs/omfs/inode.c
@@ -0,0 +1,439 @@
+/*
+ * Optimized MPEG FS - inode and super operations.
+ * Copyright (C) 2006 Bob Copeland <me@bobcopeland.com>
+ * Released under GPL v2.
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/buffer_head.h>
+#include <linux/vmalloc.h>
+#include "omfs.h"
+
+MODULE_AUTHOR("Bob Copeland <me@bobcopeland.com>");
+MODULE_DESCRIPTION("OMFS (ReplayTV/Karma) Filesystem for Linux");
+MODULE_LICENSE("GPL");
+
+struct inode *omfs_new_inode(struct inode *dir, int mode)
+{
+ struct inode *inode;
+ u64 new_block;
+ int res;
+ int len;
+ struct omfs_sb_info *sbi = OMFS_SB(dir->i_sb);
+
+ inode = new_inode(dir->i_sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ res = omfs_allocate_range(dir->i_sb, sbi->s_mirrors, sbi->s_mirrors,
+ &new_block, &len);
+ if (res)
+ return ERR_PTR(res);
+
+ inode->i_ino = new_block;
+ inode->i_mode = mode;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_blocks = 0;
+ inode->i_mapping->a_ops = &omfs_aops;
+
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ inode->i_op = &omfs_dir_inops;
+ inode->i_fop = &omfs_dir_operations;
+ inode->i_size = sbi->s_sys_blocksize;
+ inode->i_nlink++;
+ break;
+ case S_IFREG:
+ inode->i_op = &omfs_file_inops;
+ inode->i_fop = &omfs_file_operations;
+ inode->i_size = 0;
+ break;
+ }
+
+ insert_inode_hash(inode);
+ mark_inode_dirty(inode);
+ return inode;
+}
+
+static int omfs_write_inode(struct inode *inode, int wait)
+{
+ struct omfs_inode *oi;
+ struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
+ struct buffer_head *bh, *bh2;
+ unsigned int block;
+ u64 ctime;
+ int i;
+ int ret = 0;
+
+ /* get current inode since we may have written sibling ptrs etc. */
+ block = clus_to_blk(sbi, inode->i_ino);
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh) {
+ ret = -EIO;
+ goto out_nobh;
+ }
+
+ oi = (struct omfs_inode *) bh->b_data;
+
+ oi->i_head.h_self = cpu_to_be64(inode->i_ino);
+ if (S_ISDIR(inode->i_mode))
+ oi->i_type = OMFS_DIR;
+ else if (S_ISREG(inode->i_mode))
+ oi->i_type = OMFS_FILE;
+ else {
+ printk(KERN_WARNING "omfs: unknown file type: %d\n",
+ inode->i_mode);
+ ret = -EIO;
+ goto out;
+ }
+
+ oi->i_head.h_body_size = cpu_to_be32(sbi->s_sys_blocksize -
+ sizeof(struct omfs_header));
+ oi->i_head.h_version = 1;
+ oi->i_head.h_type = OMFS_INODE_NORMAL;
+ oi->i_head.h_magic = OMFS_IMAGIC;
+ oi->i_size = cpu_to_be64(inode->i_size);
+
+ ctime = inode->i_ctime.tv_sec * 1000LL +
+ ((inode->i_ctime.tv_nsec + 999)/1000);
+ oi->i_ctime = cpu_to_be64(ctime);
+
+ if (omfs_update_checksums(oi) != 0) {
+ ret = -EIO;
+ goto out;
+ }
+
+ mark_buffer_dirty(bh);
+ if (wait) {
+ sync_dirty_buffer(bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh))
+ ret = -EIO;
+ }
+
+ /* if mirroring writes, copy to next fsblock */
+ for (i = 1; i < sbi->s_mirrors; i++) {
+ bh2 = sb_bread(inode->i_sb, block + i *
+ (sbi->s_blocksize / sbi->s_sys_blocksize));
+ if (!bh2) {
+ brelse(bh2);
+ ret = -EIO;
+ goto out;
+ }
+ memcpy(bh2->b_data, bh->b_data, bh->b_size);
+ mark_buffer_dirty(bh2);
+ if (wait) {
+ sync_dirty_buffer(bh2);
+ if (buffer_req(bh2) && !buffer_uptodate(bh2))
+ ret = -EIO;
+ }
+ brelse(bh2);
+ }
+out:
+ brelse(bh);
+out_nobh:
+ return ret;
+}
+
+int omfs_sync_inode(struct inode *inode)
+{
+ return omfs_write_inode(inode, 1);
+}
+
+/*
+ * called when an entry is deleted, need to clear the bits in the
+ * bitmaps.
+ */
+static void omfs_delete_inode(struct inode *inode)
+{
+ truncate_inode_pages(&inode->i_data, 0);
+
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_size = 0;
+ omfs_shrink_inode(inode);
+ }
+
+ omfs_clear_range(inode->i_sb, inode->i_ino, 2);
+ clear_inode(inode);
+}
+
+struct inode *omfs_iget(struct super_block *sb, ino_t ino)
+{
+ struct omfs_inode *oi;
+ struct buffer_head *bh;
+ unsigned int block;
+ u64 ctime;
+ unsigned long nsecs;
+ struct inode *inode;
+
+ inode = iget_locked(sb, ino);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+
+ block = clus_to_blk(OMFS_SB(inode->i_sb), ino);
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh) {
+ iget_failed(inode);
+ return ERR_PTR(-EIO);
+ }
+
+ oi = (struct omfs_inode *)bh->b_data;
+
+ /* check self */
+ if (ino != be64_to_cpu(oi->i_head.h_self)) {
+ iget_failed(inode);
+ return ERR_PTR(-EIO);
+ }
+
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+
+ ctime = be64_to_cpu(oi->i_ctime);
+ nsecs = do_div(ctime, 1000) * 1000L;
+
+ inode->i_atime.tv_sec = ctime;
+ inode->i_mtime.tv_sec = ctime;
+ inode->i_ctime.tv_sec = ctime;
+ inode->i_atime.tv_nsec = nsecs;
+ inode->i_mtime.tv_nsec = nsecs;
+ inode->i_ctime.tv_nsec = nsecs;
+
+ inode->i_mapping->a_ops = &omfs_aops;
+
+ switch (oi->i_type) {
+ case OMFS_DIR:
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IWUGO | S_IXUGO;
+ inode->i_op = &omfs_dir_inops;
+ inode->i_fop = &omfs_dir_operations;
+ inode->i_size = be32_to_cpu(oi->i_head.h_body_size) +
+ sizeof(struct omfs_header);
+ inode->i_nlink++;
+ break;
+ case OMFS_FILE:
+ inode->i_mode = S_IFREG | S_IRUGO | S_IWUGO;
+ inode->i_fop = &omfs_file_operations;
+ inode->i_size = be64_to_cpu(oi->i_size);
+ break;
+ }
+ brelse(bh);
+ unlock_new_inode(inode);
+ return inode;
+}
+
+
+static void omfs_put_super(struct super_block *sb)
+{
+ struct omfs_sb_info *sbi = OMFS_SB(sb);
+ if (sbi) {
+ kfree(sbi->s_imap);
+ kfree(sbi);
+ }
+ sb->s_fs_info = NULL;
+}
+
+static int omfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct super_block *s = dentry->d_sb;
+ struct omfs_sb_info *sbi = OMFS_SB(s);
+ buf->f_type = OMFS_MAGIC;
+ buf->f_bsize = sbi->s_blocksize;
+ buf->f_blocks = sbi->s_num_blocks;
+ buf->f_files = sbi->s_num_blocks;
+ buf->f_namelen = OMFS_NAMELEN;
+
+ buf->f_bfree = buf->f_bavail = buf->f_ffree =
+ omfs_count_free(s);
+ return 0;
+}
+
+static struct super_operations omfs_sops = {
+ .write_inode = omfs_write_inode,
+ .delete_inode = omfs_delete_inode,
+ .put_super = omfs_put_super,
+ .statfs = omfs_statfs,
+};
+
+/*
+ * For Rio Karma, there is an on-disk free bitmap whose location is
+ * stored in the root block. For ReplayTV, there is no such free bitmap
+ * so we have to walk the tree. Both inodes and file data are allocated
+ * from the same map. This array can be big (300k) so we allocate
+ * in units of the blocksize.
+ */
+static int omfs_get_imap(struct super_block *sb)
+{
+ int bitmap_size;
+ int array_size;
+ int count;
+ struct omfs_sb_info *sbi = OMFS_SB(sb);
+ struct buffer_head *bh;
+ unsigned long **ptr;
+ sector_t block;
+
+ bitmap_size = (sbi->s_num_blocks + 7) / 8;
+ array_size = (bitmap_size + sb->s_blocksize - 1) / sb->s_blocksize;
+
+ sbi->s_imap_size = array_size;
+ sbi->s_imap = kzalloc(array_size * sizeof(unsigned long *), GFP_KERNEL);
+ if (!sbi->s_imap)
+ return -ENOMEM;
+
+ block = clus_to_blk(sbi, sbi->s_bitmap_ino);
+ ptr = sbi->s_imap;
+ for (count = bitmap_size; count > 0; count -= sb->s_blocksize) {
+ bh = sb_bread(sb, block++);
+ if (!bh)
+ goto nomem;
+ *ptr = kmalloc(sb->s_blocksize, GFP_KERNEL);
+ if (!*ptr)
+ goto nomem;
+ memcpy(*ptr, bh->b_data, sb->s_blocksize);
+ if (count < sb->s_blocksize)
+ memset((void *)*ptr + count, 0xff, sb->s_blocksize - count);
+ brelse(bh);
+ ptr++;
+ }
+ return 0;
+nomem:
+ for (count = 0; count < array_size; count++)
+ kfree(sbi->s_imap[count]);
+
+ kfree(sbi->s_imap);
+ sbi->s_imap = NULL;
+ return -ENOMEM;
+}
+
+static void set_block_shift(struct omfs_sb_info *sbi)
+{
+ unsigned int scale = sbi->s_blocksize / sbi->s_sys_blocksize;
+ sbi->s_block_shift = 0;
+ for (scale >>= 1; scale; scale >>= 1)
+ sbi->s_block_shift++;
+}
+
+static int omfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct buffer_head *bh = NULL, *bh2 = NULL;
+ struct omfs_super_block *omfs_sb;
+ struct omfs_root_block *omfs_rb;
+ struct omfs_sb_info *sbi;
+ struct inode *root;
+ sector_t start;
+ int ret = 0;
+
+ sbi = kzalloc(sizeof(struct omfs_sb_info), GFP_KERNEL);
+ if (!sbi)
+ return -ENOMEM;
+ sb->s_fs_info = sbi;
+
+ sb->s_maxbytes = 0xffffffff;
+
+ sb_set_blocksize(sb, 0x200);
+
+ bh = sb_bread(sb, 0);
+ if (!bh)
+ goto out;
+
+ omfs_sb = (struct omfs_super_block *)bh->b_data;
+
+ if (be32_to_cpu(omfs_sb->s_magic) != OMFS_MAGIC) {
+ if (!silent)
+ printk(KERN_ERR "omfs: Invalid superblock (%x)\n",
+ omfs_sb->s_magic);
+ goto out;
+ }
+ sb->s_magic = OMFS_MAGIC;
+
+ sbi->s_num_blocks = be64_to_cpu(omfs_sb->s_num_blocks);
+ sbi->s_blocksize = be32_to_cpu(omfs_sb->s_blocksize);
+ sbi->s_mirrors = be32_to_cpu(omfs_sb->s_mirrors);
+ sbi->s_root_ino = be64_to_cpu(omfs_sb->s_root_block);
+ sbi->s_sys_blocksize = be32_to_cpu(omfs_sb->s_sys_blocksize);
+ mutex_init(&sbi->s_bitmap_lock);
+
+ /*
+ * Use sys_blocksize as the fs block since it is smaller than a
+ * page while the fs blocksize can be larger.
+ */
+ sb_set_blocksize(sb, sbi->s_sys_blocksize);
+
+ /*
+ * ...and the difference goes into a shift. sys_blocksize is always
+ * a power of two factor of blocksize.
+ */
+ set_block_shift(sbi);
+
+ start = clus_to_blk(sbi, be64_to_cpu(omfs_sb->s_root_block));
+ bh2 = sb_bread(sb, start);
+ if (!bh2)
+ goto out;
+
+ omfs_rb = (struct omfs_root_block *)bh2->b_data;
+
+ sbi->s_bitmap_ino = be64_to_cpu(omfs_rb->r_bitmap);
+ sbi->s_clustersize = be32_to_cpu(omfs_rb->r_clustersize);
+
+ ret = omfs_get_imap(sb);
+ if (ret)
+ goto end;
+
+ sb->s_op = &omfs_sops;
+
+ root = omfs_iget(sb, be64_to_cpu(omfs_rb->r_root_dir));
+ if (IS_ERR(root)) {
+ ret = PTR_ERR(root);
+ goto end;
+ }
+
+ sb->s_root = d_alloc_root(root);
+ if (!sb->s_root) {
+ iput(root);
+ goto out;
+ }
+ printk(KERN_DEBUG "omfs: Mounted volume %s\n", omfs_rb->r_name);
+ goto end;
+
+out:
+ ret = -EINVAL;
+
+end:
+ if (bh2)
+ brelse(bh2);
+ if (bh)
+ brelse(bh);
+ return ret;
+}
+
+static int omfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data, struct vfsmount *m)
+{
+ return get_sb_bdev(fs_type, flags, dev_name, data, omfs_fill_super, m);
+}
+
+static struct file_system_type omfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "omfs",
+ .get_sb = omfs_get_sb,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
+static int __init init_omfs_fs(void)
+{
+ return register_filesystem(&omfs_fs_type);
+}
+
+static void __exit exit_omfs_fs(void)
+{
+ unregister_filesystem(&omfs_fs_type);
+}
+
+module_init(init_omfs_fs);
+module_exit(exit_omfs_fs);
--
1.5.4.2
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-04-12 22:58 [PATCH 2/7] omfs: add inode routines Bob Copeland
@ 2008-04-13 8:05 ` Christoph Hellwig
2008-04-13 9:17 ` Alan Cox
` (2 subsequent siblings)
3 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2008-04-13 8:05 UTC (permalink / raw)
To: Bob Copeland; +Cc: linux-kernel, linux-fsdevel, akpm
Looks good. Minor nitpick:
> +static void omfs_put_super(struct super_block *sb)
> +{
> + struct omfs_sb_info *sbi = OMFS_SB(sb);
> + if (sbi) {
This if check is no needed, as your fill_super method never returns
success without setting it up.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-04-12 22:58 [PATCH 2/7] omfs: add inode routines Bob Copeland
2008-04-13 8:05 ` Christoph Hellwig
@ 2008-04-13 9:17 ` Alan Cox
2008-04-15 18:03 ` Miklos Szeredi
2008-04-15 18:30 ` Marcin Slusarz
3 siblings, 0 replies; 15+ messages in thread
From: Alan Cox @ 2008-04-13 9:17 UTC (permalink / raw)
To: Bob Copeland; +Cc: linux-kernel, linux-fsdevel, akpm, Bob Copeland
> + * so we have to walk the tree. Both inodes and file data are allocated
> + * from the same map. This array can be big (300k) so we allocate
> + * in units of the blocksize.
sbi->s_num_blocks doesn't appear to be validated at the time of mount ?
> +static int omfs_fill_super(struct super_block *sb, void *data, int silent)
> +{
> + sbi->s_num_blocks = be64_to_cpu(omfs_sb->s_num_blocks);
> + sbi->s_blocksize = be32_to_cpu(omfs_sb->s_blocksize);
> + sbi->s_mirrors = be32_to_cpu(omfs_sb->s_mirrors);
> + sbi->s_root_ino = be64_to_cpu(omfs_sb->s_root_block);
> + sbi->s_sys_blocksize = be32_to_cpu(omfs_sb->s_sys_blocksize);
Several of these look like they should be checked before use elsewhere in
the code (eg blocksize is fed into shifts later)
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-04-12 22:58 [PATCH 2/7] omfs: add inode routines Bob Copeland
2008-04-13 8:05 ` Christoph Hellwig
2008-04-13 9:17 ` Alan Cox
@ 2008-04-15 18:03 ` Miklos Szeredi
2008-04-15 18:33 ` Bob Copeland
2008-04-15 18:30 ` Marcin Slusarz
3 siblings, 1 reply; 15+ messages in thread
From: Miklos Szeredi @ 2008-04-15 18:03 UTC (permalink / raw)
To: hch; +Cc: linux-kernel, linux-fsdevel, akpm, me
Hmm, looks like error handling needs a makeover if this is really to
become example code. See comments inline.
Somebody said this is fun? Please do a proper review of this patchset
then, thank you. ("Political" eh? Now I think *that* is really
insulting to Andrew)
Miklos
> diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c
> new file mode 100644
> index 0000000..92d794f
> --- /dev/null
> +++ b/fs/omfs/inode.c
> @@ -0,0 +1,439 @@
> +/*
> + * Optimized MPEG FS - inode and super operations.
> + * Copyright (C) 2006 Bob Copeland <me@bobcopeland.com>
> + * Released under GPL v2.
> + */
> +#include <linux/version.h>
> +#include <linux/module.h>
> +#include <linux/sched.h>
> +#include <linux/fs.h>
> +#include <linux/vfs.h>
> +#include <linux/buffer_head.h>
> +#include <linux/vmalloc.h>
> +#include "omfs.h"
> +
> +MODULE_AUTHOR("Bob Copeland <me@bobcopeland.com>");
> +MODULE_DESCRIPTION("OMFS (ReplayTV/Karma) Filesystem for Linux");
> +MODULE_LICENSE("GPL");
> +
> +struct inode *omfs_new_inode(struct inode *dir, int mode)
> +{
> + struct inode *inode;
> + u64 new_block;
> + int res;
> + int len;
> + struct omfs_sb_info *sbi = OMFS_SB(dir->i_sb);
> +
> + inode = new_inode(dir->i_sb);
> + if (!inode)
> + return ERR_PTR(-ENOMEM);
> +
> + res = omfs_allocate_range(dir->i_sb, sbi->s_mirrors, sbi->s_mirrors,
> + &new_block, &len);
> + if (res)
> + return ERR_PTR(res);
Leaks inode.
> +
> + inode->i_ino = new_block;
> + inode->i_mode = mode;
> + inode->i_uid = current->fsuid;
> + inode->i_gid = current->fsgid;
> + inode->i_blocks = 0;
> + inode->i_mapping->a_ops = &omfs_aops;
> +
> + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
> + switch (mode & S_IFMT) {
> + case S_IFDIR:
> + inode->i_op = &omfs_dir_inops;
> + inode->i_fop = &omfs_dir_operations;
> + inode->i_size = sbi->s_sys_blocksize;
> + inode->i_nlink++;
> + break;
> + case S_IFREG:
> + inode->i_op = &omfs_file_inops;
> + inode->i_fop = &omfs_file_operations;
> + inode->i_size = 0;
> + break;
> + }
> +
> + insert_inode_hash(inode);
> + mark_inode_dirty(inode);
> + return inode;
> +}
> +
> +static int omfs_write_inode(struct inode *inode, int wait)
> +{
> + struct omfs_inode *oi;
> + struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
> + struct buffer_head *bh, *bh2;
> + unsigned int block;
> + u64 ctime;
> + int i;
> + int ret = 0;
> +
> + /* get current inode since we may have written sibling ptrs etc. */
> + block = clus_to_blk(sbi, inode->i_ino);
> + bh = sb_bread(inode->i_sb, block);
> + if (!bh) {
> + ret = -EIO;
> + goto out_nobh;
> + }
This form is preferred:
ret = -EIO;
if (!bh)
goto out_nobh;
> +
> + oi = (struct omfs_inode *) bh->b_data;
> +
> + oi->i_head.h_self = cpu_to_be64(inode->i_ino);
> + if (S_ISDIR(inode->i_mode))
> + oi->i_type = OMFS_DIR;
> + else if (S_ISREG(inode->i_mode))
> + oi->i_type = OMFS_FILE;
> + else {
> + printk(KERN_WARNING "omfs: unknown file type: %d\n",
> + inode->i_mode);
> + ret = -EIO;
> + goto out;
Ditto. If err is the same as assigned previously it doesn't need to
be assigned again, so that even less lines.
> + }
> +
> + oi->i_head.h_body_size = cpu_to_be32(sbi->s_sys_blocksize -
> + sizeof(struct omfs_header));
> + oi->i_head.h_version = 1;
> + oi->i_head.h_type = OMFS_INODE_NORMAL;
> + oi->i_head.h_magic = OMFS_IMAGIC;
> + oi->i_size = cpu_to_be64(inode->i_size);
> +
> + ctime = inode->i_ctime.tv_sec * 1000LL +
> + ((inode->i_ctime.tv_nsec + 999)/1000);
> + oi->i_ctime = cpu_to_be64(ctime);
> +
> + if (omfs_update_checksums(oi) != 0) {
> + ret = -EIO;
> + goto out;
Ditto.
> + }
> +
> + mark_buffer_dirty(bh);
> + if (wait) {
> + sync_dirty_buffer(bh);
> + if (buffer_req(bh) && !buffer_uptodate(bh))
> + ret = -EIO;
Hmm, here it sets ret, but doesn't exit. Deliberate?
> + }
> +
> + /* if mirroring writes, copy to next fsblock */
> + for (i = 1; i < sbi->s_mirrors; i++) {
> + bh2 = sb_bread(inode->i_sb, block + i *
> + (sbi->s_blocksize / sbi->s_sys_blocksize));
> + if (!bh2) {
> + brelse(bh2);
> + ret = -EIO;
> + goto out;
Ditto1.
> + }
> + memcpy(bh2->b_data, bh->b_data, bh->b_size);
> + mark_buffer_dirty(bh2);
> + if (wait) {
> + sync_dirty_buffer(bh2);
> + if (buffer_req(bh2) && !buffer_uptodate(bh2))
> + ret = -EIO;
Ditto2?
> + }
> + brelse(bh2);
> + }
> +out:
> + brelse(bh);
> +out_nobh:
> + return ret;
> +}
> +
> +int omfs_sync_inode(struct inode *inode)
> +{
> + return omfs_write_inode(inode, 1);
> +}
> +
> +/*
> + * called when an entry is deleted, need to clear the bits in the
> + * bitmaps.
> + */
> +static void omfs_delete_inode(struct inode *inode)
> +{
> + truncate_inode_pages(&inode->i_data, 0);
> +
> + if (S_ISREG(inode->i_mode)) {
> + inode->i_size = 0;
> + omfs_shrink_inode(inode);
> + }
> +
> + omfs_clear_range(inode->i_sb, inode->i_ino, 2);
> + clear_inode(inode);
> +}
> +
> +struct inode *omfs_iget(struct super_block *sb, ino_t ino)
> +{
> + struct omfs_inode *oi;
> + struct buffer_head *bh;
> + unsigned int block;
> + u64 ctime;
> + unsigned long nsecs;
> + struct inode *inode;
> +
> + inode = iget_locked(sb, ino);
> + if (!inode)
> + return ERR_PTR(-ENOMEM);
> + if (!(inode->i_state & I_NEW))
> + return inode;
> +
> + block = clus_to_blk(OMFS_SB(inode->i_sb), ino);
> + bh = sb_bread(inode->i_sb, block);
> + if (!bh) {
> + iget_failed(inode);
> + return ERR_PTR(-EIO);
Should be:
if (!bh)
goto iget_failed;
...
iget_failed:
iget_failed(inode);
return ERR_PTR(-EIO);
> + }
> +
> + oi = (struct omfs_inode *)bh->b_data;
> +
> + /* check self */
> + if (ino != be64_to_cpu(oi->i_head.h_self)) {
> + iget_failed(inode);
> + return ERR_PTR(-EIO);
Leaks bh. See above.
> + }
> +
> + inode->i_uid = 0;
> + inode->i_gid = 0;
> +
> + ctime = be64_to_cpu(oi->i_ctime);
> + nsecs = do_div(ctime, 1000) * 1000L;
> +
> + inode->i_atime.tv_sec = ctime;
> + inode->i_mtime.tv_sec = ctime;
> + inode->i_ctime.tv_sec = ctime;
> + inode->i_atime.tv_nsec = nsecs;
> + inode->i_mtime.tv_nsec = nsecs;
> + inode->i_ctime.tv_nsec = nsecs;
> +
> + inode->i_mapping->a_ops = &omfs_aops;
> +
> + switch (oi->i_type) {
> + case OMFS_DIR:
> + inode->i_mode = S_IFDIR | S_IRUGO | S_IWUGO | S_IXUGO;
> + inode->i_op = &omfs_dir_inops;
> + inode->i_fop = &omfs_dir_operations;
> + inode->i_size = be32_to_cpu(oi->i_head.h_body_size) +
> + sizeof(struct omfs_header);
> + inode->i_nlink++;
> + break;
> + case OMFS_FILE:
> + inode->i_mode = S_IFREG | S_IRUGO | S_IWUGO;
> + inode->i_fop = &omfs_file_operations;
> + inode->i_size = be64_to_cpu(oi->i_size);
> + break;
> + }
> + brelse(bh);
> + unlock_new_inode(inode);
> + return inode;
> +}
> +
> +
> +static void omfs_put_super(struct super_block *sb)
> +{
> + struct omfs_sb_info *sbi = OMFS_SB(sb);
> + if (sbi) {
> + kfree(sbi->s_imap);
> + kfree(sbi);
> + }
> + sb->s_fs_info = NULL;
> +}
> +
> +static int omfs_statfs(struct dentry *dentry, struct kstatfs *buf)
> +{
> + struct super_block *s = dentry->d_sb;
> + struct omfs_sb_info *sbi = OMFS_SB(s);
> + buf->f_type = OMFS_MAGIC;
> + buf->f_bsize = sbi->s_blocksize;
> + buf->f_blocks = sbi->s_num_blocks;
> + buf->f_files = sbi->s_num_blocks;
> + buf->f_namelen = OMFS_NAMELEN;
> +
> + buf->f_bfree = buf->f_bavail = buf->f_ffree =
> + omfs_count_free(s);
> + return 0;
> +}
> +
> +static struct super_operations omfs_sops = {
> + .write_inode = omfs_write_inode,
> + .delete_inode = omfs_delete_inode,
> + .put_super = omfs_put_super,
> + .statfs = omfs_statfs,
> +};
> +
> +/*
> + * For Rio Karma, there is an on-disk free bitmap whose location is
> + * stored in the root block. For ReplayTV, there is no such free bitmap
> + * so we have to walk the tree. Both inodes and file data are allocated
> + * from the same map. This array can be big (300k) so we allocate
> + * in units of the blocksize.
> + */
> +static int omfs_get_imap(struct super_block *sb)
> +{
> + int bitmap_size;
> + int array_size;
> + int count;
> + struct omfs_sb_info *sbi = OMFS_SB(sb);
> + struct buffer_head *bh;
> + unsigned long **ptr;
> + sector_t block;
> +
> + bitmap_size = (sbi->s_num_blocks + 7) / 8;
> + array_size = (bitmap_size + sb->s_blocksize - 1) / sb->s_blocksize;
> +
> + sbi->s_imap_size = array_size;
> + sbi->s_imap = kzalloc(array_size * sizeof(unsigned long *), GFP_KERNEL);
> + if (!sbi->s_imap)
> + return -ENOMEM;
> +
> + block = clus_to_blk(sbi, sbi->s_bitmap_ino);
> + ptr = sbi->s_imap;
> + for (count = bitmap_size; count > 0; count -= sb->s_blocksize) {
> + bh = sb_bread(sb, block++);
> + if (!bh)
> + goto nomem;
> + *ptr = kmalloc(sb->s_blocksize, GFP_KERNEL);
> + if (!*ptr)
> + goto nomem;
Leaks bh.
> + memcpy(*ptr, bh->b_data, sb->s_blocksize);
> + if (count < sb->s_blocksize)
> + memset((void *)*ptr + count, 0xff, sb->s_blocksize - count);
> + brelse(bh);
> + ptr++;
> + }
> + return 0;
> +nomem:
> + for (count = 0; count < array_size; count++)
> + kfree(sbi->s_imap[count]);
> +
> + kfree(sbi->s_imap);
> + sbi->s_imap = NULL;
> + return -ENOMEM;
> +}
> +
> +static void set_block_shift(struct omfs_sb_info *sbi)
> +{
> + unsigned int scale = sbi->s_blocksize / sbi->s_sys_blocksize;
> + sbi->s_block_shift = 0;
> + for (scale >>= 1; scale; scale >>= 1)
> + sbi->s_block_shift++;
> +}
> +
> +static int omfs_fill_super(struct super_block *sb, void *data, int silent)
> +{
> + struct buffer_head *bh = NULL, *bh2 = NULL;
> + struct omfs_super_block *omfs_sb;
> + struct omfs_root_block *omfs_rb;
> + struct omfs_sb_info *sbi;
> + struct inode *root;
> + sector_t start;
> + int ret = 0;
> +
> + sbi = kzalloc(sizeof(struct omfs_sb_info), GFP_KERNEL);
> + if (!sbi)
> + return -ENOMEM;
> + sb->s_fs_info = sbi;
> +
> + sb->s_maxbytes = 0xffffffff;
> +
> + sb_set_blocksize(sb, 0x200);
> +
> + bh = sb_bread(sb, 0);
> + if (!bh)
> + goto out;
> +
> + omfs_sb = (struct omfs_super_block *)bh->b_data;
> +
> + if (be32_to_cpu(omfs_sb->s_magic) != OMFS_MAGIC) {
> + if (!silent)
> + printk(KERN_ERR "omfs: Invalid superblock (%x)\n",
> + omfs_sb->s_magic);
> + goto out;
> + }
> + sb->s_magic = OMFS_MAGIC;
> +
> + sbi->s_num_blocks = be64_to_cpu(omfs_sb->s_num_blocks);
> + sbi->s_blocksize = be32_to_cpu(omfs_sb->s_blocksize);
> + sbi->s_mirrors = be32_to_cpu(omfs_sb->s_mirrors);
> + sbi->s_root_ino = be64_to_cpu(omfs_sb->s_root_block);
> + sbi->s_sys_blocksize = be32_to_cpu(omfs_sb->s_sys_blocksize);
> + mutex_init(&sbi->s_bitmap_lock);
> +
> + /*
> + * Use sys_blocksize as the fs block since it is smaller than a
> + * page while the fs blocksize can be larger.
> + */
> + sb_set_blocksize(sb, sbi->s_sys_blocksize);
> +
> + /*
> + * ...and the difference goes into a shift. sys_blocksize is always
> + * a power of two factor of blocksize.
> + */
> + set_block_shift(sbi);
> +
> + start = clus_to_blk(sbi, be64_to_cpu(omfs_sb->s_root_block));
> + bh2 = sb_bread(sb, start);
> + if (!bh2)
> + goto out;
> +
> + omfs_rb = (struct omfs_root_block *)bh2->b_data;
> +
> + sbi->s_bitmap_ino = be64_to_cpu(omfs_rb->r_bitmap);
> + sbi->s_clustersize = be32_to_cpu(omfs_rb->r_clustersize);
> +
> + ret = omfs_get_imap(sb);
> + if (ret)
> + goto end;
> +
> + sb->s_op = &omfs_sops;
> +
> + root = omfs_iget(sb, be64_to_cpu(omfs_rb->r_root_dir));
> + if (IS_ERR(root)) {
> + ret = PTR_ERR(root);
> + goto end;
> + }
> +
> + sb->s_root = d_alloc_root(root);
> + if (!sb->s_root) {
> + iput(root);
> + goto out;
> + }
> + printk(KERN_DEBUG "omfs: Mounted volume %s\n", omfs_rb->r_name);
> + goto end;
> +
> +out:
> + ret = -EINVAL;
> +
> +end:
> + if (bh2)
> + brelse(bh2);
> + if (bh)
> + brelse(bh);
This is weird. This should be done by jumping to the proper label
between the brleses.
> + return ret;
> +}
> +
> +static int omfs_get_sb(struct file_system_type *fs_type,
> + int flags, const char *dev_name,
> + void *data, struct vfsmount *m)
> +{
> + return get_sb_bdev(fs_type, flags, dev_name, data, omfs_fill_super, m);
> +}
> +
> +static struct file_system_type omfs_fs_type = {
> + .owner = THIS_MODULE,
> + .name = "omfs",
> + .get_sb = omfs_get_sb,
> + .kill_sb = kill_block_super,
> + .fs_flags = FS_REQUIRES_DEV,
> +};
> +
> +static int __init init_omfs_fs(void)
> +{
> + return register_filesystem(&omfs_fs_type);
> +}
> +
> +static void __exit exit_omfs_fs(void)
> +{
> + unregister_filesystem(&omfs_fs_type);
> +}
> +
> +module_init(init_omfs_fs);
> +module_exit(exit_omfs_fs);
> --
> 1.5.4.2
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-04-12 22:58 [PATCH 2/7] omfs: add inode routines Bob Copeland
` (2 preceding siblings ...)
2008-04-15 18:03 ` Miklos Szeredi
@ 2008-04-15 18:30 ` Marcin Slusarz
2008-04-15 23:56 ` Bob Copeland
3 siblings, 1 reply; 15+ messages in thread
From: Marcin Slusarz @ 2008-04-15 18:30 UTC (permalink / raw)
To: Bob Copeland; +Cc: linux-kernel, linux-fsdevel, akpm
On Sat, Apr 12, 2008 at 06:58:36PM -0400, Bob Copeland wrote:
> +static int omfs_fill_super(struct super_block *sb, void *data, int silent)
> +{
> + struct buffer_head *bh = NULL, *bh2 = NULL;
> + struct omfs_super_block *omfs_sb;
> + struct omfs_root_block *omfs_rb;
> + struct omfs_sb_info *sbi;
> + struct inode *root;
> + sector_t start;
> + int ret = 0;
> +
> + sbi = kzalloc(sizeof(struct omfs_sb_info), GFP_KERNEL);
> + if (!sbi)
> + return -ENOMEM;
> + sb->s_fs_info = sbi;
> +
> + sb->s_maxbytes = 0xffffffff;
> +
> + sb_set_blocksize(sb, 0x200);
> +
> + bh = sb_bread(sb, 0);
> + if (!bh)
> + goto out;
> +
> + omfs_sb = (struct omfs_super_block *)bh->b_data;
> +
> + if (be32_to_cpu(omfs_sb->s_magic) != OMFS_MAGIC) {
can be omfs_sb->s_magic != cpu_to_be32(OMFS_MAGIC)
(cpu_to_be32 can be optimized away even on LE)
> + if (!silent)
> + printk(KERN_ERR "omfs: Invalid superblock (%x)\n",
> + omfs_sb->s_magic);
> + goto out;
> + }
> + sb->s_magic = OMFS_MAGIC;
> +
> + sbi->s_num_blocks = be64_to_cpu(omfs_sb->s_num_blocks);
> + sbi->s_blocksize = be32_to_cpu(omfs_sb->s_blocksize);
> + sbi->s_mirrors = be32_to_cpu(omfs_sb->s_mirrors);
> + sbi->s_root_ino = be64_to_cpu(omfs_sb->s_root_block);
> + sbi->s_sys_blocksize = be32_to_cpu(omfs_sb->s_sys_blocksize);
> + mutex_init(&sbi->s_bitmap_lock);
> +
> + /*
> + * Use sys_blocksize as the fs block since it is smaller than a
> + * page while the fs blocksize can be larger.
> + */
> + sb_set_blocksize(sb, sbi->s_sys_blocksize);
> +
> + /*
> + * ...and the difference goes into a shift. sys_blocksize is always
> + * a power of two factor of blocksize.
> + */
> + set_block_shift(sbi);
> +
> + start = clus_to_blk(sbi, be64_to_cpu(omfs_sb->s_root_block));
> + bh2 = sb_bread(sb, start);
> + if (!bh2)
> + goto out;
> +
> + omfs_rb = (struct omfs_root_block *)bh2->b_data;
> +
> + sbi->s_bitmap_ino = be64_to_cpu(omfs_rb->r_bitmap);
> + sbi->s_clustersize = be32_to_cpu(omfs_rb->r_clustersize);
> +
> + ret = omfs_get_imap(sb);
> + if (ret)
> + goto end;
> +
> + sb->s_op = &omfs_sops;
> +
> + root = omfs_iget(sb, be64_to_cpu(omfs_rb->r_root_dir));
> + if (IS_ERR(root)) {
> + ret = PTR_ERR(root);
> + goto end;
> + }
> +
> + sb->s_root = d_alloc_root(root);
> + if (!sb->s_root) {
> + iput(root);
> + goto out;
> + }
> + printk(KERN_DEBUG "omfs: Mounted volume %s\n", omfs_rb->r_name);
> + goto end;
> +
> +out:
> + ret = -EINVAL;
> +
> +end:
> + if (bh2)
> + brelse(bh2);
> + if (bh)
> + brelse(bh);
brelse(NULL) is safe to call
> + return ret;
> +}
> +
this code leaks omfs_sbi_info (sbi)
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-04-15 18:03 ` Miklos Szeredi
@ 2008-04-15 18:33 ` Bob Copeland
0 siblings, 0 replies; 15+ messages in thread
From: Bob Copeland @ 2008-04-15 18:33 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: hch, linux-kernel, linux-fsdevel, akpm
On Tue, Apr 15, 2008 at 2:03 PM, Miklos Szeredi <miklos@szeredi.hu> wrote:
> Hmm, looks like error handling needs a makeover if this is really to
> become example code. See comments inline.
Thanks for the review Miklos.
> > + mark_buffer_dirty(bh);
> > + if (wait) {
> > + sync_dirty_buffer(bh);
> > + if (buffer_req(bh) && !buffer_uptodate(bh))
> > + ret = -EIO;
>
> Hmm, here it sets ret, but doesn't exit. Deliberate?
It was - if sync fails, it should still try writing the mirrors. Plus
bh and bh2 get released subsequently.
> > + iget_failed(inode);
> > + return ERR_PTR(-EIO);
>
> Should be:
>
> if (!bh)
> goto iget_failed;
Nod.
> > +out:
> > + ret = -EINVAL;
> > +
> > + if (bh)
> > + brelse(bh);
>
> This is weird. This should be done by jumping to the proper label
> between the brleses.
Hrm, brelse(NULL) is allowed so the check is suspect anyway. I did
this in a couple of other places, so I'll fix those up too.
Thanks!
--
Bob Copeland %% www.bobcopeland.com
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-04-15 18:30 ` Marcin Slusarz
@ 2008-04-15 23:56 ` Bob Copeland
2008-04-16 18:31 ` Marcin Slusarz
0 siblings, 1 reply; 15+ messages in thread
From: Bob Copeland @ 2008-04-15 23:56 UTC (permalink / raw)
To: Marcin Slusarz; +Cc: linux-kernel, linux-fsdevel, akpm
On Tue, Apr 15, 2008 at 08:30:59PM +0200, Marcin Slusarz wrote:
> > + if (be32_to_cpu(omfs_sb->s_magic) != OMFS_MAGIC) {
>
> can be omfs_sb->s_magic != cpu_to_be32(OMFS_MAGIC)
> (cpu_to_be32 can be optimized away even on LE)
Good point, I'll change that.
> > + if (bh)
> > + brelse(bh);
> brelse(NULL) is safe to call
Yup, I saw that too. I dropped these and added the labels per Miklos'
suggestion.
> > + return ret;
> > +}
>
> this code leaks omfs_sbi_info (sbi)
This one I'm not seeing. FS core calls put_super if ret is nonzero so
this should be ok?
Thanks!
--
Bob Copeland %% www.bobcopeland.com
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-04-15 23:56 ` Bob Copeland
@ 2008-04-16 18:31 ` Marcin Slusarz
0 siblings, 0 replies; 15+ messages in thread
From: Marcin Slusarz @ 2008-04-16 18:31 UTC (permalink / raw)
To: Bob Copeland; +Cc: linux-kernel, linux-fsdevel, akpm
On Tue, Apr 15, 2008 at 07:56:59PM -0400, Bob Copeland wrote:
> > > + return ret;
> > > +}
> >
> > this code leaks omfs_sbi_info (sbi)
>
> This one I'm not seeing. FS core calls put_super if ret is nonzero so
> this should be ok?
Yep, you are right. Sorry for the noise.
I was mislead by other filesystems code.
Marcin
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-03-28 20:20 ` Pavel Machek
@ 2008-03-30 3:21 ` Bob Copeland
0 siblings, 0 replies; 15+ messages in thread
From: Bob Copeland @ 2008-03-30 3:21 UTC (permalink / raw)
To: Pavel Machek, Andi Kleen, Arnd Bergmann; +Cc: linux-kernel, linux-fsdevel
Version 2 of this patch with these changes:
- remove private inode cache
- include GPL v2 notice
- mark unused bitmap bits as all-1s to simplify omfs_count_free
>From bed06457049cc81227d0b5c84529ee78dc77303f Mon Sep 17 00:00:00 2001
From: Bob Copeland <me@bobcopeland.com>
Date: Thu, 27 Mar 2008 17:23:03 -0400
Subject: [PATCH] omfs: add inode routines
Add basic superblock and inode handling routines for OMFS
Signed-off-by: Bob Copeland <me@bobcopeland.com>
---
fs/omfs/inode.c | 439 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 439 insertions(+), 0 deletions(-)
create mode 100644 fs/omfs/inode.c
diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c
new file mode 100644
index 0000000..f9131ec
--- /dev/null
+++ b/fs/omfs/inode.c
@@ -0,0 +1,439 @@
+/*
+ * Optimized MPEG FS - inode and super operations.
+ * Copyright (C) 2006 Bob Copeland <me@bobcopeland.com>
+ * Released under GPL v2.
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/buffer_head.h>
+#include <linux/vmalloc.h>
+#include "omfs.h"
+
+MODULE_AUTHOR("Bob Copeland <me@bobcopeland.com>");
+MODULE_DESCRIPTION("OMFS (ReplayTV/Karma) Filesystem for Linux");
+MODULE_LICENSE("GPL");
+
+struct inode *omfs_new_inode(struct inode *dir, int mode)
+{
+ struct inode *inode;
+ u64 new_block;
+ int res;
+ int len;
+ struct omfs_sb_info *sbi = OMFS_SB(dir->i_sb);
+
+ inode = new_inode(dir->i_sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ res = omfs_allocate_range(dir->i_sb, sbi->s_mirrors, sbi->s_mirrors,
+ &new_block, &len);
+ if (res)
+ return ERR_PTR(res);
+
+ inode->i_ino = new_block;
+ inode->i_mode = mode;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_blocks = 0;
+ inode->i_mapping->a_ops = &omfs_aops;
+
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ inode->i_op = &omfs_dir_inops;
+ inode->i_fop = &omfs_dir_operations;
+ inode->i_size = sbi->s_sys_blocksize;
+ inode->i_nlink++;
+ break;
+ case S_IFREG:
+ inode->i_op = &omfs_file_inops;
+ inode->i_fop = &omfs_file_operations;
+ inode->i_size = 0;
+ break;
+ }
+
+ insert_inode_hash(inode);
+ mark_inode_dirty(inode);
+ return inode;
+}
+
+static int omfs_write_inode(struct inode *inode, int wait)
+{
+ struct omfs_inode *oi;
+ struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
+ struct buffer_head *bh, *bh2;
+ unsigned int block;
+ u64 ctime;
+ int i;
+ int ret = 0;
+
+ /* get current inode since we may have written sibling ptrs etc. */
+ block = clus_to_blk(sbi, inode->i_ino);
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh) {
+ ret = -EIO;
+ goto out_nobh;
+ }
+
+ oi = (struct omfs_inode *) bh->b_data;
+
+ oi->i_head.h_self = cpu_to_be64(inode->i_ino);
+ if (S_ISDIR(inode->i_mode))
+ oi->i_type = OMFS_DIR;
+ else if (S_ISREG(inode->i_mode))
+ oi->i_type = OMFS_FILE;
+ else {
+ printk(KERN_WARNING "omfs: unknown file type: %d\n",
+ inode->i_mode);
+ ret = -EIO;
+ goto out;
+ }
+
+ oi->i_head.h_body_size = cpu_to_be32(sbi->s_sys_blocksize -
+ sizeof(struct omfs_header));
+ oi->i_head.h_version = 1;
+ oi->i_head.h_type = OMFS_INODE_NORMAL;
+ oi->i_head.h_magic = OMFS_IMAGIC;
+ oi->i_size = cpu_to_be64(inode->i_size);
+
+ ctime = inode->i_ctime.tv_sec * 1000LL +
+ ((inode->i_ctime.tv_nsec + 999)/1000);
+ oi->i_ctime = cpu_to_be64(ctime);
+
+ if (omfs_update_checksums(oi) != 0) {
+ ret = -EIO;
+ goto out;
+ }
+
+ mark_buffer_dirty(bh);
+ if (wait) {
+ sync_dirty_buffer(bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh))
+ ret = -EIO;
+ }
+
+ /* if mirroring writes, copy to next fsblock */
+ for (i = 1; i < sbi->s_mirrors; i++) {
+ bh2 = sb_bread(inode->i_sb, block + i *
+ (sbi->s_blocksize / sbi->s_sys_blocksize));
+ if (!bh2) {
+ brelse(bh2);
+ ret = -EIO;
+ goto out;
+ }
+ memcpy(bh2->b_data, bh->b_data, bh->b_size);
+ mark_buffer_dirty(bh2);
+ if (wait) {
+ sync_dirty_buffer(bh2);
+ if (buffer_req(bh2) && !buffer_uptodate(bh2))
+ ret = -EIO;
+ }
+ brelse(bh2);
+ }
+out:
+ brelse(bh);
+out_nobh:
+ return ret;
+}
+
+int omfs_sync_inode(struct inode *inode)
+{
+ return omfs_write_inode(inode, 1);
+}
+
+/*
+ * called when an entry is deleted, need to clear the bits in the
+ * bitmaps.
+ */
+static void omfs_delete_inode(struct inode *inode)
+{
+ truncate_inode_pages(&inode->i_data, 0);
+
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_size = 0;
+ omfs_shrink_inode(inode);
+ }
+
+ omfs_clear_range(inode->i_sb, inode->i_ino, 2);
+ clear_inode(inode);
+}
+
+struct inode *omfs_iget(struct super_block *sb, ino_t ino)
+{
+ struct omfs_inode *oi;
+ struct buffer_head *bh;
+ unsigned int block;
+ u64 ctime;
+ unsigned long nsecs;
+ struct inode *inode;
+
+ inode = iget_locked(sb, ino);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+
+ block = clus_to_blk(OMFS_SB(inode->i_sb), ino);
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh) {
+ iget_failed(inode);
+ return ERR_PTR(-EIO);
+ }
+
+ oi = (struct omfs_inode *)bh->b_data;
+
+ /* check self */
+ if (ino != be64_to_cpu(oi->i_head.h_self)) {
+ iget_failed(inode);
+ return ERR_PTR(-EIO);
+ }
+
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+
+ ctime = be64_to_cpu(oi->i_ctime);
+ nsecs = do_div(ctime, 1000) * 1000L;
+
+ inode->i_atime.tv_sec = ctime;
+ inode->i_mtime.tv_sec = ctime;
+ inode->i_ctime.tv_sec = ctime;
+ inode->i_atime.tv_nsec = nsecs;
+ inode->i_mtime.tv_nsec = nsecs;
+ inode->i_ctime.tv_nsec = nsecs;
+
+ inode->i_mapping->a_ops = &omfs_aops;
+
+ switch (oi->i_type) {
+ case OMFS_DIR:
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IWUGO | S_IXUGO;
+ inode->i_op = &omfs_dir_inops;
+ inode->i_fop = &omfs_dir_operations;
+ inode->i_size = be32_to_cpu(oi->i_head.h_body_size) +
+ sizeof(struct omfs_header);
+ inode->i_nlink++;
+ break;
+ case OMFS_FILE:
+ inode->i_mode = S_IFREG | S_IRUGO | S_IWUGO;
+ inode->i_fop = &omfs_file_operations;
+ inode->i_size = be64_to_cpu(oi->i_size);
+ break;
+ }
+ brelse(bh);
+ unlock_new_inode(inode);
+ return inode;
+}
+
+
+static void omfs_put_super(struct super_block *sb)
+{
+ struct omfs_sb_info *sbi = OMFS_SB(sb);
+ if (sbi) {
+ kfree(sbi->s_imap);
+ kfree(sbi);
+ }
+ sb->s_fs_info = NULL;
+}
+
+static int omfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct super_block *s = dentry->d_sb;
+ struct omfs_sb_info *sbi = OMFS_SB(s);
+ buf->f_type = OMFS_MAGIC;
+ buf->f_bsize = sbi->s_blocksize;
+ buf->f_blocks = sbi->s_num_blocks;
+ buf->f_files = sbi->s_num_blocks;
+ buf->f_namelen = OMFS_NAMELEN;
+
+ buf->f_bfree = buf->f_bavail = buf->f_ffree =
+ omfs_count_free(s);
+ return 0;
+}
+
+struct super_operations omfs_sops = {
+ .write_inode = omfs_write_inode,
+ .delete_inode = omfs_delete_inode,
+ .put_super = omfs_put_super,
+ .statfs = omfs_statfs,
+};
+
+/*
+ * For Rio Karma, there is an on-disk free bitmap whose location is
+ * stored in the root block. For ReplayTV, there is no such free bitmap
+ * so we have to walk the tree. Both inodes and file data are allocated
+ * from the same map. This array can be big (300k) so we allocate
+ * in units of the blocksize.
+ */
+static int omfs_get_imap(struct super_block *sb)
+{
+ int bitmap_size;
+ int array_size;
+ int count;
+ struct omfs_sb_info *sbi = OMFS_SB(sb);
+ struct buffer_head *bh;
+ unsigned long **ptr;
+ sector_t block;
+
+ bitmap_size = (sbi->s_num_blocks + 7) / 8;
+ array_size = (bitmap_size + sb->s_blocksize - 1) / sb->s_blocksize;
+
+ sbi->s_imap_size = array_size;
+ sbi->s_imap = kzalloc(array_size * sizeof(unsigned long *), GFP_KERNEL);
+ if (!sbi->s_imap)
+ return -ENOMEM;
+
+ block = clus_to_blk(sbi, sbi->s_bitmap_ino);
+ ptr = sbi->s_imap;
+ for (count = bitmap_size; count > 0; count -= sb->s_blocksize) {
+ bh = sb_bread(sb, block++);
+ if (!bh)
+ goto nomem;
+ *ptr = kmalloc(sb->s_blocksize, GFP_KERNEL);
+ if (!*ptr)
+ goto nomem;
+ memcpy(*ptr, bh->b_data, sb->s_blocksize);
+ if (count < sb->s_blocksize)
+ memset((void *)*ptr + count, 0xff, sb->s_blocksize - count);
+ brelse(bh);
+ ptr++;
+ }
+ return 0;
+nomem:
+ for (count = 0; count < array_size; count++)
+ kfree(sbi->s_imap[count]);
+
+ kfree(sbi->s_imap);
+ sbi->s_imap = NULL;
+ return -ENOMEM;
+}
+
+static void set_block_shift(struct omfs_sb_info *sbi)
+{
+ unsigned int scale = sbi->s_blocksize / sbi->s_sys_blocksize;
+ sbi->s_block_shift = 0;
+ for (scale >>= 1; scale; scale >>= 1)
+ sbi->s_block_shift++;
+}
+
+static int omfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct buffer_head *bh = NULL, *bh2 = NULL;
+ struct omfs_super_block *omfs_sb;
+ struct omfs_root_block *omfs_rb;
+ struct omfs_sb_info *sbi;
+ struct inode *root;
+ sector_t start;
+ int ret = 0;
+
+ sbi = kzalloc(sizeof(struct omfs_sb_info), GFP_KERNEL);
+ if (!sbi)
+ return -ENOMEM;
+ sb->s_fs_info = sbi;
+
+ sb->s_maxbytes = 0xffffffff;
+
+ sb_set_blocksize(sb, 0x200);
+
+ bh = sb_bread(sb, 0);
+ if (!bh)
+ goto out;
+
+ omfs_sb = (struct omfs_super_block *)bh->b_data;
+
+ if (be32_to_cpu(omfs_sb->s_magic) != OMFS_MAGIC) {
+ if (!silent)
+ printk(KERN_ERR "omfs: Invalid superblock (%x)\n",
+ omfs_sb->s_magic);
+ goto out;
+ }
+ sb->s_magic = OMFS_MAGIC;
+
+ sbi->s_num_blocks = be64_to_cpu(omfs_sb->s_num_blocks);
+ sbi->s_blocksize = be32_to_cpu(omfs_sb->s_blocksize);
+ sbi->s_mirrors = be32_to_cpu(omfs_sb->s_mirrors);
+ sbi->s_root_ino = be64_to_cpu(omfs_sb->s_root_block);
+ sbi->s_sys_blocksize = be32_to_cpu(omfs_sb->s_sys_blocksize);
+ mutex_init(&sbi->s_bitmap_lock);
+
+ /*
+ * Use sys_blocksize as the fs block since it is smaller than a
+ * page while the fs blocksize can be larger.
+ */
+ sb_set_blocksize(sb, sbi->s_sys_blocksize);
+
+ /*
+ * ...and the difference goes into a shift. sys_blocksize is always
+ * a power of two factor of blocksize.
+ */
+ set_block_shift(sbi);
+
+ start = clus_to_blk(sbi, be64_to_cpu(omfs_sb->s_root_block));
+ bh2 = sb_bread(sb, start);
+ if (!bh2)
+ goto out;
+
+ omfs_rb = (struct omfs_root_block *)bh2->b_data;
+
+ sbi->s_bitmap_ino = be64_to_cpu(omfs_rb->r_bitmap);
+ sbi->s_clustersize = be32_to_cpu(omfs_rb->r_clustersize);
+
+ ret = omfs_get_imap(sb);
+ if (ret)
+ goto end;
+
+ sb->s_op = &omfs_sops;
+
+ root = omfs_iget(sb, be64_to_cpu(omfs_rb->r_root_dir));
+ if (IS_ERR(root)) {
+ ret = PTR_ERR(root);
+ goto end;
+ }
+
+ sb->s_root = d_alloc_root(root);
+ if (!sb->s_root) {
+ iput(root);
+ goto out;
+ }
+ printk(KERN_DEBUG "omfs: Mounted volume %s\n", omfs_rb->r_name);
+ goto end;
+
+out:
+ ret = -EINVAL;
+
+end:
+ if (bh2)
+ brelse(bh2);
+ if (bh)
+ brelse(bh);
+ return ret;
+}
+
+static int omfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data, struct vfsmount *m)
+{
+ return get_sb_bdev(fs_type, flags, dev_name, data, omfs_fill_super, m);
+}
+
+static struct file_system_type omfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "omfs",
+ .get_sb = omfs_get_sb,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
+static int __init init_omfs_fs(void)
+{
+ return register_filesystem(&omfs_fs_type);
+}
+
+static void __exit exit_omfs_fs(void)
+{
+ unregister_filesystem(&omfs_fs_type);
+}
+
+module_init(init_omfs_fs);
+module_exit(exit_omfs_fs);
--
1.5.4.2.182.gb3092
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-03-27 0:45 Bob Copeland
2008-03-27 6:13 ` Andi Kleen
2008-03-28 3:42 ` Arnd Bergmann
@ 2008-03-28 20:20 ` Pavel Machek
2008-03-30 3:21 ` Bob Copeland
2 siblings, 1 reply; 15+ messages in thread
From: Pavel Machek @ 2008-03-28 20:20 UTC (permalink / raw)
To: Bob Copeland; +Cc: linux-kernel, linux-fsdevel
On Wed 2008-03-26 20:45:55, Bob Copeland wrote:
> Add basic superblock and inode handling routines for OMFS
>
> Signed-off-by: Bob Copeland <me@bobcopeland.com>
> ---
> fs/omfs/inode.c | 496 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 496 insertions(+), 0 deletions(-)
> create mode 100644 fs/omfs/inode.c
>
> diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c
> new file mode 100644
> index 0000000..b1cad5e
> --- /dev/null
> +++ b/fs/omfs/inode.c
> @@ -0,0 +1,496 @@
> +/*
> + * Optimized MPEG FS - inode and super operations.
> + * Copyright (C) 2006 Bob Copeland <me@bobcopeland.com>
> + */
If you have copyright, add GPL notice, too.
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-03-28 3:42 ` Arnd Bergmann
@ 2008-03-28 13:34 ` Bob Copeland
0 siblings, 0 replies; 15+ messages in thread
From: Bob Copeland @ 2008-03-28 13:34 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: linux-kernel, linux-fsdevel
On Thu, Mar 27, 2008 at 11:42 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Thursday 27 March 2008, Bob Copeland wrote:
> If you could avoid the i_state member in omfs_inode_info, you could get
> rid of your own inode cache entirely. Your i_state seems to be mostly
> write-only, why is it needed in your file system?
Err, yeah, it looks to be superfluous. I think it's a relic from when
I once did an initialization in omfs_write_inode.
Thanks,
Bob
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-03-27 0:45 Bob Copeland
2008-03-27 6:13 ` Andi Kleen
@ 2008-03-28 3:42 ` Arnd Bergmann
2008-03-28 13:34 ` Bob Copeland
2008-03-28 20:20 ` Pavel Machek
2 siblings, 1 reply; 15+ messages in thread
From: Arnd Bergmann @ 2008-03-28 3:42 UTC (permalink / raw)
To: Bob Copeland; +Cc: linux-kernel, linux-fsdevel
On Thursday 27 March 2008, Bob Copeland wrote:
> +static struct inode *omfs_alloc_inode(struct super_block *sb)
> +{
> + struct omfs_inode_info *oinf;
> + oinf = (struct omfs_inode_info *) kmem_cache_alloc(omfs_inode_cachep,
> + GFP_KERNEL);
> + if (!oinf)
> + return NULL;
> + return &oinf->vfs_inode;
> +}
If you could avoid the i_state member in omfs_inode_info, you could get
rid of your own inode cache entirely. Your i_state seems to be mostly
write-only, why is it needed in your file system?
Arnd <><
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-03-27 6:13 ` Andi Kleen
@ 2008-03-27 12:38 ` Bob Copeland
0 siblings, 0 replies; 15+ messages in thread
From: Bob Copeland @ 2008-03-27 12:38 UTC (permalink / raw)
To: Andi Kleen; +Cc: linux-kernel, linux-fsdevel
On Thu, Mar 27, 2008 at 07:13:50AM +0100, Andi Kleen wrote:
> Bob Copeland <me@bobcopeland.com> writes:
> > +
> > + bitmap_size = (sbi->s_num_blocks + 7) / 8;
> > + array_size = (bitmap_size + sb->s_blocksize - 1) / sb->s_blocksize;
> > +
> > + sbi->s_imap_size = array_size;
> > + sbi->s_imap = kzalloc(array_size * sizeof(unsigned long), GFP_KERNEL);
>
>
> If the array can be really 300k you should probably have a vmalloc fallback
> here.
bitmap_size will be 300k (20G / 8192 / 8), but array_size adds another
level of indirection so s_imap will be 300k/8192 segments, each of size
8192. Hmm, I guess that should be 'sizeof(unsigned long *)' above.
One might wonder why we keep it in memory anyway since we can read the
bitmap blocks on demand. The reason is that once the tree walk is added
for ReplayTV then it would use this same i_map. However, I've yet to
hear from interested RTV folks so it is currently read-only and there is
no tree walk for the bitmap.
--
Bob Copeland %% www.bobcopeland.com
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/7] omfs: add inode routines
2008-03-27 0:45 Bob Copeland
@ 2008-03-27 6:13 ` Andi Kleen
2008-03-27 12:38 ` Bob Copeland
2008-03-28 3:42 ` Arnd Bergmann
2008-03-28 20:20 ` Pavel Machek
2 siblings, 1 reply; 15+ messages in thread
From: Andi Kleen @ 2008-03-27 6:13 UTC (permalink / raw)
To: Bob Copeland; +Cc: linux-kernel, linux-fsdevel
Bob Copeland <me@bobcopeland.com> writes:
> +
> + bitmap_size = (sbi->s_num_blocks + 7) / 8;
> + array_size = (bitmap_size + sb->s_blocksize - 1) / sb->s_blocksize;
> +
> + sbi->s_imap_size = array_size;
> + sbi->s_imap = kzalloc(array_size * sizeof(unsigned long), GFP_KERNEL);
If the array can be really 300k you should probably have a vmalloc fallback
here.
-Andi
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 2/7] omfs: add inode routines
@ 2008-03-27 0:45 Bob Copeland
2008-03-27 6:13 ` Andi Kleen
` (2 more replies)
0 siblings, 3 replies; 15+ messages in thread
From: Bob Copeland @ 2008-03-27 0:45 UTC (permalink / raw)
To: linux-kernel; +Cc: linux-fsdevel, Bob Copeland
Add basic superblock and inode handling routines for OMFS
Signed-off-by: Bob Copeland <me@bobcopeland.com>
---
fs/omfs/inode.c | 496 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 496 insertions(+), 0 deletions(-)
create mode 100644 fs/omfs/inode.c
diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c
new file mode 100644
index 0000000..b1cad5e
--- /dev/null
+++ b/fs/omfs/inode.c
@@ -0,0 +1,496 @@
+/*
+ * Optimized MPEG FS - inode and super operations.
+ * Copyright (C) 2006 Bob Copeland <me@bobcopeland.com>
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/buffer_head.h>
+#include <linux/vmalloc.h>
+#include "omfs.h"
+
+MODULE_AUTHOR("Bob Copeland <me@bobcopeland.com>");
+MODULE_DESCRIPTION("OMFS (ReplayTV/Karma) Filesystem for Linux");
+MODULE_LICENSE("GPL");
+
+static struct kmem_cache *omfs_inode_cachep;
+
+static struct inode *omfs_alloc_inode(struct super_block *sb)
+{
+ struct omfs_inode_info *oinf;
+ oinf = (struct omfs_inode_info *) kmem_cache_alloc(omfs_inode_cachep,
+ GFP_KERNEL);
+ if (!oinf)
+ return NULL;
+ return &oinf->vfs_inode;
+}
+
+static void omfs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(omfs_inode_cachep, OMFS_I(inode));
+}
+
+static void init_once(struct kmem_cache *cachep, void *p)
+{
+ struct omfs_inode_info *oinf = (struct omfs_inode_info *) p;
+ inode_init_once(&oinf->vfs_inode);
+}
+
+static int init_inodecache(void)
+{
+ omfs_inode_cachep = kmem_cache_create("omfs_inode_cache",
+ sizeof(struct omfs_inode_info),
+ 0, (SLAB_RECLAIM_ACCOUNT |
+ SLAB_MEM_SPREAD),
+ init_once
+ );
+ if (!omfs_inode_cachep)
+ return -ENOMEM;
+ return 0;
+}
+
+static void destroy_inodecache(void)
+{
+ kmem_cache_destroy(omfs_inode_cachep);
+}
+
+struct inode *omfs_new_inode(struct inode *dir, int mode)
+{
+ struct inode *inode;
+ u64 new_block;
+ int res;
+ int len;
+ struct omfs_sb_info *sbi = OMFS_SB(dir->i_sb);
+
+ inode = new_inode(dir->i_sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ res = omfs_allocate_range(dir->i_sb, sbi->s_mirrors, sbi->s_mirrors,
+ &new_block, &len);
+ if (res)
+ return ERR_PTR(res);
+
+ inode->i_ino = new_block;
+ inode->i_mode = mode;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_blocks = 0;
+ inode->i_mapping->a_ops = &omfs_aops;
+
+ OMFS_I(inode)->i_state = OMFS_STATE_NEW;
+
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ inode->i_op = &omfs_dir_inops;
+ inode->i_fop = &omfs_dir_operations;
+ inode->i_size = sbi->s_sys_blocksize;
+ inode->i_nlink++;
+ break;
+ case S_IFREG:
+ inode->i_op = &omfs_file_inops;
+ inode->i_fop = &omfs_file_operations;
+ inode->i_size = 0;
+ break;
+ }
+
+ insert_inode_hash(inode);
+ mark_inode_dirty(inode);
+ return inode;
+}
+
+static int omfs_write_inode(struct inode *inode, int wait)
+{
+ struct omfs_inode *oi;
+ struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
+ struct buffer_head *bh, *bh2;
+ unsigned int block;
+ u64 ctime;
+ int i;
+ int ret = 0;
+
+ /* get current inode since we may have written sibling ptrs etc. */
+ block = clus_to_blk(sbi, inode->i_ino);
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh) {
+ ret = -EIO;
+ goto out_nobh;
+ }
+
+ oi = (struct omfs_inode *) bh->b_data;
+ if (OMFS_I(inode)->i_state & OMFS_STATE_NEW)
+ OMFS_I(inode)->i_state &= ~OMFS_STATE_NEW;
+
+ oi->i_head.h_self = cpu_to_be64(inode->i_ino);
+ if (S_ISDIR(inode->i_mode))
+ oi->i_type = OMFS_DIR;
+ else if (S_ISREG(inode->i_mode))
+ oi->i_type = OMFS_FILE;
+ else {
+ printk(KERN_WARNING "omfs: unknown file type: %d\n",
+ inode->i_mode);
+ ret = -EIO;
+ goto out;
+ }
+
+ oi->i_head.h_body_size = cpu_to_be32(sbi->s_sys_blocksize -
+ sizeof(struct omfs_header));
+ oi->i_head.h_version = 1;
+ oi->i_head.h_type = OMFS_INODE_NORMAL;
+ oi->i_head.h_magic = OMFS_IMAGIC;
+ oi->i_size = cpu_to_be64(inode->i_size);
+
+ ctime = inode->i_ctime.tv_sec * 1000LL +
+ ((inode->i_ctime.tv_nsec + 999)/1000);
+ oi->i_ctime = cpu_to_be64(ctime);
+
+ if (omfs_update_checksums(oi, inode->i_sb, inode->i_ino) != 0) {
+ ret = -EIO;
+ goto out;
+ }
+
+ mark_buffer_dirty(bh);
+ if (wait) {
+ sync_dirty_buffer(bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh))
+ ret = -EIO;
+ }
+
+ /* if mirroring writes, copy to next fsblock */
+ for (i = 1; i < sbi->s_mirrors; i++) {
+ bh2 = sb_bread(inode->i_sb, block + i *
+ (sbi->s_blocksize / sbi->s_sys_blocksize));
+ if (!bh2) {
+ brelse(bh2);
+ ret = -EIO;
+ goto out;
+ }
+ memcpy(bh2->b_data, bh->b_data, bh->b_size);
+ mark_buffer_dirty(bh2);
+ if (wait) {
+ sync_dirty_buffer(bh2);
+ if (buffer_req(bh2) && !buffer_uptodate(bh2))
+ ret = -EIO;
+ }
+ brelse(bh2);
+ }
+out:
+ brelse(bh);
+out_nobh:
+ return ret;
+}
+
+int omfs_sync_inode(struct inode *inode)
+{
+ return omfs_write_inode(inode, 1);
+}
+
+/*
+ * called when an entry is deleted, need to clear the bits in the
+ * bitmaps.
+ */
+static void omfs_delete_inode(struct inode *inode)
+{
+ truncate_inode_pages(&inode->i_data, 0);
+
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_size = 0;
+ omfs_shrink_inode(inode);
+ }
+
+ omfs_clear_range(inode->i_sb, inode->i_ino, 2);
+ clear_inode(inode);
+}
+
+struct inode *omfs_iget(struct super_block *sb, ino_t ino)
+{
+ struct omfs_inode *oi;
+ struct omfs_inode_info *oinf;
+ struct buffer_head *bh;
+ unsigned int block;
+ u64 ctime;
+ unsigned long nsecs;
+ struct inode *inode;
+
+ inode = iget_locked(sb, ino);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+
+ oinf = OMFS_I(inode);
+
+ block = clus_to_blk(OMFS_SB(inode->i_sb), ino);
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh) {
+ iget_failed(inode);
+ return ERR_PTR(-EIO);
+ }
+
+ oi = (struct omfs_inode *)bh->b_data;
+
+ /* check self */
+ if (ino != be64_to_cpu(oi->i_head.h_self)) {
+ iget_failed(inode);
+ return ERR_PTR(-EIO);
+ }
+
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+
+ ctime = be64_to_cpu(oi->i_ctime);
+ nsecs = do_div(ctime, 1000) * 1000L;
+
+ inode->i_atime.tv_sec = ctime;
+ inode->i_mtime.tv_sec = ctime;
+ inode->i_ctime.tv_sec = ctime;
+ inode->i_atime.tv_nsec = nsecs;
+ inode->i_mtime.tv_nsec = nsecs;
+ inode->i_ctime.tv_nsec = nsecs;
+
+ oinf->i_state = 0;
+ inode->i_mapping->a_ops = &omfs_aops;
+
+ switch (oi->i_type) {
+ case OMFS_DIR:
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IWUGO | S_IXUGO;
+ inode->i_op = &omfs_dir_inops;
+ inode->i_fop = &omfs_dir_operations;
+ inode->i_size = be32_to_cpu(oi->i_head.h_body_size) +
+ sizeof(struct omfs_header);
+ inode->i_nlink++;
+ break;
+ case OMFS_FILE:
+ inode->i_mode = S_IFREG | S_IRUGO | S_IWUGO;
+ inode->i_fop = &omfs_file_operations;
+ inode->i_size = be64_to_cpu(oi->i_size);
+ break;
+ }
+ brelse(bh);
+ unlock_new_inode(inode);
+ return inode;
+}
+
+
+static void omfs_put_super(struct super_block *sb)
+{
+ struct omfs_sb_info *sbi = OMFS_SB(sb);
+ if (sbi) {
+ kfree(sbi->s_imap);
+ kfree(sbi);
+ }
+ sb->s_fs_info = NULL;
+}
+
+static int omfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct super_block *s = dentry->d_sb;
+ struct omfs_sb_info *sbi = OMFS_SB(s);
+ buf->f_type = OMFS_MAGIC;
+ buf->f_bsize = sbi->s_blocksize;
+ buf->f_blocks = sbi->s_num_blocks;
+ buf->f_files = sbi->s_num_blocks;
+ buf->f_namelen = OMFS_NAMELEN;
+
+ buf->f_bfree = buf->f_bavail = buf->f_ffree =
+ omfs_count_free(s);
+ return 0;
+}
+
+struct super_operations omfs_sops = {
+ .alloc_inode = omfs_alloc_inode,
+ .destroy_inode = omfs_destroy_inode,
+ .write_inode = omfs_write_inode,
+ .delete_inode = omfs_delete_inode,
+ .put_super = omfs_put_super,
+ .statfs = omfs_statfs,
+};
+
+/*
+ * For Rio Karma, there is an on-disk free bitmap whose location is
+ * stored in the root block. For ReplayTV, there is no such free bitmap
+ * so we have to walk the tree. Both inodes and file data are allocated
+ * from the same map. This array can be big (300k) so we allocate
+ * in units of the blocksize.
+ */
+static int omfs_get_imap(struct super_block *sb)
+{
+ int bitmap_size;
+ int array_size;
+ int count;
+ struct omfs_sb_info *sbi = OMFS_SB(sb);
+ struct buffer_head *bh;
+ unsigned long **ptr;
+ sector_t block;
+
+ bitmap_size = (sbi->s_num_blocks + 7) / 8;
+ array_size = (bitmap_size + sb->s_blocksize - 1) / sb->s_blocksize;
+
+ sbi->s_imap_size = array_size;
+ sbi->s_imap = kzalloc(array_size * sizeof(unsigned long), GFP_KERNEL);
+ if (!sbi->s_imap)
+ return -ENOMEM;
+
+ block = clus_to_blk(sbi, sbi->s_bitmap_ino);
+ ptr = sbi->s_imap;
+ for (count = bitmap_size; count > 0; count -= sb->s_blocksize) {
+ bh = sb_bread(sb, block++);
+ if (!bh)
+ goto nomem;
+ *ptr = kmalloc(sb->s_blocksize, GFP_KERNEL);
+ if (!*ptr)
+ goto nomem;
+ memcpy(*ptr, bh->b_data, sb->s_blocksize);
+ brelse(bh);
+ ptr++;
+ }
+ return 0;
+nomem:
+ for (count = 0; count < array_size; count++)
+ kfree(sbi->s_imap[count]);
+
+ kfree(sbi->s_imap);
+ sbi->s_imap = NULL;
+ return -ENOMEM;
+}
+
+static void set_block_shift(struct omfs_sb_info *sbi)
+{
+ unsigned int scale = sbi->s_blocksize / sbi->s_sys_blocksize;
+ sbi->s_block_shift = 0;
+ for (scale >>= 1; scale; scale >>= 1)
+ sbi->s_block_shift++;
+}
+
+static int omfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct buffer_head *bh = NULL, *bh2 = NULL;
+ struct omfs_super_block *omfs_sb;
+ struct omfs_root_block *omfs_rb;
+ struct omfs_sb_info *sbi;
+ struct inode *root;
+ sector_t start;
+ int ret = 0;
+
+ sbi = kzalloc(sizeof(struct omfs_sb_info), GFP_KERNEL);
+ if (!sbi)
+ return -ENOMEM;
+ sb->s_fs_info = sbi;
+
+ sb->s_maxbytes = 0xffffffff;
+
+ sb_set_blocksize(sb, 0x200);
+
+ bh = sb_bread(sb, 0);
+ if (!bh)
+ goto out;
+
+ omfs_sb = (struct omfs_super_block *)bh->b_data;
+
+ if (be32_to_cpu(omfs_sb->s_magic) != OMFS_MAGIC) {
+ if (!silent)
+ printk(KERN_ERR "omfs: Invalid superblock (%x)\n",
+ omfs_sb->s_magic);
+ goto out;
+ }
+ sb->s_magic = OMFS_MAGIC;
+
+ sbi->s_num_blocks = be64_to_cpu(omfs_sb->s_num_blocks);
+ sbi->s_blocksize = be32_to_cpu(omfs_sb->s_blocksize);
+ sbi->s_mirrors = be32_to_cpu(omfs_sb->s_mirrors);
+ sbi->s_root_ino = be64_to_cpu(omfs_sb->s_root_block);
+ sbi->s_sys_blocksize = be32_to_cpu(omfs_sb->s_sys_blocksize);
+ mutex_init(&sbi->s_bitmap_lock);
+
+ /*
+ * Use sys_blocksize as the fs block since it is smaller than a
+ * page while the fs blocksize can be larger.
+ */
+ sb_set_blocksize(sb, sbi->s_sys_blocksize);
+
+ /*
+ * ...and the difference goes into a shift. sys_blocksize is always
+ * a power of two factor of blocksize.
+ */
+ set_block_shift(sbi);
+
+ start = clus_to_blk(sbi, be64_to_cpu(omfs_sb->s_root_block));
+ bh2 = sb_bread(sb, start);
+ if (!bh2)
+ goto out;
+
+ omfs_rb = (struct omfs_root_block *)bh2->b_data;
+
+ sbi->s_bitmap_ino = be64_to_cpu(omfs_rb->r_bitmap);
+ sbi->s_clustersize = be32_to_cpu(omfs_rb->r_clustersize);
+
+ ret = omfs_get_imap(sb);
+ if (ret)
+ goto end;
+
+ sb->s_op = &omfs_sops;
+
+ root = omfs_iget(sb, be64_to_cpu(omfs_rb->r_root_dir));
+ if (IS_ERR(root)) {
+ ret = PTR_ERR(root);
+ goto end;
+ }
+
+ sb->s_root = d_alloc_root(root);
+ if (!sb->s_root) {
+ iput(root);
+ goto out;
+ }
+ printk(KERN_DEBUG "omfs: Mounted volume %s\n", omfs_rb->r_name);
+ goto end;
+
+out:
+ ret = -EINVAL;
+
+end:
+ if (bh2)
+ brelse(bh2);
+ if (bh)
+ brelse(bh);
+ return ret;
+}
+
+static int omfs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data, struct vfsmount *m)
+{
+ return get_sb_bdev(fs_type, flags, dev_name, data, omfs_fill_super, m);
+}
+
+static struct file_system_type omfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "omfs",
+ .get_sb = omfs_get_sb,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
+static int __init init_omfs_fs(void)
+{
+ int err = init_inodecache();
+ if (err)
+ goto out;
+
+ err = register_filesystem(&omfs_fs_type);
+ if (err)
+ destroy_inodecache();
+out:
+ return err;
+}
+
+static void __exit exit_omfs_fs(void)
+{
+ unregister_filesystem(&omfs_fs_type);
+ destroy_inodecache();
+}
+
+module_init(init_omfs_fs);
+module_exit(exit_omfs_fs);
--
1.5.4.2.182.gb3092
^ permalink raw reply related [flat|nested] 15+ messages in thread
end of thread, other threads:[~2008-04-16 18:32 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-04-12 22:58 [PATCH 2/7] omfs: add inode routines Bob Copeland
2008-04-13 8:05 ` Christoph Hellwig
2008-04-13 9:17 ` Alan Cox
2008-04-15 18:03 ` Miklos Szeredi
2008-04-15 18:33 ` Bob Copeland
2008-04-15 18:30 ` Marcin Slusarz
2008-04-15 23:56 ` Bob Copeland
2008-04-16 18:31 ` Marcin Slusarz
-- strict thread matches above, loose matches on Subject: below --
2008-03-27 0:45 Bob Copeland
2008-03-27 6:13 ` Andi Kleen
2008-03-27 12:38 ` Bob Copeland
2008-03-28 3:42 ` Arnd Bergmann
2008-03-28 13:34 ` Bob Copeland
2008-03-28 20:20 ` Pavel Machek
2008-03-30 3:21 ` Bob Copeland
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).