Signed-off-by: Josef 'Jeff' Sipek <jeffpc@xxxxxxxxxxxxxx> --- fs/cmsfs/inode.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 269 insertions(+), 0 deletions(-) create mode 100644 fs/cmsfs/inode.c diff --git a/fs/cmsfs/inode.c b/fs/cmsfs/inode.c new file mode 100644 index 0000000..9c4d51b --- /dev/null +++ b/fs/cmsfs/inode.c @@ -0,0 +1,269 @@ +/* + * CMSFS + * + * (C) 2008 Josef 'Jeff' Sipek <jeffpc@xxxxxxxxxxxxxx> + * + * Based on cmsfs from 2.4.12-ac6: + * + * (C) 2001 Rick Troth <rtroth@xxxxxxx> + * (C) 2001 BMC Software, Inc., Houston, Texas, USA + * + */ + +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/pagemap.h> + +#include "cmsfs.h" + +/** + * cmsfs_inode_lookup - lookup a file in the directory + * @dir: directory + * @dentry: dentry to lookup + * @nd: (unused) + */ +static struct dentry *cmsfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct cmsfs_inode_info *cdir = CMSFS_I(dir); + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct page *page; + struct CMSFSFST *fst; + char outname[18]; + int i; + int err; + + if (dir->i_ino != CMSFS_DIRECTOR_INO) + return ERR_PTR(-ENOTDIR); + + page = NULL; + inode = NULL; + fst = NULL; + + for (i = 0; i < cdir->items; i++, fst++) { + if ((i * cdir->lrecl) % CMSFS_SB(sb)->blksz == 0) { + if (i) { + kunmap(page); + page_cache_release(page); + } + + page = read_mapping_page(dir->i_mapping, (i * cdir->lrecl) / + CMSFS_SB(sb)->blksz, NULL); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto out; + } + + fst = kmap(page); + if (PageError(page)) { + err = -EIO; + goto out_release; + } + } + + /* skipping .DIRECTOR and .ALLOCMAP, fill VFS dir with files */ + if (i < 2) + continue; + + munge_name(fst->FSTFNAME, outname); + + if (!strncmp(outname, dentry->d_name.name, 17)) { + inode = cmsfs_iget(sb, i + CMSFS_FIRST_INO); + break; + } + } + + kunmap(page); + page_cache_release(page); + return d_splice_alias(inode, dentry); + +out_release: + page_cache_release(page); +out: + return ERR_PTR(err); +} + +static struct inode_operations cmsfs_inode_ops = { + .lookup = cmsfs_lookup, + .setxattr = cmsfs_setxattr, + .getxattr = cmsfs_getxattr, + .removexattr = cmsfs_removexattr, + .listxattr = cmsfs_listxattr, +}; + +/** + * cmsfs_set_inode_size - calculate and set the inode size + * @inode: inode to work with + */ +static int cmsfs_set_inode_size(struct inode *inode) +{ + struct cmsfs_inode_info *cinode = CMSFS_I(inode); + + /* the default - for safety */ + inode->i_size = 0; + + /* if there are no records, then there ain't no bytes */ + if (cinode->items == 0) + return 0; + + /* how big is this file? "it depends" */ + switch (cinode->recfm) { + case RECFM_FIXED: + inode->i_size = cinode->lrecl * cinode->items; + break; + + case RECFM_VAR: + { + static int warned; + if (!warned) + printk(KERN_WARNING "cmsfs: recfm=v is" + " unsuported\n"); + warned=1; + } + break; + + default: + BUG(); + } + + return 0; +} + +/** + * read_inode - read an inode from DASD + * @inode: inode to fill in + */ +static int read_inode(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct page *page; + struct CMSFSFST *fst, *buf; + int err; + + if (inode->i_ino < CMSFS_FIRST_INO) + return -ENOENT; + + page=NULL; + buf=NULL; + + if (inode->i_ino != CMSFS_DIRECTOR_INO) { + /* read file block within the directory */ + unsigned long ino = inode->i_ino - CMSFS_FIRST_INO; + + page = read_mapping_page(sb->s_root->d_inode->i_mapping, + (ino * sizeof(struct CMSFSFST))/ + sb->s_blocksize, NULL); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto out; + } + + fst = kmap(page); + if (PageError(page)) { + err = -EIO; + goto out_release; + } + + fst = &fst[ino % (sb->s_blocksize / sizeof(struct CMSFSFST))]; + } else { + /* + * if we're reading the directory, we can't rely on + * sb->s_root->d_inode to be set up, therefore we are forced + * to do a block read + */ + buf = (struct CMSFSFST *)__get_free_page(GFP_KERNEL); + if(!buf) + return -ENOMEM; + + err = 0; + if (cmsfs_read_block(sb, buf, CMSFS_SB(sb)->origin-1, + sb->s_blocksize) != sb->s_blocksize) { + err = -EIO; + goto out; + } + + fst = buf; + } + + CMSFS_I(inode)->recfm = (enum recfm) fst->FSTRECFM; + CMSFS_I(inode)->lrecl = be32_to_cpu(fst->FSTLRECL); + CMSFS_I(inode)->origin = be32_to_cpu(fst->FSTFOP); + CMSFS_I(inode)->blocks = be32_to_cpu(fst->FSTADBC); + CMSFS_I(inode)->items = be32_to_cpu(fst->FSTAIC); + + /* levels of indirection and pointer size */ + CMSFS_I(inode)->level = fst->FSTNLVL; + CMSFS_I(inode)->psize = fst->FSTPTRSZ; + + CMSFS_I(inode)->ctime = mktime(hex2int(fst->FSTADATI[0]) + + (fst->FSTFLAGS & FSTCNTRY ? 2000 : 1900), + hex2int(fst->FSTADATI[1] - 1), + hex2int(fst->FSTADATI[2]), + hex2int(fst->FSTADATI[3]), + hex2int(fst->FSTADATI[4]), + hex2int(fst->FSTADATI[5])); + + err = 0; + +out: + if (inode->i_ino != CMSFS_DIRECTOR_INO) { + kunmap(page); +out_release: + page_cache_release(page); + } else + free_page((unsigned long)buf); + + return err; +} + +/** + * cmsfs_iget - get a filled-in inode based on inode number + * @sb: superblock to read inode from + * @ino: inode number to look up + */ +struct inode *cmsfs_iget(struct super_block *sb, unsigned long ino) +{ + struct inode *inode; + int err = 0; + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + if ((err = read_inode(inode))) + goto bad_inode; + + if (cmsfs_set_inode_size(inode)) + goto bad_inode; + + if (inode->i_ino == CMSFS_DIRECTOR_INO) + inode->i_mode = S_IRWXUGO | S_IFDIR; + else + inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_nlink = 1; + inode->i_blocks = CMSFS_I(inode)->blocks; + inode->i_atime.tv_sec = CMSFS_I(inode)->ctime; + inode->i_ctime.tv_sec = CMSFS_I(inode)->ctime; + inode->i_mtime.tv_sec = CMSFS_I(inode)->ctime; + inode->i_atime.tv_nsec = 0; + inode->i_ctime.tv_nsec = 0; + inode->i_mtime.tv_nsec = 0; + + inode->i_generation = 0; + + inode->i_op = &cmsfs_inode_ops; + inode->i_fop = &cmsfs_file_ops; + inode->i_mapping->a_ops = &cmsfs_aops; + + unlock_new_inode(inode); + return inode; + +bad_inode: + iget_failed(inode); + return ERR_PTR(err); +} -- 1.5.6.3 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html