Orangefs: Clean up error decoding.
[linux-2.6-block.git] / fs / orangefs / dir.c
CommitLineData
5db11c21
MM
1/*
2 * (C) 2001 Clemson University and The University of Chicago
3 *
4 * See COPYING in top-level directory.
5 */
6
7#include "protocol.h"
8#include "pvfs2-kernel.h"
9#include "pvfs2-bufmap.h"
10
11struct readdir_handle_s {
12 int buffer_index;
13 struct pvfs2_readdir_response_s readdir_response;
14 void *dents_buf;
15};
16
17/*
18 * decode routine needed by kmod to make sense of the shared page for readdirs.
19 */
20static long decode_dirents(char *ptr, struct pvfs2_readdir_response_s *readdir)
21{
22 int i;
23 struct pvfs2_readdir_response_s *rd =
24 (struct pvfs2_readdir_response_s *) ptr;
25 char *buf = ptr;
26 char **pptr = &buf;
27
28 readdir->token = rd->token;
29 readdir->pvfs_dirent_outcount = rd->pvfs_dirent_outcount;
30 readdir->dirent_array = kmalloc(readdir->pvfs_dirent_outcount *
31 sizeof(*readdir->dirent_array),
32 GFP_KERNEL);
33 if (readdir->dirent_array == NULL)
34 return -ENOMEM;
35 *pptr += offsetof(struct pvfs2_readdir_response_s, dirent_array);
36 for (i = 0; i < readdir->pvfs_dirent_outcount; i++) {
37 dec_string(pptr, &readdir->dirent_array[i].d_name,
38 &readdir->dirent_array[i].d_length);
39 readdir->dirent_array[i].khandle =
40 *(struct pvfs2_khandle *) *pptr;
41 *pptr += 16;
42 }
43 return (unsigned long)*pptr - (unsigned long)ptr;
44}
45
46static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf,
47 int buffer_index)
48{
49 long ret;
50
51 if (buf == NULL) {
52 gossip_err
53 ("Invalid NULL buffer specified in readdir_handle_ctor\n");
54 return -ENOMEM;
55 }
56 if (buffer_index < 0) {
57 gossip_err
58 ("Invalid buffer index specified in readdir_handle_ctor\n");
59 return -EINVAL;
60 }
61 rhandle->buffer_index = buffer_index;
62 rhandle->dents_buf = buf;
63 ret = decode_dirents(buf, &rhandle->readdir_response);
64 if (ret < 0) {
65 gossip_err("Could not decode readdir from buffer %ld\n", ret);
66 rhandle->buffer_index = -1;
67 gossip_debug(GOSSIP_DIR_DEBUG, "vfree %p\n", buf);
68 vfree(buf);
69 rhandle->dents_buf = NULL;
70 }
71 return ret;
72}
73
74static void readdir_handle_dtor(struct pvfs2_bufmap *bufmap,
75 struct readdir_handle_s *rhandle)
76{
77 if (rhandle == NULL)
78 return;
79
80 /* kfree(NULL) is safe */
81 kfree(rhandle->readdir_response.dirent_array);
82 rhandle->readdir_response.dirent_array = NULL;
83
84 if (rhandle->buffer_index >= 0) {
85 readdir_index_put(bufmap, rhandle->buffer_index);
86 rhandle->buffer_index = -1;
87 }
88 if (rhandle->dents_buf) {
89 gossip_debug(GOSSIP_DIR_DEBUG, "vfree %p\n",
90 rhandle->dents_buf);
91 vfree(rhandle->dents_buf);
92 rhandle->dents_buf = NULL;
93 }
94}
95
96/*
97 * Read directory entries from an instance of an open directory.
5db11c21
MM
98 */
99static int pvfs2_readdir(struct file *file, struct dir_context *ctx)
100{
101 struct pvfs2_bufmap *bufmap = NULL;
102 int ret = 0;
103 int buffer_index;
88309aae
MM
104 /*
105 * ptoken supports Orangefs' distributed directory logic, added
106 * in 2.9.2.
107 */
5db11c21
MM
108 __u64 *ptoken = file->private_data;
109 __u64 pos = 0;
110 ino_t ino = 0;
111 struct dentry *dentry = file->f_path.dentry;
112 struct pvfs2_kernel_op_s *new_op = NULL;
113 struct pvfs2_inode_s *pvfs2_inode = PVFS2_I(dentry->d_inode);
114 int buffer_full = 0;
115 struct readdir_handle_s rhandle;
116 int i = 0;
117 int len = 0;
118 ino_t current_ino = 0;
119 char *current_entry = NULL;
120 long bytes_decoded;
121
88309aae
MM
122 gossip_debug(GOSSIP_DIR_DEBUG,
123 "%s: ctx->pos:%lld, ptoken = %llu\n",
124 __func__,
125 lld(ctx->pos),
126 llu(*ptoken));
5db11c21
MM
127
128 pos = (__u64) ctx->pos;
129
130 /* are we done? */
131 if (pos == PVFS_READDIR_END) {
132 gossip_debug(GOSSIP_DIR_DEBUG,
133 "Skipping to termination path\n");
134 return 0;
135 }
136
137 gossip_debug(GOSSIP_DIR_DEBUG,
138 "pvfs2_readdir called on %s (pos=%llu)\n",
139 dentry->d_name.name, llu(pos));
140
141 rhandle.buffer_index = -1;
142 rhandle.dents_buf = NULL;
143 memset(&rhandle.readdir_response, 0, sizeof(rhandle.readdir_response));
144
145 new_op = op_alloc(PVFS2_VFS_OP_READDIR);
146 if (!new_op)
147 return -ENOMEM;
148
149 new_op->uses_shared_memory = 1;
150 new_op->upcall.req.readdir.refn = pvfs2_inode->refn;
151 new_op->upcall.req.readdir.max_dirent_count = MAX_DIRENT_COUNT_READDIR;
152
153 gossip_debug(GOSSIP_DIR_DEBUG,
154 "%s: upcall.req.readdir.refn.khandle: %pU\n",
155 __func__,
156 &new_op->upcall.req.readdir.refn.khandle);
157
5db11c21
MM
158 new_op->upcall.req.readdir.token = *ptoken;
159
160get_new_buffer_index:
161 ret = readdir_index_get(&bufmap, &buffer_index);
162 if (ret < 0) {
163 gossip_lerr("pvfs2_readdir: readdir_index_get() failure (%d)\n",
164 ret);
165 goto out_free_op;
166 }
167 new_op->upcall.req.readdir.buf_index = buffer_index;
168
169 ret = service_operation(new_op,
170 "pvfs2_readdir",
171 get_interruptible_flag(dentry->d_inode));
172
173 gossip_debug(GOSSIP_DIR_DEBUG,
174 "Readdir downcall status is %d. ret:%d\n",
175 new_op->downcall.status,
176 ret);
177
178 if (ret == -EAGAIN && op_state_purged(new_op)) {
179 /*
180 * readdir shared memory aread has been wiped due to
181 * pvfs2-client-core restarting, so we must get a new
182 * index into the shared memory.
183 */
184 gossip_debug(GOSSIP_DIR_DEBUG,
185 "%s: Getting new buffer_index for retry of readdir..\n",
186 __func__);
187 readdir_index_put(bufmap, buffer_index);
188 goto get_new_buffer_index;
189 }
190
191 if (ret == -EIO && op_state_purged(new_op)) {
192 gossip_err("%s: Client is down. Aborting readdir call.\n",
193 __func__);
194 readdir_index_put(bufmap, buffer_index);
195 goto out_free_op;
196 }
197
198 if (ret < 0 || new_op->downcall.status != 0) {
199 gossip_debug(GOSSIP_DIR_DEBUG,
200 "Readdir request failed. Status:%d\n",
201 new_op->downcall.status);
202 readdir_index_put(bufmap, buffer_index);
203 if (ret >= 0)
204 ret = new_op->downcall.status;
205 goto out_free_op;
206 }
207
208 bytes_decoded =
209 readdir_handle_ctor(&rhandle,
210 new_op->downcall.trailer_buf,
211 buffer_index);
212 if (bytes_decoded < 0) {
213 gossip_err("pvfs2_readdir: Could not decode trailer buffer into a readdir response %d\n",
214 ret);
215 ret = bytes_decoded;
216 readdir_index_put(bufmap, buffer_index);
217 goto out_free_op;
218 }
219
220 if (bytes_decoded != new_op->downcall.trailer_size) {
88309aae
MM
221 gossip_err("pvfs2_readdir: # bytes decoded (%ld) "
222 "!= trailer size (%ld)\n",
223 bytes_decoded,
224 (long)new_op->downcall.trailer_size);
5db11c21
MM
225 ret = -EINVAL;
226 goto out_destroy_handle;
227 }
228
88309aae
MM
229 /*
230 * pvfs2 doesn't actually store dot and dot-dot, but
231 * we need to have them represented.
232 */
5db11c21
MM
233 if (pos == 0) {
234 ino = get_ino_from_khandle(dentry->d_inode);
235 gossip_debug(GOSSIP_DIR_DEBUG,
236 "%s: calling dir_emit of \".\" with pos = %llu\n",
237 __func__,
238 llu(pos));
239 ret = dir_emit(ctx, ".", 1, ino, DT_DIR);
88309aae 240 pos += 1;
5db11c21
MM
241 }
242
243 if (pos == 1) {
244 ino = get_parent_ino_from_dentry(dentry);
245 gossip_debug(GOSSIP_DIR_DEBUG,
246 "%s: calling dir_emit of \"..\" with pos = %llu\n",
247 __func__,
248 llu(pos));
249 ret = dir_emit(ctx, "..", 2, ino, DT_DIR);
88309aae 250 pos += 1;
5db11c21
MM
251 }
252
88309aae
MM
253 /*
254 * we stored PVFS_ITERATE_NEXT in ctx->pos last time around
255 * to prevent "finding" dot and dot-dot on any iteration
256 * other than the first.
257 */
258 if (ctx->pos == PVFS_ITERATE_NEXT)
259 ctx->pos = 0;
260
261 for (i = ctx->pos;
262 i < rhandle.readdir_response.pvfs_dirent_outcount;
263 i++) {
5db11c21
MM
264 len = rhandle.readdir_response.dirent_array[i].d_length;
265 current_entry = rhandle.readdir_response.dirent_array[i].d_name;
266 current_ino = pvfs2_khandle_to_ino(
267 &(rhandle.readdir_response.dirent_array[i].khandle));
268
269 gossip_debug(GOSSIP_DIR_DEBUG,
88309aae
MM
270 "calling dir_emit for %s with len %d"
271 ", ctx->pos %ld\n",
5db11c21
MM
272 current_entry,
273 len,
88309aae
MM
274 (unsigned long)ctx->pos);
275 /*
276 * type is unknown. We don't return object type
277 * in the dirent_array. This leaves getdents
278 * clueless about type.
279 */
5db11c21
MM
280 ret =
281 dir_emit(ctx, current_entry, len, current_ino, DT_UNKNOWN);
88309aae
MM
282 if (!ret)
283 break;
5db11c21 284 ctx->pos++;
88309aae 285 gossip_debug(GOSSIP_DIR_DEBUG,
5db11c21
MM
286 "%s: ctx->pos:%lld\n",
287 __func__,
288 lld(ctx->pos));
289
5db11c21
MM
290 }
291
88309aae
MM
292 /*
293 * we ran all the way through the last batch, set up for
294 * getting another batch...
295 */
296 if (ret) {
5db11c21 297 *ptoken = rhandle.readdir_response.token;
88309aae 298 ctx->pos = PVFS_ITERATE_NEXT;
5db11c21
MM
299 }
300
301 /*
302 * Did we hit the end of the directory?
303 */
304 if (rhandle.readdir_response.token == PVFS_READDIR_END &&
305 !buffer_full) {
88309aae
MM
306 gossip_debug(GOSSIP_DIR_DEBUG,
307 "End of dir detected; setting ctx->pos to PVFS_READDIR_END.\n");
5db11c21
MM
308 ctx->pos = PVFS_READDIR_END;
309 }
310
5db11c21
MM
311out_destroy_handle:
312 readdir_handle_dtor(bufmap, &rhandle);
313out_free_op:
314 op_release(new_op);
315 gossip_debug(GOSSIP_DIR_DEBUG, "pvfs2_readdir returning %d\n", ret);
316 return ret;
317}
318
319static int pvfs2_dir_open(struct inode *inode, struct file *file)
320{
321 __u64 *ptoken;
322
323 file->private_data = kmalloc(sizeof(__u64), GFP_KERNEL);
324 if (!file->private_data)
325 return -ENOMEM;
326
327 ptoken = file->private_data;
328 *ptoken = PVFS_READDIR_START;
329 return 0;
330}
331
332static int pvfs2_dir_release(struct inode *inode, struct file *file)
333{
334 pvfs2_flush_inode(inode);
335 kfree(file->private_data);
336 return 0;
337}
338
339/** PVFS2 implementation of VFS directory operations */
340const struct file_operations pvfs2_dir_operations = {
341 .read = generic_read_dir,
342 .iterate = pvfs2_readdir,
343 .open = pvfs2_dir_open,
344 .release = pvfs2_dir_release,
345};