linux/fs/9p/vfs_file.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/9p/vfs_file.c
   3 *
   4 * This file contians vfs file ops for 9P2000.
   5 *
   6 *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
   7 *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
   8 *
   9 *  This program is free software; you can redistribute it and/or modify
  10 *  it under the terms of the GNU General Public License version 2
  11 *  as published by the Free Software Foundation.
  12 *
  13 *  This program is distributed in the hope that it will be useful,
  14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 *  GNU General Public License for more details.
  17 *
  18 *  You should have received a copy of the GNU General Public License
  19 *  along with this program; if not, write to:
  20 *  Free Software Foundation
  21 *  51 Franklin Street, Fifth Floor
  22 *  Boston, MA  02111-1301  USA
  23 *
  24 */
  25
  26#include <linux/module.h>
  27#include <linux/errno.h>
  28#include <linux/fs.h>
  29#include <linux/sched.h>
  30#include <linux/file.h>
  31#include <linux/stat.h>
  32#include <linux/string.h>
  33#include <linux/inet.h>
  34#include <linux/list.h>
  35#include <linux/pagemap.h>
  36#include <asm/uaccess.h>
  37#include <linux/idr.h>
  38#include <net/9p/9p.h>
  39#include <net/9p/client.h>
  40
  41#include "v9fs.h"
  42#include "v9fs_vfs.h"
  43#include "fid.h"
  44#include "cache.h"
  45
  46static const struct file_operations v9fs_cached_file_operations;
  47
  48/**
  49 * v9fs_file_open - open a file (or directory)
  50 * @inode: inode to be opened
  51 * @file: file being opened
  52 *
  53 */
  54
  55int v9fs_file_open(struct inode *inode, struct file *file)
  56{
  57        int err;
  58        struct v9fs_session_info *v9ses;
  59        struct p9_fid *fid;
  60        int omode;
  61
  62        P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p \n", inode, file);
  63        v9ses = v9fs_inode2v9ses(inode);
  64        omode = v9fs_uflags2omode(file->f_flags, v9fs_extended(v9ses));
  65        fid = file->private_data;
  66        if (!fid) {
  67                fid = v9fs_fid_clone(file->f_path.dentry);
  68                if (IS_ERR(fid))
  69                        return PTR_ERR(fid);
  70
  71                err = p9_client_open(fid, omode);
  72                if (err < 0) {
  73                        p9_client_clunk(fid);
  74                        return err;
  75                }
  76                if (omode & P9_OTRUNC) {
  77                        i_size_write(inode, 0);
  78                        inode->i_blocks = 0;
  79                }
  80                if ((file->f_flags & O_APPEND) && (!v9fs_extended(v9ses)))
  81                        generic_file_llseek(file, 0, SEEK_END);
  82        }
  83
  84        file->private_data = fid;
  85        if ((fid->qid.version) && (v9ses->cache)) {
  86                P9_DPRINTK(P9_DEBUG_VFS, "cached");
  87                /* enable cached file options */
  88                if(file->f_op == &v9fs_file_operations)
  89                        file->f_op = &v9fs_cached_file_operations;
  90
  91#ifdef CONFIG_9P_FSCACHE
  92                v9fs_cache_inode_set_cookie(inode, file);
  93#endif
  94        }
  95
  96        return 0;
  97}
  98
  99/**
 100 * v9fs_file_lock - lock a file (or directory)
 101 * @filp: file to be locked
 102 * @cmd: lock command
 103 * @fl: file lock structure
 104 *
 105 * Bugs: this looks like a local only lock, we should extend into 9P
 106 *       by using open exclusive
 107 */
 108
 109static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
 110{
 111        int res = 0;
 112        struct inode *inode = filp->f_path.dentry->d_inode;
 113
 114        P9_DPRINTK(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
 115
 116        /* No mandatory locks */
 117        if (__mandatory_lock(inode))
 118                return -ENOLCK;
 119
 120        if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
 121                filemap_write_and_wait(inode->i_mapping);
 122                invalidate_mapping_pages(&inode->i_data, 0, -1);
 123        }
 124
 125        return res;
 126}
 127
 128/**
 129 * v9fs_file_readn - read from a file
 130 * @filp: file pointer to read
 131 * @data: data buffer to read data into
 132 * @udata: user data buffer to read data into
 133 * @count: size of buffer
 134 * @offset: offset at which to read data
 135 *
 136 */
 137
 138ssize_t
 139v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count,
 140               u64 offset)
 141{
 142        int n, total;
 143        struct p9_fid *fid = filp->private_data;
 144
 145        P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid,
 146                                        (long long unsigned) offset, count);
 147
 148        n = 0;
 149        total = 0;
 150        do {
 151                n = p9_client_read(fid, data, udata, offset, count);
 152                if (n <= 0)
 153                        break;
 154
 155                if (data)
 156                        data += n;
 157                if (udata)
 158                        udata += n;
 159
 160                offset += n;
 161                count -= n;
 162                total += n;
 163        } while (count > 0 && n == (fid->clnt->msize - P9_IOHDRSZ));
 164
 165        if (n < 0)
 166                total = n;
 167
 168        return total;
 169}
 170
 171/**
 172 * v9fs_file_read - read from a file
 173 * @filp: file pointer to read
 174 * @udata: user data buffer to read data into
 175 * @count: size of buffer
 176 * @offset: offset at which to read data
 177 *
 178 */
 179
 180static ssize_t
 181v9fs_file_read(struct file *filp, char __user *udata, size_t count,
 182               loff_t * offset)
 183{
 184        int ret;
 185        struct p9_fid *fid;
 186
 187        P9_DPRINTK(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset);
 188        fid = filp->private_data;
 189
 190        if (count > (fid->clnt->msize - P9_IOHDRSZ))
 191                ret = v9fs_file_readn(filp, NULL, udata, count, *offset);
 192        else
 193                ret = p9_client_read(fid, NULL, udata, *offset, count);
 194
 195        if (ret > 0)
 196                *offset += ret;
 197
 198        return ret;
 199}
 200
 201/**
 202 * v9fs_file_write - write to a file
 203 * @filp: file pointer to write
 204 * @data: data buffer to write data from
 205 * @count: size of buffer
 206 * @offset: offset at which to write data
 207 *
 208 */
 209
 210static ssize_t
 211v9fs_file_write(struct file *filp, const char __user * data,
 212                size_t count, loff_t * offset)
 213{
 214        int n, rsize, total = 0;
 215        struct p9_fid *fid;
 216        struct p9_client *clnt;
 217        struct inode *inode = filp->f_path.dentry->d_inode;
 218        int origin = *offset;
 219        unsigned long pg_start, pg_end;
 220
 221        P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data,
 222                (int)count, (int)*offset);
 223
 224        fid = filp->private_data;
 225        clnt = fid->clnt;
 226
 227        rsize = fid->iounit;
 228        if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
 229                rsize = clnt->msize - P9_IOHDRSZ;
 230
 231        do {
 232                if (count < rsize)
 233                        rsize = count;
 234
 235                n = p9_client_write(fid, NULL, data+total, origin+total,
 236                                                                        rsize);
 237                if (n <= 0)
 238                        break;
 239                count -= n;
 240                total += n;
 241        } while (count > 0);
 242
 243        if (total > 0) {
 244                pg_start = origin >> PAGE_CACHE_SHIFT;
 245                pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT;
 246                if (inode->i_mapping && inode->i_mapping->nrpages)
 247                        invalidate_inode_pages2_range(inode->i_mapping,
 248                                                      pg_start, pg_end);
 249                *offset += total;
 250                i_size_write(inode, i_size_read(inode) + total);
 251                inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9;
 252        }
 253
 254        if (n < 0)
 255                return n;
 256
 257        return total;
 258}
 259
 260static const struct file_operations v9fs_cached_file_operations = {
 261        .llseek = generic_file_llseek,
 262        .read = do_sync_read,
 263        .aio_read = generic_file_aio_read,
 264        .write = v9fs_file_write,
 265        .open = v9fs_file_open,
 266        .release = v9fs_dir_release,
 267        .lock = v9fs_file_lock,
 268        .mmap = generic_file_readonly_mmap,
 269};
 270
 271const struct file_operations v9fs_file_operations = {
 272        .llseek = generic_file_llseek,
 273        .read = v9fs_file_read,
 274        .write = v9fs_file_write,
 275        .open = v9fs_file_open,
 276        .release = v9fs_dir_release,
 277        .lock = v9fs_file_lock,
 278        .mmap = generic_file_readonly_mmap,
 279};
 280