xref: /openbmc/linux/fs/9p/vfs_file.c (revision 46848de0)
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 <linux/utsname.h>
37 #include <asm/uaccess.h>
38 #include <linux/idr.h>
39 #include <net/9p/9p.h>
40 #include <net/9p/client.h>
41 
42 #include "v9fs.h"
43 #include "v9fs_vfs.h"
44 #include "fid.h"
45 #include "cache.h"
46 
47 /**
48  * v9fs_file_open - open a file (or directory)
49  * @inode: inode to be opened
50  * @file: file being opened
51  *
52  */
53 
54 int v9fs_file_open(struct inode *inode, struct file *file)
55 {
56 	int err;
57 	struct v9fs_session_info *v9ses;
58 	struct p9_fid *fid;
59 	int omode;
60 
61 	P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file);
62 	v9ses = v9fs_inode2v9ses(inode);
63 	if (v9fs_proto_dotl(v9ses))
64 		omode = file->f_flags;
65 	else
66 		omode = v9fs_uflags2omode(file->f_flags,
67 					v9fs_proto_dotu(v9ses));
68 	fid = file->private_data;
69 	if (!fid) {
70 		fid = v9fs_fid_clone(file->f_path.dentry);
71 		if (IS_ERR(fid))
72 			return PTR_ERR(fid);
73 
74 		err = p9_client_open(fid, omode);
75 		if (err < 0) {
76 			p9_client_clunk(fid);
77 			return err;
78 		}
79 		if (file->f_flags & O_TRUNC) {
80 			i_size_write(inode, 0);
81 			inode->i_blocks = 0;
82 		}
83 		if ((file->f_flags & O_APPEND) &&
84 			(!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)))
85 			generic_file_llseek(file, 0, SEEK_END);
86 	}
87 
88 	file->private_data = fid;
89 #ifdef CONFIG_9P_FSCACHE
90 	if (v9ses->cache)
91 		v9fs_cache_inode_set_cookie(inode, file);
92 #endif
93 	return 0;
94 }
95 
96 /**
97  * v9fs_file_lock - lock a file (or directory)
98  * @filp: file to be locked
99  * @cmd: lock command
100  * @fl: file lock structure
101  *
102  * Bugs: this looks like a local only lock, we should extend into 9P
103  *       by using open exclusive
104  */
105 
106 static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
107 {
108 	int res = 0;
109 	struct inode *inode = filp->f_path.dentry->d_inode;
110 
111 	P9_DPRINTK(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
112 
113 	/* No mandatory locks */
114 	if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
115 		return -ENOLCK;
116 
117 	if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
118 		filemap_write_and_wait(inode->i_mapping);
119 		invalidate_mapping_pages(&inode->i_data, 0, -1);
120 	}
121 
122 	return res;
123 }
124 
125 static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
126 {
127 	struct p9_flock flock;
128 	struct p9_fid *fid;
129 	uint8_t status;
130 	int res = 0;
131 	unsigned char fl_type;
132 
133 	fid = filp->private_data;
134 	BUG_ON(fid == NULL);
135 
136 	if ((fl->fl_flags & FL_POSIX) != FL_POSIX)
137 		BUG();
138 
139 	res = posix_lock_file_wait(filp, fl);
140 	if (res < 0)
141 		goto out;
142 
143 	/* convert posix lock to p9 tlock args */
144 	memset(&flock, 0, sizeof(flock));
145 	flock.type = fl->fl_type;
146 	flock.start = fl->fl_start;
147 	if (fl->fl_end == OFFSET_MAX)
148 		flock.length = 0;
149 	else
150 		flock.length = fl->fl_end - fl->fl_start + 1;
151 	flock.proc_id = fl->fl_pid;
152 	flock.client_id = utsname()->nodename;
153 	if (IS_SETLKW(cmd))
154 		flock.flags = P9_LOCK_FLAGS_BLOCK;
155 
156 	/*
157 	 * if its a blocked request and we get P9_LOCK_BLOCKED as the status
158 	 * for lock request, keep on trying
159 	 */
160 	for (;;) {
161 		res = p9_client_lock_dotl(fid, &flock, &status);
162 		if (res < 0)
163 			break;
164 
165 		if (status != P9_LOCK_BLOCKED)
166 			break;
167 		if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd))
168 			break;
169 		schedule_timeout_interruptible(P9_LOCK_TIMEOUT);
170 	}
171 
172 	/* map 9p status to VFS status */
173 	switch (status) {
174 	case P9_LOCK_SUCCESS:
175 		res = 0;
176 		break;
177 	case P9_LOCK_BLOCKED:
178 		res = -EAGAIN;
179 		break;
180 	case P9_LOCK_ERROR:
181 	case P9_LOCK_GRACE:
182 		res = -ENOLCK;
183 		break;
184 	default:
185 		BUG();
186 	}
187 
188 	/*
189 	 * incase server returned error for lock request, revert
190 	 * it locally
191 	 */
192 	if (res < 0 && fl->fl_type != F_UNLCK) {
193 		fl_type = fl->fl_type;
194 		fl->fl_type = F_UNLCK;
195 		res = posix_lock_file_wait(filp, fl);
196 		fl->fl_type = fl_type;
197 	}
198 out:
199 	return res;
200 }
201 
202 static int v9fs_file_getlock(struct file *filp, struct file_lock *fl)
203 {
204 	struct p9_getlock glock;
205 	struct p9_fid *fid;
206 	int res = 0;
207 
208 	fid = filp->private_data;
209 	BUG_ON(fid == NULL);
210 
211 	posix_test_lock(filp, fl);
212 	/*
213 	 * if we have a conflicting lock locally, no need to validate
214 	 * with server
215 	 */
216 	if (fl->fl_type != F_UNLCK)
217 		return res;
218 
219 	/* convert posix lock to p9 tgetlock args */
220 	memset(&glock, 0, sizeof(glock));
221 	glock.type = fl->fl_type;
222 	glock.start = fl->fl_start;
223 	if (fl->fl_end == OFFSET_MAX)
224 		glock.length = 0;
225 	else
226 		glock.length = fl->fl_end - fl->fl_start + 1;
227 	glock.proc_id = fl->fl_pid;
228 	glock.client_id = utsname()->nodename;
229 
230 	res = p9_client_getlock_dotl(fid, &glock);
231 	if (res < 0)
232 		return res;
233 	if (glock.type != F_UNLCK) {
234 		fl->fl_type = glock.type;
235 		fl->fl_start = glock.start;
236 		if (glock.length == 0)
237 			fl->fl_end = OFFSET_MAX;
238 		else
239 			fl->fl_end = glock.start + glock.length - 1;
240 		fl->fl_pid = glock.proc_id;
241 	} else
242 		fl->fl_type = F_UNLCK;
243 
244 	return res;
245 }
246 
247 /**
248  * v9fs_file_lock_dotl - lock a file (or directory)
249  * @filp: file to be locked
250  * @cmd: lock command
251  * @fl: file lock structure
252  *
253  */
254 
255 static int v9fs_file_lock_dotl(struct file *filp, int cmd, struct file_lock *fl)
256 {
257 	struct inode *inode = filp->f_path.dentry->d_inode;
258 	int ret = -ENOLCK;
259 
260 	P9_DPRINTK(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %s\n", filp,
261 				cmd, fl, filp->f_path.dentry->d_name.name);
262 
263 	/* No mandatory locks */
264 	if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
265 		goto out_err;
266 
267 	if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
268 		filemap_write_and_wait(inode->i_mapping);
269 		invalidate_mapping_pages(&inode->i_data, 0, -1);
270 	}
271 
272 	if (IS_SETLK(cmd) || IS_SETLKW(cmd))
273 		ret = v9fs_file_do_lock(filp, cmd, fl);
274 	else if (IS_GETLK(cmd))
275 		ret = v9fs_file_getlock(filp, fl);
276 	else
277 		ret = -EINVAL;
278 out_err:
279 	return ret;
280 }
281 
282 /**
283  * v9fs_file_flock_dotl - lock a file
284  * @filp: file to be locked
285  * @cmd: lock command
286  * @fl: file lock structure
287  *
288  */
289 
290 static int v9fs_file_flock_dotl(struct file *filp, int cmd,
291 	struct file_lock *fl)
292 {
293 	struct inode *inode = filp->f_path.dentry->d_inode;
294 	int ret = -ENOLCK;
295 
296 	P9_DPRINTK(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %s\n", filp,
297 				cmd, fl, filp->f_path.dentry->d_name.name);
298 
299 	/* No mandatory locks */
300 	if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
301 		goto out_err;
302 
303 	if (!(fl->fl_flags & FL_FLOCK))
304 		goto out_err;
305 
306 	if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
307 		filemap_write_and_wait(inode->i_mapping);
308 		invalidate_mapping_pages(&inode->i_data, 0, -1);
309 	}
310 	/* Convert flock to posix lock */
311 	fl->fl_owner = (fl_owner_t)filp;
312 	fl->fl_start = 0;
313 	fl->fl_end = OFFSET_MAX;
314 	fl->fl_flags |= FL_POSIX;
315 	fl->fl_flags ^= FL_FLOCK;
316 
317 	if (IS_SETLK(cmd) | IS_SETLKW(cmd))
318 		ret = v9fs_file_do_lock(filp, cmd, fl);
319 	else
320 		ret = -EINVAL;
321 out_err:
322 	return ret;
323 }
324 
325 /**
326  * v9fs_file_readn - read from a file
327  * @filp: file pointer to read
328  * @data: data buffer to read data into
329  * @udata: user data buffer to read data into
330  * @count: size of buffer
331  * @offset: offset at which to read data
332  *
333  */
334 
335 ssize_t
336 v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count,
337 	       u64 offset)
338 {
339 	int n, total, size;
340 	struct p9_fid *fid = filp->private_data;
341 
342 	P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid,
343 					(long long unsigned) offset, count);
344 
345 	n = 0;
346 	total = 0;
347 	size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ;
348 	do {
349 		n = p9_client_read(fid, data, udata, offset, count);
350 		if (n <= 0)
351 			break;
352 
353 		if (data)
354 			data += n;
355 		if (udata)
356 			udata += n;
357 
358 		offset += n;
359 		count -= n;
360 		total += n;
361 	} while (count > 0 && n == size);
362 
363 	if (n < 0)
364 		total = n;
365 
366 	return total;
367 }
368 
369 /**
370  * v9fs_file_read - read from a file
371  * @filp: file pointer to read
372  * @udata: user data buffer to read data into
373  * @count: size of buffer
374  * @offset: offset at which to read data
375  *
376  */
377 
378 static ssize_t
379 v9fs_file_read(struct file *filp, char __user *udata, size_t count,
380 	       loff_t * offset)
381 {
382 	int ret;
383 	struct p9_fid *fid;
384 	size_t size;
385 
386 	P9_DPRINTK(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset);
387 	fid = filp->private_data;
388 
389 	size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ;
390 	if (count > size)
391 		ret = v9fs_file_readn(filp, NULL, udata, count, *offset);
392 	else
393 		ret = p9_client_read(fid, NULL, udata, *offset, count);
394 
395 	if (ret > 0)
396 		*offset += ret;
397 
398 	return ret;
399 }
400 
401 /**
402  * v9fs_file_write - write to a file
403  * @filp: file pointer to write
404  * @data: data buffer to write data from
405  * @count: size of buffer
406  * @offset: offset at which to write data
407  *
408  */
409 
410 static ssize_t
411 v9fs_file_write(struct file *filp, const char __user * data,
412 		size_t count, loff_t * offset)
413 {
414 	ssize_t retval;
415 	size_t total = 0;
416 	int n;
417 	struct p9_fid *fid;
418 	struct p9_client *clnt;
419 	struct inode *inode = filp->f_path.dentry->d_inode;
420 	loff_t origin = *offset;
421 	unsigned long pg_start, pg_end;
422 
423 	P9_DPRINTK(P9_DEBUG_VFS, "data %p count %d offset %x\n", data,
424 		(int)count, (int)*offset);
425 
426 	fid = filp->private_data;
427 	clnt = fid->clnt;
428 
429 	retval = generic_write_checks(filp, &origin, &count, 0);
430 	if (retval)
431 		goto out;
432 
433 	retval = -EINVAL;
434 	if ((ssize_t) count < 0)
435 		goto out;
436 	retval = 0;
437 	if (!count)
438 		goto out;
439 
440 	do {
441 		n = p9_client_write(fid, NULL, data+total, origin+total, count);
442 		if (n <= 0)
443 			break;
444 		count -= n;
445 		total += n;
446 	} while (count > 0);
447 
448 	if (total > 0) {
449 		pg_start = origin >> PAGE_CACHE_SHIFT;
450 		pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT;
451 		if (inode->i_mapping && inode->i_mapping->nrpages)
452 			invalidate_inode_pages2_range(inode->i_mapping,
453 						      pg_start, pg_end);
454 		*offset += total;
455 		i_size_write(inode, i_size_read(inode) + total);
456 		inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9;
457 	}
458 
459 	if (n < 0)
460 		retval = n;
461 	else
462 		retval = total;
463 out:
464 	return retval;
465 }
466 
467 static int v9fs_file_fsync(struct file *filp, int datasync)
468 {
469 	struct p9_fid *fid;
470 	struct p9_wstat wstat;
471 	int retval;
472 
473 	P9_DPRINTK(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
474 
475 	fid = filp->private_data;
476 	v9fs_blank_wstat(&wstat);
477 
478 	retval = p9_client_wstat(fid, &wstat);
479 	return retval;
480 }
481 
482 int v9fs_file_fsync_dotl(struct file *filp, int datasync)
483 {
484 	struct p9_fid *fid;
485 	int retval;
486 
487 	P9_DPRINTK(P9_DEBUG_VFS, "v9fs_file_fsync_dotl: filp %p datasync %x\n",
488 			filp, datasync);
489 
490 	fid = filp->private_data;
491 
492 	retval = p9_client_fsync(fid, datasync);
493 	return retval;
494 }
495 
496 const struct file_operations v9fs_cached_file_operations = {
497 	.llseek = generic_file_llseek,
498 	.read = do_sync_read,
499 	.aio_read = generic_file_aio_read,
500 	.write = v9fs_file_write,
501 	.open = v9fs_file_open,
502 	.release = v9fs_dir_release,
503 	.lock = v9fs_file_lock,
504 	.mmap = generic_file_readonly_mmap,
505 	.fsync = v9fs_file_fsync,
506 };
507 
508 const struct file_operations v9fs_cached_file_operations_dotl = {
509 	.llseek = generic_file_llseek,
510 	.read = do_sync_read,
511 	.aio_read = generic_file_aio_read,
512 	.write = v9fs_file_write,
513 	.open = v9fs_file_open,
514 	.release = v9fs_dir_release,
515 	.lock = v9fs_file_lock_dotl,
516 	.flock = v9fs_file_flock_dotl,
517 	.mmap = generic_file_readonly_mmap,
518 	.fsync = v9fs_file_fsync_dotl,
519 };
520 
521 const struct file_operations v9fs_file_operations = {
522 	.llseek = generic_file_llseek,
523 	.read = v9fs_file_read,
524 	.write = v9fs_file_write,
525 	.open = v9fs_file_open,
526 	.release = v9fs_dir_release,
527 	.lock = v9fs_file_lock,
528 	.mmap = generic_file_readonly_mmap,
529 	.fsync = v9fs_file_fsync,
530 };
531 
532 const struct file_operations v9fs_file_operations_dotl = {
533 	.llseek = generic_file_llseek,
534 	.read = v9fs_file_read,
535 	.write = v9fs_file_write,
536 	.open = v9fs_file_open,
537 	.release = v9fs_dir_release,
538 	.lock = v9fs_file_lock_dotl,
539 	.flock = v9fs_file_flock_dotl,
540 	.mmap = generic_file_readonly_mmap,
541 	.fsync = v9fs_file_fsync_dotl,
542 };
543