Signed-off-by: Josef 'Jeff' Sipek <jeffpc@xxxxxxxxxxxxxx> --- fs/cmsfs/file.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 204 insertions(+), 0 deletions(-) create mode 100644 fs/cmsfs/file.c diff --git a/fs/cmsfs/file.c b/fs/cmsfs/file.c new file mode 100644 index 0000000..26f23f8 --- /dev/null +++ b/fs/cmsfs/file.c @@ -0,0 +1,204 @@ +/* + * 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 <linux/uaccess.h> + +#include "cmsfs.h" + +/** + * __read_recfm_fixed - read len bytes from file position pos + * @inode: inode to read from + * @buf: user buffer pointer to copy data to + * @len: length of the buffer + * @pos: position into the inode + */ +static ssize_t __read_recfm_fixed(struct inode *inode, char __user *buf, + size_t len, loff_t pos) +{ + struct cmsfs_inode_info *ci = CMSFS_I(inode); + struct super_block *sb = inode->i_sb; + struct page *page; + unsigned char *kaddr; + u32 blkoff; + size_t err = -EIO; + + BUG_ON(ci->recfm != RECFM_FIXED); + BUG_ON(sb->s_blocksize > PAGE_SIZE); + + page = read_mapping_page(inode->i_mapping, pos >> PAGE_SHIFT, NULL); + if (IS_ERR(page)) + return PTR_ERR(page); + + kaddr = kmap(page); + if (PageError(page)) + goto out; + + blkoff = pos & ~PAGE_MASK; + + /* won't read more than a page */ + len = min(len, PAGE_SIZE - blkoff); + + err = copy_to_user(buf, kaddr + blkoff, len); + if (!err) + err = len; + +out: + kunmap(page); + page_cache_release(page); + return err; +} + +/** + * cmsfs_file_read - read from a file + * @file: file to read from + * @buf: user buffer pointer for data + * @len: length of the buffer + * @ppos: pointer to the file offset to read at + */ +static ssize_t cmsfs_file_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + ssize_t ret; + + if ((*ppos < inode->i_size) && (*ppos + len > inode->i_size)) + len = inode->i_size - *ppos; + else if (*ppos >= inode->i_size) + return 0; + + switch (CMSFS_I(inode)->recfm) { + case RECFM_FIXED: + ret = __read_recfm_fixed(inode, buf, len, *ppos); + break; + case RECFM_VAR: + default: + ret = -EINVAL; + break; + } + + if (ret > 0) + *ppos = *ppos + ret; + + return ret; +} + +/** + * cmsfs_readdir - readdir + * @file: file to readdir + * @dirent: ? + * @filldir: readdir filler function ptr + */ +static int cmsfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct super_block *sb = inode->i_sb; + struct cmsfs_inode_info *cinode = CMSFS_I(inode); + struct CMSFSFST *fst; + struct page *page; + char outname[18]; + int i; + int err = 0; + + if (inode->i_ino != CMSFS_DIRECTOR_INO) + return -ENOTDIR; + + /* FIXME: this should go away, I think */ + if (file->f_pos != 0) + return 0; + + page = NULL; + fst = NULL; + file->f_pos = 0; + + for (i = 0; i < cinode->items; i++, fst++) { + if ((i * cinode->lrecl) % CMSFS_SB(sb)->blksz == 0) { + if (i) { + kunmap(page); + page_cache_release(page); + } + + page = read_mapping_page(inode->i_mapping, + (i * cinode->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, but using the dirent + * space for the '.' and '..' entries + */ + if (i < 2) { + err = filldir(dirent, i ? ".." : ".", + i ? 2 : 1, file->f_pos, + CMSFS_DIRECTOR_INO, + DT_DIR); + if (err) + break; + continue; + } + + munge_name(fst->FSTFNAME, outname); + + /* No subdirs so all files are regular */ + err = filldir(dirent, outname, strlen(outname), file->f_pos, + i + CMSFS_FIRST_INO, DT_REG); + if (err) + break; + + /* set the file possition */ + file->f_pos += cinode->lrecl; + } + + kunmap(page); +out_release: + page_cache_release(page); +out: + return err; +} + +/** + * cmsfs_file_open - open routine + * @inode: inode to read + * @file: file ptr to be associated with the inode + * + * NOTE: The only reason this exists is because it's a good place to prevent + * RECFM_VAR files being open, and later read. Once that's supported, this + * function should go away. + */ +static int cmsfs_file_open(struct inode *inode, struct file *file) +{ + if (CMSFS_I(inode)->recfm != RECFM_FIXED) { + printk("cmsfs: cannot open files with recfm != fixed\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +struct file_operations cmsfs_file_ops = { + .readdir = cmsfs_readdir, + .open = cmsfs_file_open, + .read = cmsfs_file_read, +}; -- 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