From: Marco Stornelli <marco.stornelli@xxxxxxxxx> File operations. Signed-off-by: Marco Stornelli <marco.stornelli@xxxxxxxxx> --- diff --git a/fs/pramfs/file.c b/fs/pramfs/file.c new file mode 100644 index 0000000..05a4af4 --- /dev/null +++ b/fs/pramfs/file.c @@ -0,0 +1,326 @@ +/* + * BRIEF DESCRIPTION + * + * File operations for files. + * + * Copyright 2009-2010 Marco Stornelli <marco.stornelli@xxxxxxxxx> + * Copyright 2003 Sony Corporation + * Copyright 2003 Matsushita Electric Industrial Co., Ltd. + * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/uio.h> +#include <linux/mm.h> +#include <linux/uaccess.h> +#include "pram.h" +#include "acl.h" +#include "xip.h" +#include "xattr.h" + +/* + * The following functions are helper routines to copy to/from + * user space and iter over io vectors (mainly for readv/writev). + * They are used in the direct IO path. + */ +static size_t __pram_iov_copy_from(char *vaddr, + const struct iovec *iov, size_t base, size_t bytes) +{ + size_t copied = 0, left = 0; + + while (bytes) { + char __user *buf = iov->iov_base + base; + int copy = min(bytes, iov->iov_len - base); + + base = 0; + left = __copy_from_user(vaddr, buf, copy); + copied += copy; + bytes -= copy; + vaddr += copy; + iov++; + + if (unlikely(left)) + break; + } + return copied - left; +} + +static size_t __pram_iov_copy_to(char *vaddr, + const struct iovec *iov, size_t base, size_t bytes) +{ + size_t copied = 0, left = 0; + + while (bytes) { + char __user *buf = iov->iov_base + base; + int copy = min(bytes, iov->iov_len - base); + + base = 0; + left = __copy_to_user(buf, vaddr, copy); + copied += copy; + bytes -= copy; + vaddr += copy; + iov++; + + if (unlikely(left)) + break; + } + return copied - left; +} + +static size_t pram_iov_copy_from(void *to, struct iov_iter *i, size_t bytes) +{ + size_t copied; + + if (likely(i->nr_segs == 1)) { + int left; + char __user *buf = i->iov->iov_base + i->iov_offset; + left = __copy_from_user(to, buf, bytes); + copied = bytes - left; + } else { + copied = __pram_iov_copy_from(to, i->iov, i->iov_offset, bytes); + } + + return copied; +} + +static size_t pram_iov_copy_to(void *from, struct iov_iter *i, size_t bytes) +{ + size_t copied; + + if (likely(i->nr_segs == 1)) { + int left; + char __user *buf = i->iov->iov_base + i->iov_offset; + left = __copy_to_user(buf, from, bytes); + copied = bytes - left; + } else { + copied = __pram_iov_copy_to(from, i->iov, i->iov_offset, bytes); + } + + return copied; +} + +static size_t __pram_clear_user(const struct iovec *iov, size_t base, size_t bytes) +{ + size_t claened = 0, left = 0; + + while (bytes) { + char __user *buf = iov->iov_base + base; + int clear = min(bytes, iov->iov_len - base); + + base = 0; + left = __clear_user(buf, clear); + claened += clear; + bytes -= clear; + iov++; + + if (unlikely(left)) + break; + } + return claened - left; +} + +static size_t pram_clear_user(struct iov_iter *i, size_t bytes) +{ + size_t clear; + + if (likely(i->nr_segs == 1)) { + int left; + char __user *buf = i->iov->iov_base + i->iov_offset; + left = __clear_user(buf, bytes); + clear = bytes - left; + } else { + clear = __pram_clear_user(i->iov, i->iov_offset, bytes); + } + + return clear; +} + +static int pram_open_file(struct inode *inode, struct file *filp) +{ + filp->f_flags |= O_DIRECT; + return generic_file_open(inode, filp); +} + +ssize_t pram_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, + loff_t offset, unsigned long nr_segs) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_mapping->host; + struct super_block *sb = inode->i_sb; + int progress = 0, hole = 0, alloc_once = 1; + ssize_t retval = 0; + void *tmp = NULL; + unsigned long blocknr, blockoff, blocknr_start; + struct iov_iter iter; + int num_blocks, blocksize_mask; + size_t length = iov_length(iov, nr_segs); + + if (length < 0) + return -EINVAL; + if ((rw == READ) && (offset + length > inode->i_size)) + length = inode->i_size - offset; + if (!length) + goto out; + + blocksize_mask = (1 << sb->s_blocksize_bits) - 1; + /* find starting block number to access */ + blocknr = offset >> sb->s_blocksize_bits; + /* find starting offset within starting block */ + blockoff = offset & blocksize_mask; + /* find number of blocks to access */ + num_blocks = (blockoff + length + blocksize_mask) >> + sb->s_blocksize_bits; + blocknr_start = blocknr; + + if (rw == WRITE) { + /* prepare a temporary buffer to hold a user data block + for writing. */ + tmp = kmalloc(sb->s_blocksize, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + } + + iov_iter_init(&iter, iov, nr_segs, length, 0); + + while (length) { + int count; + u8 *bp = NULL; + u64 block = pram_find_data_block(inode, blocknr); + if (!block) { + if (alloc_once && rw == WRITE) { + /* + * Allocate the data blocks starting from blocknr + * to the end. + */ + retval = pram_alloc_blocks(inode, blocknr, + num_blocks - (blocknr - + blocknr_start)); + if (retval) + goto fail; + /* retry....*/ + block = pram_find_data_block(inode, blocknr); + BUG_ON(!block); + alloc_once = 0; + } else if (unlikely(rw == READ)) { + /* We are falling in a hole */ + hole = 1; + goto hole; + } + } + bp = (u8 *)pram_get_block(sb, block); + if (!bp) { + retval = -EACCES; + goto fail; + } + hole: + ++blocknr; + + count = blockoff + length > sb->s_blocksize ? + sb->s_blocksize - blockoff : length; + + if (rw == READ) { + if (unlikely(hole)) { + retval = pram_clear_user(&iter, count); + if (retval != count) { + retval = -EFAULT; + goto fail; + } + } else { + retval = pram_iov_copy_to(&bp[blockoff], &iter, + count); + if (retval != count) { + retval = -EFAULT; + goto fail; + } + } + } else { + retval = pram_iov_copy_from(tmp, &iter, count); + if (retval != count) { + retval = -EFAULT; + goto fail; + } + + pram_memunlock_block(inode->i_sb, bp); + memcpy(&bp[blockoff], tmp, count); + pram_memlock_block(inode->i_sb, bp); + } + + progress += count; + iov_iter_advance(&iter, count); + length -= count; + blockoff = 0; + hole = 0; + } + + retval = progress; + fail: + kfree(tmp); + out: + return retval; +} + +int pram_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* Only private mappings */ + if (vma->vm_flags & VM_SHARED) + return -EINVAL; + return generic_file_mmap(file, vma); +} + +static int pram_check_flags(int flags) +{ + if (!(flags & O_DIRECT)) + return -EINVAL; + + return 0; +} + +struct file_operations pram_file_operations = { + .llseek = generic_file_llseek, + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = pram_mmap, + .open = pram_open_file, + .fsync = noop_fsync, + .check_flags = pram_check_flags, + .unlocked_ioctl = pram_ioctl, + .splice_read = generic_file_splice_read, +#ifdef CONFIG_COMPAT + .compat_ioctl = pram_compat_ioctl, +#endif +}; + +#ifdef CONFIG_PRAMFS_XIP +struct file_operations pram_xip_file_operations = { + .llseek = generic_file_llseek, + .read = xip_file_read, + .write = xip_file_write, + .mmap = xip_file_mmap, + .open = generic_file_open, + .fsync = noop_fsync, + .unlocked_ioctl = pram_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = pram_compat_ioctl, +#endif +}; +#endif + +struct inode_operations pram_file_inode_operations = { +#ifdef CONFIG_PRAMFS_XATTR + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = pram_listxattr, + .removexattr = generic_removexattr, +#endif + .setattr = pram_notify_change, + .check_acl = pram_check_acl, + .fallocate = pram_fallocate, +}; -- 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