xref: /openbmc/linux/fs/9p/vfs_dir.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
11f327613SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e69e7fe5SEric Van Hensbergen /*
3e69e7fe5SEric Van Hensbergen  * This file contains vfs directory ops for the 9P2000 protocol.
4e69e7fe5SEric Van Hensbergen  *
5e69e7fe5SEric Van Hensbergen  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
6e69e7fe5SEric Van Hensbergen  *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
7e69e7fe5SEric Van Hensbergen  */
8e69e7fe5SEric Van Hensbergen 
9e69e7fe5SEric Van Hensbergen #include <linux/module.h>
10e69e7fe5SEric Van Hensbergen #include <linux/errno.h>
11e69e7fe5SEric Van Hensbergen #include <linux/fs.h>
12e69e7fe5SEric Van Hensbergen #include <linux/file.h>
13e69e7fe5SEric Van Hensbergen #include <linux/stat.h>
14e69e7fe5SEric Van Hensbergen #include <linux/string.h>
15914e2637SAl Viro #include <linux/sched.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
17e1200fe6SAl Viro #include <linux/uio.h>
1824e42e32SDavid Howells #include <linux/fscache.h>
19bd238fb4SLatchesar Ionkov #include <net/9p/9p.h>
20bd238fb4SLatchesar Ionkov #include <net/9p/client.h>
21e69e7fe5SEric Van Hensbergen 
22e69e7fe5SEric Van Hensbergen #include "v9fs.h"
23531b1094SLatchesar Ionkov #include "v9fs_vfs.h"
24e69e7fe5SEric Van Hensbergen #include "fid.h"
25e69e7fe5SEric Van Hensbergen 
26e69e7fe5SEric Van Hensbergen /**
273e2796a9SEric Van Hensbergen  * struct p9_rdir - readdir accounting
283e2796a9SEric Van Hensbergen  * @head: start offset of current dirread buffer
293e2796a9SEric Van Hensbergen  * @tail: end offset of current dirread buffer
303e2796a9SEric Van Hensbergen  * @buf: dirread buffer
313e2796a9SEric Van Hensbergen  *
323e2796a9SEric Van Hensbergen  * private structure for keeping track of readdir
333e2796a9SEric Van Hensbergen  * allocated on demand
343e2796a9SEric Van Hensbergen  */
353e2796a9SEric Van Hensbergen 
363e2796a9SEric Van Hensbergen struct p9_rdir {
373e2796a9SEric Van Hensbergen 	int head;
383e2796a9SEric Van Hensbergen 	int tail;
397ffdea7eSAl Viro 	uint8_t buf[];
403e2796a9SEric Van Hensbergen };
413e2796a9SEric Van Hensbergen 
423e2796a9SEric Van Hensbergen /**
43e69e7fe5SEric Van Hensbergen  * dt_type - return file type
44e69e7fe5SEric Van Hensbergen  * @mistat: mistat structure
45e69e7fe5SEric Van Hensbergen  *
46e69e7fe5SEric Van Hensbergen  */
47e69e7fe5SEric Van Hensbergen 
dt_type(struct p9_wstat * mistat)4802da398bSEric Van Hensbergen static inline int dt_type(struct p9_wstat *mistat)
49e69e7fe5SEric Van Hensbergen {
50e69e7fe5SEric Van Hensbergen 	unsigned long perm = mistat->mode;
51e69e7fe5SEric Van Hensbergen 	int rettype = DT_REG;
52e69e7fe5SEric Van Hensbergen 
53bd238fb4SLatchesar Ionkov 	if (perm & P9_DMDIR)
54e69e7fe5SEric Van Hensbergen 		rettype = DT_DIR;
55bd238fb4SLatchesar Ionkov 	if (perm & P9_DMSYMLINK)
56e69e7fe5SEric Van Hensbergen 		rettype = DT_LNK;
57e69e7fe5SEric Van Hensbergen 
58e69e7fe5SEric Van Hensbergen 	return rettype;
59e69e7fe5SEric Van Hensbergen }
60e69e7fe5SEric Van Hensbergen 
61e69e7fe5SEric Van Hensbergen /**
627751bdb3SSripathi Kodi  * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
637751bdb3SSripathi Kodi  * @filp: opened file structure
647751bdb3SSripathi Kodi  * @buflen: Length in bytes of buffer to allocate
657751bdb3SSripathi Kodi  *
667751bdb3SSripathi Kodi  */
677751bdb3SSripathi Kodi 
v9fs_alloc_rdir_buf(struct file * filp,int buflen)687ffdea7eSAl Viro static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
697751bdb3SSripathi Kodi {
707ffdea7eSAl Viro 	struct p9_fid *fid = filp->private_data;
716d66ffc1SSohaib Mohamed 
727ffdea7eSAl Viro 	if (!fid->rdir)
737ffdea7eSAl Viro 		fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
747ffdea7eSAl Viro 	return fid->rdir;
757751bdb3SSripathi Kodi }
767751bdb3SSripathi Kodi 
777751bdb3SSripathi Kodi /**
788f29843aSAl Viro  * v9fs_dir_readdir - iterate through a directory
798f29843aSAl Viro  * @file: opened file structure
808f29843aSAl Viro  * @ctx: actor we feed the entries to
81e69e7fe5SEric Van Hensbergen  *
82e69e7fe5SEric Van Hensbergen  */
83e69e7fe5SEric Van Hensbergen 
v9fs_dir_readdir(struct file * file,struct dir_context * ctx)848f29843aSAl Viro static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
85e69e7fe5SEric Van Hensbergen {
868f29843aSAl Viro 	bool over;
8702da398bSEric Van Hensbergen 	struct p9_wstat st;
883e2796a9SEric Van Hensbergen 	int err = 0;
89bd238fb4SLatchesar Ionkov 	struct p9_fid *fid;
9006b55b46SEric Van Hensbergen 	int buflen;
913e2796a9SEric Van Hensbergen 	struct p9_rdir *rdir;
92e1200fe6SAl Viro 	struct kvec kvec;
93e69e7fe5SEric Van Hensbergen 
944b8e9923SAl Viro 	p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
958f29843aSAl Viro 	fid = file->private_data;
96e69e7fe5SEric Van Hensbergen 
9706b55b46SEric Van Hensbergen 	buflen = fid->clnt->msize - P9_IOHDRSZ;
98e69e7fe5SEric Van Hensbergen 
998f29843aSAl Viro 	rdir = v9fs_alloc_rdir_buf(file, buflen);
1007ffdea7eSAl Viro 	if (!rdir)
1017ffdea7eSAl Viro 		return -ENOMEM;
102e1200fe6SAl Viro 	kvec.iov_base = rdir->buf;
103e1200fe6SAl Viro 	kvec.iov_len = buflen;
1043e2796a9SEric Van Hensbergen 
1057ffdea7eSAl Viro 	while (1) {
1063e2796a9SEric Van Hensbergen 		if (rdir->tail == rdir->head) {
107e1200fe6SAl Viro 			struct iov_iter to;
108e1200fe6SAl Viro 			int n;
1096d66ffc1SSohaib Mohamed 
110de4eda9dSAl Viro 			iov_iter_kvec(&to, ITER_DEST, &kvec, 1, buflen);
111e1200fe6SAl Viro 			n = p9_client_read(file->private_data, ctx->pos, &to,
112e1200fe6SAl Viro 					   &err);
113e1200fe6SAl Viro 			if (err)
1147ffdea7eSAl Viro 				return err;
1158e3c5005SJohannes Berg 			if (n == 0)
1168e3c5005SJohannes Berg 				return 0;
117e69e7fe5SEric Van Hensbergen 
1183e2796a9SEric Van Hensbergen 			rdir->head = 0;
119e1200fe6SAl Viro 			rdir->tail = n;
1203e2796a9SEric Van Hensbergen 		}
1213e2796a9SEric Van Hensbergen 		while (rdir->head < rdir->tail) {
122348b5901SAneesh Kumar K.V 			err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
123348b5901SAneesh Kumar K.V 					  rdir->tail - rdir->head, &st);
1242803cf43SGertjan Halkes 			if (err <= 0) {
1255d385153SJoe Perches 				p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
1267ffdea7eSAl Viro 				return -EIO;
127e69e7fe5SEric Van Hensbergen 			}
12806b55b46SEric Van Hensbergen 
1298f29843aSAl Viro 			over = !dir_emit(ctx, st.name, strlen(st.name),
1308f29843aSAl Viro 					 v9fs_qid2ino(&st.qid), dt_type(&st));
13102da398bSEric Van Hensbergen 			p9stat_free(&st);
1327ffdea7eSAl Viro 			if (over)
1337ffdea7eSAl Viro 				return 0;
1347ffdea7eSAl Viro 
1352803cf43SGertjan Halkes 			rdir->head += err;
1362803cf43SGertjan Halkes 			ctx->pos += err;
13706b55b46SEric Van Hensbergen 		}
13806b55b46SEric Van Hensbergen 	}
139e69e7fe5SEric Van Hensbergen }
140e69e7fe5SEric Van Hensbergen 
1417751bdb3SSripathi Kodi /**
1428f29843aSAl Viro  * v9fs_dir_readdir_dotl - iterate through a directory
1438f29843aSAl Viro  * @file: opened file structure
1448f29843aSAl Viro  * @ctx: actor we feed the entries to
1457751bdb3SSripathi Kodi  *
1467751bdb3SSripathi Kodi  */
v9fs_dir_readdir_dotl(struct file * file,struct dir_context * ctx)1478f29843aSAl Viro static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
1487751bdb3SSripathi Kodi {
1497751bdb3SSripathi Kodi 	int err = 0;
1507751bdb3SSripathi Kodi 	struct p9_fid *fid;
1517751bdb3SSripathi Kodi 	int buflen;
1527751bdb3SSripathi Kodi 	struct p9_rdir *rdir;
1537751bdb3SSripathi Kodi 	struct p9_dirent curdirent;
1547751bdb3SSripathi Kodi 
1554b8e9923SAl Viro 	p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
1568f29843aSAl Viro 	fid = file->private_data;
1577751bdb3SSripathi Kodi 
1587751bdb3SSripathi Kodi 	buflen = fid->clnt->msize - P9_READDIRHDRSZ;
1597751bdb3SSripathi Kodi 
1608f29843aSAl Viro 	rdir = v9fs_alloc_rdir_buf(file, buflen);
1617ffdea7eSAl Viro 	if (!rdir)
1627ffdea7eSAl Viro 		return -ENOMEM;
1637751bdb3SSripathi Kodi 
1647ffdea7eSAl Viro 	while (1) {
1657751bdb3SSripathi Kodi 		if (rdir->tail == rdir->head) {
1667751bdb3SSripathi Kodi 			err = p9_client_readdir(fid, rdir->buf, buflen,
1678f29843aSAl Viro 						ctx->pos);
1687751bdb3SSripathi Kodi 			if (err <= 0)
1697ffdea7eSAl Viro 				return err;
1707751bdb3SSripathi Kodi 
1717751bdb3SSripathi Kodi 			rdir->head = 0;
1727751bdb3SSripathi Kodi 			rdir->tail = err;
1737751bdb3SSripathi Kodi 		}
1747751bdb3SSripathi Kodi 
1757751bdb3SSripathi Kodi 		while (rdir->head < rdir->tail) {
1767751bdb3SSripathi Kodi 
177348b5901SAneesh Kumar K.V 			err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
1788812a3d5SSripathi Kodi 					    rdir->tail - rdir->head,
179348b5901SAneesh Kumar K.V 					    &curdirent);
1807751bdb3SSripathi Kodi 			if (err < 0) {
1815d385153SJoe Perches 				p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
1827ffdea7eSAl Viro 				return -EIO;
1837751bdb3SSripathi Kodi 			}
1847751bdb3SSripathi Kodi 
1858f29843aSAl Viro 			if (!dir_emit(ctx, curdirent.d_name,
1867751bdb3SSripathi Kodi 				      strlen(curdirent.d_name),
1878f29843aSAl Viro 				      v9fs_qid2ino(&curdirent.qid),
1888f29843aSAl Viro 				      curdirent.d_type))
1897ffdea7eSAl Viro 				return 0;
1907751bdb3SSripathi Kodi 
1918f29843aSAl Viro 			ctx->pos = curdirent.d_off;
1927751bdb3SSripathi Kodi 			rdir->head += err;
1937751bdb3SSripathi Kodi 		}
1947751bdb3SSripathi Kodi 	}
1957751bdb3SSripathi Kodi }
1967751bdb3SSripathi Kodi 
197e69e7fe5SEric Van Hensbergen 
198e69e7fe5SEric Van Hensbergen /**
199d9bc0d11SEric Van Hensbergen  * v9fs_dir_release - close a directory or a file
200d9bc0d11SEric Van Hensbergen  * @inode: inode of the directory or file
201d9bc0d11SEric Van Hensbergen  * @filp: file pointer to a directory or file
202e69e7fe5SEric Van Hensbergen  *
203e69e7fe5SEric Van Hensbergen  */
204e69e7fe5SEric Van Hensbergen 
v9fs_dir_release(struct inode * inode,struct file * filp)205e69e7fe5SEric Van Hensbergen int v9fs_dir_release(struct inode *inode, struct file *filp)
206e69e7fe5SEric Van Hensbergen {
20724e42e32SDavid Howells 	struct v9fs_inode *v9inode = V9FS_I(inode);
208bd238fb4SLatchesar Ionkov 	struct p9_fid *fid;
20924e42e32SDavid Howells 	__le32 version;
21024e42e32SDavid Howells 	loff_t i_size;
211*eee4a119SDominique Martinet 	int retval = 0, put_err;
212e69e7fe5SEric Van Hensbergen 
213bd238fb4SLatchesar Ionkov 	fid = filp->private_data;
2145d385153SJoe Perches 	p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
21562726a7aSjvrao 		 inode, filp, fid ? fid->fid : -1);
216d9bc0d11SEric Van Hensbergen 
2176636b6dcSJianyong Wu 	if (fid) {
218d9bc0d11SEric Van Hensbergen 		if ((S_ISREG(inode->i_mode)) && (filp->f_mode & FMODE_WRITE))
219d9bc0d11SEric Van Hensbergen 			retval = filemap_fdatawrite(inode->i_mapping);
220d9bc0d11SEric Van Hensbergen 
221987a6485SGreg Kurz 		spin_lock(&inode->i_lock);
222987a6485SGreg Kurz 		hlist_del(&fid->ilist);
223987a6485SGreg Kurz 		spin_unlock(&inode->i_lock);
224*eee4a119SDominique Martinet 		put_err = p9_fid_put(fid);
225*eee4a119SDominique Martinet 		retval = retval < 0 ? retval : put_err;
2266636b6dcSJianyong Wu 	}
22724e42e32SDavid Howells 
22824e42e32SDavid Howells 	if ((filp->f_mode & FMODE_WRITE)) {
22924e42e32SDavid Howells 		version = cpu_to_le32(v9inode->qid.version);
23024e42e32SDavid Howells 		i_size = i_size_read(inode);
23124e42e32SDavid Howells 		fscache_unuse_cookie(v9fs_inode_cookie(v9inode),
23224e42e32SDavid Howells 				     &version, &i_size);
23324e42e32SDavid Howells 	} else {
23424e42e32SDavid Howells 		fscache_unuse_cookie(v9fs_inode_cookie(v9inode), NULL, NULL);
23524e42e32SDavid Howells 	}
23689c58cb3SEric Van Hensbergen 	return retval;
237e69e7fe5SEric Van Hensbergen }
238e69e7fe5SEric Van Hensbergen 
2394b6f5d20SArjan van de Ven const struct file_operations v9fs_dir_operations = {
240e69e7fe5SEric Van Hensbergen 	.read = generic_read_dir,
24159af1584SAl Viro 	.llseek = generic_file_llseek,
2425963ded8SAl Viro 	.iterate_shared = v9fs_dir_readdir,
243e69e7fe5SEric Van Hensbergen 	.open = v9fs_file_open,
244e69e7fe5SEric Van Hensbergen 	.release = v9fs_dir_release,
245e69e7fe5SEric Van Hensbergen };
2469b6533c9SSripathi Kodi 
2479b6533c9SSripathi Kodi const struct file_operations v9fs_dir_operations_dotl = {
2489b6533c9SSripathi Kodi 	.read = generic_read_dir,
2499b6533c9SSripathi Kodi 	.llseek = generic_file_llseek,
2505963ded8SAl Viro 	.iterate_shared = v9fs_dir_readdir_dotl,
2519b6533c9SSripathi Kodi 	.open = v9fs_file_open,
2529b6533c9SSripathi Kodi 	.release = v9fs_dir_release,
253b165d601SVenkateswararao Jujjuri (JV) 	.fsync = v9fs_file_fsync_dotl,
2549b6533c9SSripathi Kodi };
255