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