GNU Linux-libre 6.9-gnu
[releases.git] / fs / 9p / vfs_dir.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * This file contains vfs directory ops for the 9P2000 protocol.
4  *
5  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
6  *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
7  */
8
9 #include <linux/module.h>
10 #include <linux/errno.h>
11 #include <linux/fs.h>
12 #include <linux/file.h>
13 #include <linux/stat.h>
14 #include <linux/string.h>
15 #include <linux/sched.h>
16 #include <linux/slab.h>
17 #include <linux/uio.h>
18 #include <linux/fscache.h>
19 #include <net/9p/9p.h>
20 #include <net/9p/client.h>
21
22 #include "v9fs.h"
23 #include "v9fs_vfs.h"
24 #include "fid.h"
25
26 /**
27  * struct p9_rdir - readdir accounting
28  * @head: start offset of current dirread buffer
29  * @tail: end offset of current dirread buffer
30  * @buf: dirread buffer
31  *
32  * private structure for keeping track of readdir
33  * allocated on demand
34  */
35
36 struct p9_rdir {
37         int head;
38         int tail;
39         uint8_t buf[];
40 };
41
42 /**
43  * dt_type - return file type
44  * @mistat: mistat structure
45  *
46  */
47
48 static inline int dt_type(struct p9_wstat *mistat)
49 {
50         unsigned long perm = mistat->mode;
51         int rettype = DT_REG;
52
53         if (perm & P9_DMDIR)
54                 rettype = DT_DIR;
55         if (perm & P9_DMSYMLINK)
56                 rettype = DT_LNK;
57
58         return rettype;
59 }
60
61 /**
62  * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
63  * @filp: opened file structure
64  * @buflen: Length in bytes of buffer to allocate
65  *
66  */
67
68 static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
69 {
70         struct p9_fid *fid = filp->private_data;
71
72         if (!fid->rdir)
73                 fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
74         return fid->rdir;
75 }
76
77 /**
78  * v9fs_dir_readdir - iterate through a directory
79  * @file: opened file structure
80  * @ctx: actor we feed the entries to
81  *
82  */
83
84 static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
85 {
86         bool over;
87         struct p9_wstat st;
88         int err = 0;
89         struct p9_fid *fid;
90         int buflen;
91         struct p9_rdir *rdir;
92         struct kvec kvec;
93
94         p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
95         fid = file->private_data;
96
97         buflen = fid->clnt->msize - P9_IOHDRSZ;
98
99         rdir = v9fs_alloc_rdir_buf(file, buflen);
100         if (!rdir)
101                 return -ENOMEM;
102         kvec.iov_base = rdir->buf;
103         kvec.iov_len = buflen;
104
105         while (1) {
106                 if (rdir->tail == rdir->head) {
107                         struct iov_iter to;
108                         int n;
109
110                         iov_iter_kvec(&to, ITER_DEST, &kvec, 1, buflen);
111                         n = p9_client_read(file->private_data, ctx->pos, &to,
112                                            &err);
113                         if (err)
114                                 return err;
115                         if (n == 0)
116                                 return 0;
117
118                         rdir->head = 0;
119                         rdir->tail = n;
120                 }
121                 while (rdir->head < rdir->tail) {
122                         err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
123                                           rdir->tail - rdir->head, &st);
124                         if (err <= 0) {
125                                 p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
126                                 return -EIO;
127                         }
128
129                         over = !dir_emit(ctx, st.name, strlen(st.name),
130                                         QID2INO(&st.qid), dt_type(&st));
131                         p9stat_free(&st);
132                         if (over)
133                                 return 0;
134
135                         rdir->head += err;
136                         ctx->pos += err;
137                 }
138         }
139 }
140
141 /**
142  * v9fs_dir_readdir_dotl - iterate through a directory
143  * @file: opened file structure
144  * @ctx: actor we feed the entries to
145  *
146  */
147 static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
148 {
149         int err = 0;
150         struct p9_fid *fid;
151         int buflen;
152         struct p9_rdir *rdir;
153         struct p9_dirent curdirent;
154
155         p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
156         fid = file->private_data;
157
158         buflen = fid->clnt->msize - P9_READDIRHDRSZ;
159
160         rdir = v9fs_alloc_rdir_buf(file, buflen);
161         if (!rdir)
162                 return -ENOMEM;
163
164         while (1) {
165                 if (rdir->tail == rdir->head) {
166                         err = p9_client_readdir(fid, rdir->buf, buflen,
167                                                 ctx->pos);
168                         if (err <= 0)
169                                 return err;
170
171                         rdir->head = 0;
172                         rdir->tail = err;
173                 }
174
175                 while (rdir->head < rdir->tail) {
176
177                         err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
178                                             rdir->tail - rdir->head,
179                                             &curdirent);
180                         if (err < 0) {
181                                 p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
182                                 return -EIO;
183                         }
184
185                         if (!dir_emit(ctx, curdirent.d_name,
186                                       strlen(curdirent.d_name),
187                                       QID2INO(&curdirent.qid),
188                                       curdirent.d_type))
189                                 return 0;
190
191                         ctx->pos = curdirent.d_off;
192                         rdir->head += err;
193                 }
194         }
195 }
196
197
198 /**
199  * v9fs_dir_release - close a directory or a file
200  * @inode: inode of the directory or file
201  * @filp: file pointer to a directory or file
202  *
203  */
204
205 int v9fs_dir_release(struct inode *inode, struct file *filp)
206 {
207         struct v9fs_inode *v9inode = V9FS_I(inode);
208         struct p9_fid *fid;
209         __le32 version;
210         loff_t i_size;
211         int retval = 0, put_err;
212
213         fid = filp->private_data;
214         p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
215                  inode, filp, fid ? fid->fid : -1);
216
217         if (fid) {
218                 if ((S_ISREG(inode->i_mode)) && (filp->f_mode & FMODE_WRITE))
219                         retval = filemap_fdatawrite(inode->i_mapping);
220
221                 spin_lock(&inode->i_lock);
222                 hlist_del(&fid->ilist);
223                 spin_unlock(&inode->i_lock);
224                 put_err = p9_fid_put(fid);
225                 retval = retval < 0 ? retval : put_err;
226         }
227
228         if ((filp->f_mode & FMODE_WRITE)) {
229                 version = cpu_to_le32(v9inode->qid.version);
230                 i_size = i_size_read(inode);
231                 fscache_unuse_cookie(v9fs_inode_cookie(v9inode),
232                                      &version, &i_size);
233         } else {
234                 fscache_unuse_cookie(v9fs_inode_cookie(v9inode), NULL, NULL);
235         }
236         return retval;
237 }
238
239 const struct file_operations v9fs_dir_operations = {
240         .read = generic_read_dir,
241         .llseek = generic_file_llseek,
242         .iterate_shared = v9fs_dir_readdir,
243         .open = v9fs_file_open,
244         .release = v9fs_dir_release,
245 };
246
247 const struct file_operations v9fs_dir_operations_dotl = {
248         .read = generic_read_dir,
249         .llseek = generic_file_llseek,
250         .iterate_shared = v9fs_dir_readdir_dotl,
251         .open = v9fs_file_open,
252         .release = v9fs_dir_release,
253         .fsync = v9fs_file_fsync_dotl,
254 };