Merge branch 'work.afs' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[sfrench/cifs-2.6.git] / fs / 9p / vfs_dir.c
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 <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
54 struct 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
66 static 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
86 static 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
101 static 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         struct p9_rdir *rdir;
109         struct kvec kvec;
110
111         p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
112         fid = file->private_data;
113
114         buflen = fid->clnt->msize - P9_IOHDRSZ;
115
116         rdir = v9fs_alloc_rdir_buf(file, buflen);
117         if (!rdir)
118                 return -ENOMEM;
119         kvec.iov_base = rdir->buf;
120         kvec.iov_len = buflen;
121
122         while (1) {
123                 if (rdir->tail == rdir->head) {
124                         struct iov_iter to;
125                         int n;
126                         iov_iter_kvec(&to, READ, &kvec, 1, buflen);
127                         n = p9_client_read(file->private_data, ctx->pos, &to,
128                                            &err);
129                         if (err)
130                                 return err;
131                         if (n == 0)
132                                 return 0;
133
134                         rdir->head = 0;
135                         rdir->tail = n;
136                 }
137                 while (rdir->head < rdir->tail) {
138                         err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
139                                           rdir->tail - rdir->head, &st);
140                         if (err <= 0) {
141                                 p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
142                                 return -EIO;
143                         }
144
145                         over = !dir_emit(ctx, st.name, strlen(st.name),
146                                          v9fs_qid2ino(&st.qid), dt_type(&st));
147                         p9stat_free(&st);
148                         if (over)
149                                 return 0;
150
151                         rdir->head += err;
152                         ctx->pos += err;
153                 }
154         }
155 }
156
157 /**
158  * v9fs_dir_readdir_dotl - iterate through a directory
159  * @file: opened file structure
160  * @ctx: actor we feed the entries to
161  *
162  */
163 static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
164 {
165         int err = 0;
166         struct p9_fid *fid;
167         int buflen;
168         struct p9_rdir *rdir;
169         struct p9_dirent curdirent;
170
171         p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
172         fid = file->private_data;
173
174         buflen = fid->clnt->msize - P9_READDIRHDRSZ;
175
176         rdir = v9fs_alloc_rdir_buf(file, buflen);
177         if (!rdir)
178                 return -ENOMEM;
179
180         while (1) {
181                 if (rdir->tail == rdir->head) {
182                         err = p9_client_readdir(fid, rdir->buf, buflen,
183                                                 ctx->pos);
184                         if (err <= 0)
185                                 return err;
186
187                         rdir->head = 0;
188                         rdir->tail = err;
189                 }
190
191                 while (rdir->head < rdir->tail) {
192
193                         err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
194                                             rdir->tail - rdir->head,
195                                             &curdirent);
196                         if (err < 0) {
197                                 p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
198                                 return -EIO;
199                         }
200
201                         if (!dir_emit(ctx, curdirent.d_name,
202                                       strlen(curdirent.d_name),
203                                       v9fs_qid2ino(&curdirent.qid),
204                                       curdirent.d_type))
205                                 return 0;
206
207                         ctx->pos = curdirent.d_off;
208                         rdir->head += err;
209                 }
210         }
211 }
212
213
214 /**
215  * v9fs_dir_release - close a directory
216  * @inode: inode of the directory
217  * @filp: file pointer to a directory
218  *
219  */
220
221 int v9fs_dir_release(struct inode *inode, struct file *filp)
222 {
223         struct p9_fid *fid;
224
225         fid = filp->private_data;
226         p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
227                  inode, filp, fid ? fid->fid : -1);
228         if (fid)
229                 p9_client_clunk(fid);
230         return 0;
231 }
232
233 const struct file_operations v9fs_dir_operations = {
234         .read = generic_read_dir,
235         .llseek = generic_file_llseek,
236         .iterate_shared = v9fs_dir_readdir,
237         .open = v9fs_file_open,
238         .release = v9fs_dir_release,
239 };
240
241 const struct file_operations v9fs_dir_operations_dotl = {
242         .read = generic_read_dir,
243         .llseek = generic_file_llseek,
244         .iterate_shared = v9fs_dir_readdir_dotl,
245         .open = v9fs_file_open,
246         .release = v9fs_dir_release,
247         .fsync = v9fs_file_fsync_dotl,
248 };