vfs_file.c 6.32 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
44

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

45
46
static const struct file_operations v9fs_cached_file_operations;

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

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

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

91
	return 0;
92
93
94
95
}

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

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

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

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

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

	return res;
}

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

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;

140
	P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid,
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
174
					(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
 *
 */

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

Eric Van Hensbergen's avatar
Eric Van Hensbergen committed
182
	P9_DPRINTK(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset);
183
	fid = filp->private_data;
184
185
186
187
188
189

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

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

193
	return ret;
194
195
196
}

/**
197
 * v9fs_file_write - write to a file
198
 * @filp: file pointer to write
199
200
201
202
203
204
205
 * @data: data buffer to write data from
 * @count: size of buffer
 * @offset: offset at which to write data
 *
 */

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

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

219
	fid = filp->private_data;
220
221
222
223
224
225
226
227
228
229
	clnt = fid->clnt;

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

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

230
		n = p9_client_write(fid, NULL, data+total, origin+total,
231
232
233
234
235
236
237
238
									rsize);
		if (n <= 0)
			break;
		count -= n;
		total += n;
	} while (count > 0);

	if (total > 0) {
239
240
241
242
		pg_start = origin >> PAGE_CACHE_SHIFT;
		pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT;
		invalidate_inode_pages2_range(inode->i_mapping, pg_start,
								pg_end);
243
		*offset += total;
244
		i_size_write(inode, i_size_read(inode) + total);
245
		inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9;
246
247
	}

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

	return total;
252
253
}

254
static const struct file_operations v9fs_cached_file_operations = {
255
256
257
258
259
260
261
	.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,
262
	.mmap = generic_file_readonly_mmap,
263
264
};

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