Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/fs/9p/vfs_dir.c
15109 views
1
/*
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 <[email protected]>
7
* Copyright (C) 2002 by Ron Minnich <[email protected]>
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 <net/9p/9p.h>
37
#include <net/9p/client.h>
38
39
#include "v9fs.h"
40
#include "v9fs_vfs.h"
41
#include "fid.h"
42
43
/**
44
* struct p9_rdir - readdir accounting
45
* @mutex: mutex protecting readdir
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
54
struct p9_rdir {
55
struct mutex mutex;
56
int head;
57
int tail;
58
uint8_t *buf;
59
};
60
61
/**
62
* dt_type - return file type
63
* @mistat: mistat structure
64
*
65
*/
66
67
static inline int dt_type(struct p9_wstat *mistat)
68
{
69
unsigned long perm = mistat->mode;
70
int rettype = DT_REG;
71
72
if (perm & P9_DMDIR)
73
rettype = DT_DIR;
74
if (perm & P9_DMSYMLINK)
75
rettype = DT_LNK;
76
77
return rettype;
78
}
79
80
static void p9stat_init(struct p9_wstat *stbuf)
81
{
82
stbuf->name = NULL;
83
stbuf->uid = NULL;
84
stbuf->gid = NULL;
85
stbuf->muid = NULL;
86
stbuf->extension = NULL;
87
}
88
89
/**
90
* v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
91
* @filp: opened file structure
92
* @buflen: Length in bytes of buffer to allocate
93
*
94
*/
95
96
static int v9fs_alloc_rdir_buf(struct file *filp, int buflen)
97
{
98
struct p9_rdir *rdir;
99
struct p9_fid *fid;
100
int err = 0;
101
102
fid = filp->private_data;
103
if (!fid->rdir) {
104
rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
105
106
if (rdir == NULL) {
107
err = -ENOMEM;
108
goto exit;
109
}
110
spin_lock(&filp->f_dentry->d_lock);
111
if (!fid->rdir) {
112
rdir->buf = (uint8_t *)rdir + sizeof(struct p9_rdir);
113
mutex_init(&rdir->mutex);
114
rdir->head = rdir->tail = 0;
115
fid->rdir = (void *) rdir;
116
rdir = NULL;
117
}
118
spin_unlock(&filp->f_dentry->d_lock);
119
kfree(rdir);
120
}
121
exit:
122
return err;
123
}
124
125
/**
126
* v9fs_dir_readdir - read a directory
127
* @filp: opened file structure
128
* @dirent: directory structure ???
129
* @filldir: function to populate directory structure ???
130
*
131
*/
132
133
static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
134
{
135
int over;
136
struct p9_wstat st;
137
int err = 0;
138
struct p9_fid *fid;
139
int buflen;
140
int reclen = 0;
141
struct p9_rdir *rdir;
142
143
P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
144
fid = filp->private_data;
145
146
buflen = fid->clnt->msize - P9_IOHDRSZ;
147
148
err = v9fs_alloc_rdir_buf(filp, buflen);
149
if (err)
150
goto exit;
151
rdir = (struct p9_rdir *) fid->rdir;
152
153
err = mutex_lock_interruptible(&rdir->mutex);
154
if (err)
155
return err;
156
while (err == 0) {
157
if (rdir->tail == rdir->head) {
158
err = v9fs_file_readn(filp, rdir->buf, NULL,
159
buflen, filp->f_pos);
160
if (err <= 0)
161
goto unlock_and_exit;
162
163
rdir->head = 0;
164
rdir->tail = err;
165
}
166
while (rdir->head < rdir->tail) {
167
p9stat_init(&st);
168
err = p9stat_read(rdir->buf + rdir->head,
169
rdir->tail - rdir->head, &st,
170
fid->clnt->proto_version);
171
if (err) {
172
P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
173
err = -EIO;
174
p9stat_free(&st);
175
goto unlock_and_exit;
176
}
177
reclen = st.size+2;
178
179
over = filldir(dirent, st.name, strlen(st.name),
180
filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st));
181
182
p9stat_free(&st);
183
184
if (over) {
185
err = 0;
186
goto unlock_and_exit;
187
}
188
rdir->head += reclen;
189
filp->f_pos += reclen;
190
}
191
}
192
193
unlock_and_exit:
194
mutex_unlock(&rdir->mutex);
195
exit:
196
return err;
197
}
198
199
/**
200
* v9fs_dir_readdir_dotl - read a directory
201
* @filp: opened file structure
202
* @dirent: buffer to fill dirent structures
203
* @filldir: function to populate dirent structures
204
*
205
*/
206
static int v9fs_dir_readdir_dotl(struct file *filp, void *dirent,
207
filldir_t filldir)
208
{
209
int over;
210
int err = 0;
211
struct p9_fid *fid;
212
int buflen;
213
struct p9_rdir *rdir;
214
struct p9_dirent curdirent;
215
u64 oldoffset = 0;
216
217
P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
218
fid = filp->private_data;
219
220
buflen = fid->clnt->msize - P9_READDIRHDRSZ;
221
222
err = v9fs_alloc_rdir_buf(filp, buflen);
223
if (err)
224
goto exit;
225
rdir = (struct p9_rdir *) fid->rdir;
226
227
err = mutex_lock_interruptible(&rdir->mutex);
228
if (err)
229
return err;
230
231
while (err == 0) {
232
if (rdir->tail == rdir->head) {
233
err = p9_client_readdir(fid, rdir->buf, buflen,
234
filp->f_pos);
235
if (err <= 0)
236
goto unlock_and_exit;
237
238
rdir->head = 0;
239
rdir->tail = err;
240
}
241
242
while (rdir->head < rdir->tail) {
243
244
err = p9dirent_read(rdir->buf + rdir->head,
245
rdir->tail - rdir->head,
246
&curdirent,
247
fid->clnt->proto_version);
248
if (err < 0) {
249
P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
250
err = -EIO;
251
goto unlock_and_exit;
252
}
253
254
/* d_off in dirent structure tracks the offset into
255
* the next dirent in the dir. However, filldir()
256
* expects offset into the current dirent. Hence
257
* while calling filldir send the offset from the
258
* previous dirent structure.
259
*/
260
over = filldir(dirent, curdirent.d_name,
261
strlen(curdirent.d_name),
262
oldoffset, v9fs_qid2ino(&curdirent.qid),
263
curdirent.d_type);
264
oldoffset = curdirent.d_off;
265
266
if (over) {
267
err = 0;
268
goto unlock_and_exit;
269
}
270
271
filp->f_pos = curdirent.d_off;
272
rdir->head += err;
273
}
274
}
275
276
unlock_and_exit:
277
mutex_unlock(&rdir->mutex);
278
exit:
279
return err;
280
}
281
282
283
/**
284
* v9fs_dir_release - close a directory
285
* @inode: inode of the directory
286
* @filp: file pointer to a directory
287
*
288
*/
289
290
int v9fs_dir_release(struct inode *inode, struct file *filp)
291
{
292
struct p9_fid *fid;
293
294
fid = filp->private_data;
295
P9_DPRINTK(P9_DEBUG_VFS,
296
"v9fs_dir_release: inode: %p filp: %p fid: %d\n",
297
inode, filp, fid ? fid->fid : -1);
298
if (fid)
299
p9_client_clunk(fid);
300
return 0;
301
}
302
303
const struct file_operations v9fs_dir_operations = {
304
.read = generic_read_dir,
305
.llseek = generic_file_llseek,
306
.readdir = v9fs_dir_readdir,
307
.open = v9fs_file_open,
308
.release = v9fs_dir_release,
309
};
310
311
const struct file_operations v9fs_dir_operations_dotl = {
312
.read = generic_read_dir,
313
.llseek = generic_file_llseek,
314
.readdir = v9fs_dir_readdir_dotl,
315
.open = v9fs_file_open,
316
.release = v9fs_dir_release,
317
.fsync = v9fs_file_fsync_dotl,
318
};
319
320