From mboxrd@z Thu Jan 1 00:00:00 1970 From: Bob Copeland Subject: [RFC][PATCH 2/7] omfs: inode and superblock routines Date: Wed, 15 Mar 2006 22:01:44 -0500 Message-ID: <1142478104752-git-send-email-me@bobcopeland.com> References: <11424781043331-git-send-email-me@bobcopeland.com> Reply-To: Bob Copeland Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7BIT Cc: Bob Copeland Return-path: Received: from mail.deathmatch.net ([216.200.85.210]:35812 "EHLO mail.deathmatch.net") by vger.kernel.org with ESMTP id S1752154AbWCPDBr (ORCPT ); Wed, 15 Mar 2006 22:01:47 -0500 In-Reply-To: <11424781043331-git-send-email-me@bobcopeland.com> To: linux-fsdevel@vger.kernel.org Sender: linux-fsdevel-owner@vger.kernel.org List-Id: linux-fsdevel.vger.kernel.org This patch contains routines for reading and writing OMFS inodes and the filesystem superblock. --- fs/omfs/inode.c | 453 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 453 insertions(+), 0 deletions(-) create mode 100644 fs/omfs/inode.c 5a57cd0c9df8444bb16354bddf0142744f672b08 diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c new file mode 100644 index 0000000..ac0febb --- /dev/null +++ b/fs/omfs/inode.c @@ -0,0 +1,453 @@ +/* + * Optimized MPEG FS - inode and super operations. + * Copyright (C) 2005 Bob Copeland + */ +#include +#include +#include +#include +#include +#include +#include "omfs.h" + +MODULE_AUTHOR("Bob Copeland "); +MODULE_DESCRIPTION("OMFS (ReplayTV/Karma) Filesystem for Linux"); +MODULE_LICENSE("GPL"); + +static kmem_cache_t *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, + SLAB_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(void *p, kmem_cache_t *cachep, unsigned long flags) +{ + struct omfs_inode_info *oinf = (struct omfs_inode_info *) p; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + 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, + init_once, NULL); + if (!omfs_inode_cachep) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(omfs_inode_cachep)) + printk(KERN_INFO "omfs_inode_cache: not all structures were freed\n"); +} + +#ifdef OMFS_WRITE +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_blksize = PAGE_SIZE; + 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++; /* "." entry */ + break; + case S_IFREG: + inode->i_fop = &omfs_file_operations; + inode->i_size = 0; + break; + } + + insert_inode_hash(inode); + printk(KERN_DEBUG "omfs: new marked dirty: %lx\n", inode->i_ino); + 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; + + printk(KERN_DEBUG "omfs: writing inode %lx\n", inode->i_ino); + + /* 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) + return -EIO; + + oi = (struct omfs_inode *) bh->b_data; + if (OMFS_I(inode)->i_state & OMFS_STATE_NEW) + { + memset(oi, 0, sizeof(struct omfs_header)); + 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 + BUG(); + + 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) + return -EIO; + + mark_buffer_dirty(bh); + brelse(bh); + + // if mirroring writes, copy to next fsblock + for (i = 0; i < sbi->s_mirrors; i++) + { + bh = sb_bread(inode->i_sb, block); + if (!bh) + return -EIO; + bh2 = sb_bread(inode->i_sb, block + i * + (sbi->s_blocksize / sbi->s_sys_blocksize)); + if (!bh2) + { + brelse(bh); + return -EIO; + } + memcpy(bh2->b_data, bh->b_data, bh->b_size); + mark_buffer_dirty(bh2); + brelse(bh); + brelse(bh2); + } + + // do_sync? + return 0; +} +#endif + +void omfs_read_inode(struct inode *inode) +{ + struct omfs_inode *oi; + struct omfs_inode_info *oinf = OMFS_I(inode); + struct buffer_head *bh; + unsigned int block; + u64 ctime; + unsigned long nsecs; + ino_t ino = inode->i_ino; + + // check against s_num_blocks + block = clus_to_blk(OMFS_SB(inode->i_sb), ino); + bh = sb_bread(inode->i_sb, block); + if (!bh) { + make_bad_inode(inode); + return; + } + + oi = (struct omfs_inode *)bh->b_data; + + // check self + if (ino != be64_to_cpu(oi->i_head.h_self)) { + make_bad_inode(inode); + return; + } + + 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; + + if (oi->i_type == 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); + } else if (oi->i_type == OMFS_FILE) { + inode->i_mode = S_IFREG | S_IRUGO; + inode->i_mapping->a_ops = &omfs_aops; + inode->i_fop = &omfs_file_operations; + inode->i_size = be64_to_cpu(oi->i_size); + } + brelse(bh); +} + + +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 super_block *s, struct kstatfs *buf) +{ + 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, +#ifdef OMFS_WRITE + .write_inode = omfs_write_inode, +#endif + .read_inode = omfs_read_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; + + printk(KERN_DEBUG "omfs: trying to alloc %d bytes\n", array_size); + printk(KERN_DEBUG "omfs: bitmap size: %d / blocksize %ld \n", + bitmap_size, 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); + memcpy(*ptr, bh->b_data, sb->s_blocksize); + brelse(bh); + ptr ++; + } + return 0; +nomem: + for (count=0; counts_imap[count]); + } + printk(KERN_DEBUG "Freeing imap\n"); + 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; + +#ifndef OMFS_WRITE + sb->s_flags |= MS_RDONLY; +#endif + sb->s_maxbytes = 0xffffffff; + + sb_set_blocksize(sb, 0x200); + + if (!(bh = sb_bread(sb, 0))) + 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); + + // 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)); + if (!(bh2 = sb_bread(sb, start))) + goto out; + + omfs_rb = (struct omfs_root_block *)bh2->b_data; + printk(KERN_DEBUG "omfs: volume: %s\n", omfs_rb->r_name); + + 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 = iget(sb, be64_to_cpu(omfs_rb->r_root_dir)); + + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + iput(root); + goto out; + } + goto end; + +out: + ret = -EINVAL; + +end: + if (bh2) + brelse(bh2); + if (bh) + brelse(bh); + return ret; +} + +static struct super_block *omfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, omfs_fill_super); +} + +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.2.1