vfs_file.c 6.9 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
#include <linux/file.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/inet.h>
#include <linux/list.h>
35
#include <linux/pagemap.h>
36 37
#include <asm/uaccess.h>
#include <linux/idr.h>
38 39
#include <net/9p/9p.h>
#include <net/9p/client.h>
40 41 42 43

#include "v9fs.h"
#include "v9fs_vfs.h"
#include "fid.h"
44
#include "cache.h"
45

46 47
static const struct file_operations v9fs_cached_file_operations;

48 49 50 51 52 53 54 55 56
/**
 * 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)
{
57
	int err;
58 59 60
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	int omode;
61

62 63
	P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p \n", inode, file);
	v9ses = v9fs_inode2v9ses(inode);
64
	omode = v9fs_uflags2omode(file->f_flags, v9fs_proto_dotu(v9ses));
65 66 67 68 69 70 71
	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);
72
		if (err < 0) {
73 74 75
			p9_client_clunk(fid);
			return err;
		}
76
		if (omode & P9_OTRUNC) {
77
			i_size_write(inode, 0);
78 79
			inode->i_blocks = 0;
		}
80
		if ((file->f_flags & O_APPEND) && (!v9fs_proto_dotu(v9ses)))
81
			generic_file_llseek(file, 0, SEEK_END);
82
	}
83

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

#ifdef CONFIG_9P_FSCACHE
		v9fs_cache_inode_set_cookie(inode, file);
#endif
94 95
	}

96
	return 0;
97 98 99 100
}

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

static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
{
	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) && fl->fl_type != F_UNLCK)
118 119 120
		return -ENOLCK;

	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 126 127 128
	}

	return res;
}

/**
129
 * v9fs_file_readn - read from a file
E
Eric Van Hensbergen 已提交
130
 * @filp: file pointer to read
131
 * @data: data buffer to read data into
132
 * @udata: user data buffer to read data into
133 134 135 136
 * @count: size of buffer
 * @offset: offset at which to read data
 *
 */
137 138 139 140 141 142 143 144

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;

145
	P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid,
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 174 175 176 177 178 179
					(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
 *
 */

180
static ssize_t
181
v9fs_file_read(struct file *filp, char __user *udata, size_t count,
182
	       loff_t * offset)
183
{
184 185
	int ret;
	struct p9_fid *fid;
186

E
Eric Van Hensbergen 已提交
187
	P9_DPRINTK(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset);
188
	fid = filp->private_data;
189 190 191 192 193 194

	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);

195 196
	if (ret > 0)
		*offset += ret;
197

198
	return ret;
199 200 201
}

/**
202
 * v9fs_file_write - write to a file
E
Eric Van Hensbergen 已提交
203
 * @filp: file pointer to write
204 205 206 207 208 209 210
 * @data: data buffer to write data from
 * @count: size of buffer
 * @offset: offset at which to write data
 *
 */

static ssize_t
211 212
v9fs_file_write(struct file *filp, const char __user * data,
		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
	loff_t origin = *offset;
219
	unsigned long pg_start, pg_end;
220

221 222
	P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data,
		(int)count, (int)*offset);
223

224
	fid = filp->private_data;
225 226 227 228 229 230 231 232 233 234
	clnt = fid->clnt;

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

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

235
		n = p9_client_write(fid, NULL, data+total, origin+total,
236 237 238 239 240 241 242 243
									rsize);
		if (n <= 0)
			break;
		count -= n;
		total += n;
	} while (count > 0);

	if (total > 0) {
244 245
		pg_start = origin >> PAGE_CACHE_SHIFT;
		pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT;
246 247 248
		if (inode->i_mapping && inode->i_mapping->nrpages)
			invalidate_inode_pages2_range(inode->i_mapping,
						      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 255 256 257
	if (n < 0)
		return n;

	return total;
258 259
}

260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
static int v9fs_file_fsync(struct file *filp, struct dentry *dentry,
					int datasync)
{
	struct p9_fid *fid;
	struct p9_wstat wstat;
	int retval;

	P9_DPRINTK(P9_DEBUG_VFS, "filp %p dentry %p datasync %x\n", filp,
						dentry, datasync);

	fid = filp->private_data;
	v9fs_blank_wstat(&wstat);

	retval = p9_client_wstat(fid, &wstat);
	return retval;
}

277
static const struct file_operations v9fs_cached_file_operations = {
278 279 280 281 282 283 284
	.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,
285
	.mmap = generic_file_readonly_mmap,
286
	.fsync = v9fs_file_fsync,
287 288
};

289
const struct file_operations v9fs_file_operations = {
290 291 292 293 294 295
	.llseek = generic_file_llseek,
	.read = v9fs_file_read,
	.write = v9fs_file_write,
	.open = v9fs_file_open,
	.release = v9fs_dir_release,
	.lock = v9fs_file_lock,
296
	.mmap = generic_file_readonly_mmap,
297
	.fsync = v9fs_file_fsync,
298
};