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