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