vfs_file.c 6.2 KB
Newer Older
1 2 3 4 5 6 7 8 9
/*
 *  linux/fs/9p/vfs_file.c
 *
 * This file contians vfs file ops for 9P2000.
 *
 *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
 *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
 *
 *  This program is free software; you can redistribute it and/or modify
10 11
 *  it under the terms of the GNU General Public License version 2
 *  as published by the Free Software Foundation.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to:
 *  Free Software Foundation
 *  51 Franklin Street, Fifth Floor
 *  Boston, MA  02111-1301  USA
 *
 */

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
29
#include <linux/sched.h>
30 31 32 33 34 35 36
#include <linux/file.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/inet.h>
#include <linux/list.h>
#include <asm/uaccess.h>
#include <linux/idr.h>
37 38
#include <net/9p/9p.h>
#include <net/9p/client.h>
39 40 41 42 43

#include "v9fs.h"
#include "v9fs_vfs.h"
#include "fid.h"

44 45
static const struct file_operations v9fs_cached_file_operations;

46 47 48 49 50 51 52 53 54
/**
 * v9fs_file_open - open a file (or directory)
 * @inode: inode to be opened
 * @file: file being opened
 *
 */

int v9fs_file_open(struct inode *inode, struct file *file)
{
55
	int err;
56 57 58
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	int omode;
59

60 61
	P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p \n", inode, file);
	v9ses = v9fs_inode2v9ses(inode);
62
	omode = v9fs_uflags2omode(file->f_flags, v9fs_extended(v9ses));
63 64 65 66 67 68 69
	fid = file->private_data;
	if (!fid) {
		fid = v9fs_fid_clone(file->f_path.dentry);
		if (IS_ERR(fid))
			return PTR_ERR(fid);

		err = p9_client_open(fid, omode);
70
		if (err < 0) {
71 72 73
			p9_client_clunk(fid);
			return err;
		}
74
		if (omode & P9_OTRUNC) {
75
			i_size_write(inode, 0);
76 77
			inode->i_blocks = 0;
		}
78 79
		if ((file->f_flags & O_APPEND) && (!v9fs_extended(v9ses)))
			generic_file_llseek(file, 0, SEEK_END);
80
	}
81

82 83 84
	file->private_data = fid;
	if ((fid->qid.version) && (v9ses->cache)) {
		P9_DPRINTK(P9_DEBUG_VFS, "cached");
85 86 87 88 89
		/* enable cached file options */
		if(file->f_op == &v9fs_file_operations)
			file->f_op = &v9fs_cached_file_operations;
	}

90
	return 0;
91 92 93 94
}

/**
 * v9fs_file_lock - lock a file (or directory)
E
Eric Van Hensbergen 已提交
95 96 97
 * @filp: file to be locked
 * @cmd: lock command
 * @fl: file lock structure
98
 *
E
Eric Van Hensbergen 已提交
99
 * Bugs: this looks like a local only lock, we should extend into 9P
100 101 102 103 104 105
 *       by using open exclusive
 */

static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
{
	int res = 0;
106
	struct inode *inode = filp->f_path.dentry->d_inode;
107

108
	P9_DPRINTK(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
109 110

	/* No mandatory locks */
111
	if (__mandatory_lock(inode))
112 113 114
		return -ENOLCK;

	if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
115
		filemap_write_and_wait(inode->i_mapping);
116
		invalidate_mapping_pages(&inode->i_data, 0, -1);
117 118 119 120 121 122
	}

	return res;
}

/**
123
 * v9fs_file_readn - read from a file
E
Eric Van Hensbergen 已提交
124
 * @filp: file pointer to read
125
 * @data: data buffer to read data into
126
 * @udata: user data buffer to read data into
127 128 129 130
 * @count: size of buffer
 * @offset: offset at which to read data
 *
 */
131 132 133 134 135 136 137 138

ssize_t
v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count,
	       u64 offset)
{
	int n, total;
	struct p9_fid *fid = filp->private_data;

139
	P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid,
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
					(long long unsigned) offset, count);

	n = 0;
	total = 0;
	do {
		n = p9_client_read(fid, data, udata, offset, count);
		if (n <= 0)
			break;

		if (data)
			data += n;
		if (udata)
			udata += n;

		offset += n;
		count -= n;
		total += n;
	} while (count > 0 && n == (fid->clnt->msize - P9_IOHDRSZ));

	if (n < 0)
		total = n;

	return total;
}

/**
 * v9fs_file_read - read from a file
 * @filp: file pointer to read
 * @udata: user data buffer to read data into
 * @count: size of buffer
 * @offset: offset at which to read data
 *
 */

174
static ssize_t
175
v9fs_file_read(struct file *filp, char __user *udata, size_t count,
176
	       loff_t * offset)
177
{
178 179
	int ret;
	struct p9_fid *fid;
180

E
Eric Van Hensbergen 已提交
181
	P9_DPRINTK(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset);
182
	fid = filp->private_data;
183 184 185 186 187 188

	if (count > (fid->clnt->msize - P9_IOHDRSZ))
		ret = v9fs_file_readn(filp, NULL, udata, count, *offset);
	else
		ret = p9_client_read(fid, NULL, udata, *offset, count);

189 190
	if (ret > 0)
		*offset += ret;
191

192
	return ret;
193 194 195
}

/**
196
 * v9fs_file_write - write to a file
E
Eric Van Hensbergen 已提交
197
 * @filp: file pointer to write
198 199 200 201 202 203 204
 * @data: data buffer to write data from
 * @count: size of buffer
 * @offset: offset at which to write data
 *
 */

static ssize_t
205 206
v9fs_file_write(struct file *filp, const char __user * data,
		size_t count, loff_t * offset)
207
{
208
	int n, rsize, total = 0;
209
	struct p9_fid *fid;
210
	struct p9_client *clnt;
211
	struct inode *inode = filp->f_path.dentry->d_inode;
212
	int origin = *offset;
213

214 215
	P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data,
		(int)count, (int)*offset);
216

217
	fid = filp->private_data;
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
	clnt = fid->clnt;

	rsize = fid->iounit;
	if (!rsize || rsize > clnt->msize-P9_IOHDRSZ)
		rsize = clnt->msize - P9_IOHDRSZ;

	do {
		if (count < rsize)
			rsize = count;

		n = p9_client_write(fid, NULL, data+total, *offset+total,
									rsize);
		if (n <= 0)
			break;
		count -= n;
		total += n;
	} while (count > 0);

	if (total > 0) {
		invalidate_inode_pages2_range(inode->i_mapping, origin,
								origin+total);
		*offset += total;
240
	}
241

242 243 244
	if (*offset > i_size_read(inode)) {
		i_size_write(inode, *offset);
		inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9;
245 246
	}

247 248 249 250
	if (n < 0)
		return n;

	return total;
251 252
}

253
static const struct file_operations v9fs_cached_file_operations = {
254 255 256 257 258 259 260
	.llseek = generic_file_llseek,
	.read = do_sync_read,
	.aio_read = generic_file_aio_read,
	.write = v9fs_file_write,
	.open = v9fs_file_open,
	.release = v9fs_dir_release,
	.lock = v9fs_file_lock,
261
	.mmap = generic_file_readonly_mmap,
262 263
};

264
const struct file_operations v9fs_file_operations = {
265 266 267 268 269 270
	.llseek = generic_file_llseek,
	.read = v9fs_file_read,
	.write = v9fs_file_write,
	.open = v9fs_file_open,
	.release = v9fs_dir_release,
	.lock = v9fs_file_lock,
271
	.mmap = generic_file_readonly_mmap,
272
};