]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - fs/orangefs/dir.c
Merge remote-tracking branch 'usb/usb-next'
[karo-tx-linux.git] / fs / orangefs / dir.c
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 "orangefs-kernel.h"
9 #include "orangefs-bufmap.h"
10
11 struct readdir_handle_s {
12         int buffer_index;
13         struct orangefs_readdir_response_s readdir_response;
14         void *dents_buf;
15 };
16
17 /*
18  * decode routine used by kmod to deal with the blob sent from
19  * userspace for readdirs. The blob contains zero or more of these
20  * sub-blobs:
21  *   __u32 - represents length of the character string that follows.
22  *   string - between 1 and ORANGEFS_NAME_MAX bytes long.
23  *   padding - (if needed) to cause the __u32 plus the string to be
24  *             eight byte aligned.
25  *   khandle - sizeof(khandle) bytes.
26  */
27 static long decode_dirents(char *ptr, size_t size,
28                            struct orangefs_readdir_response_s *readdir)
29 {
30         int i;
31         struct orangefs_readdir_response_s *rd =
32                 (struct orangefs_readdir_response_s *) ptr;
33         char *buf = ptr;
34         int khandle_size = sizeof(struct orangefs_khandle);
35         size_t offset = offsetof(struct orangefs_readdir_response_s,
36                                 dirent_array);
37         /* 8 reflects eight byte alignment */
38         int smallest_blob = khandle_size + 8;
39         __u32 len;
40         int aligned_len;
41         int sizeof_u32 = sizeof(__u32);
42         long ret;
43
44         gossip_debug(GOSSIP_DIR_DEBUG, "%s: size:%zu:\n", __func__, size);
45
46         /* size is = offset on empty dirs, > offset on non-empty dirs... */
47         if (size < offset) {
48                 gossip_err("%s: size:%zu: offset:%zu:\n",
49                            __func__,
50                            size,
51                            offset);
52                 ret = -EINVAL;
53                 goto out;
54         }
55
56         if ((size == offset) && (readdir->orangefs_dirent_outcount != 0)) {
57                 gossip_err("%s: size:%zu: dirent_outcount:%d:\n",
58                            __func__,
59                            size,
60                            readdir->orangefs_dirent_outcount);
61                 ret = -EINVAL;
62                 goto out;
63         }
64
65         readdir->token = rd->token;
66         readdir->orangefs_dirent_outcount = rd->orangefs_dirent_outcount;
67         readdir->dirent_array = kcalloc(readdir->orangefs_dirent_outcount,
68                                         sizeof(*readdir->dirent_array),
69                                         GFP_KERNEL);
70         if (readdir->dirent_array == NULL) {
71                 gossip_err("%s: kcalloc failed.\n", __func__);
72                 ret = -ENOMEM;
73                 goto out;
74         }
75
76         buf += offset;
77         size -= offset;
78
79         for (i = 0; i < readdir->orangefs_dirent_outcount; i++) {
80                 if (size < smallest_blob) {
81                         gossip_err("%s: size:%zu: smallest_blob:%d:\n",
82                                    __func__,
83                                    size,
84                                    smallest_blob);
85                         ret = -EINVAL;
86                         goto free;
87                 }
88
89                 len = *(__u32 *)buf;
90                 if ((len < 1) || (len > ORANGEFS_NAME_MAX)) {
91                         gossip_err("%s: len:%d:\n", __func__, len);
92                         ret = -EINVAL;
93                         goto free;
94                 }
95
96                 gossip_debug(GOSSIP_DIR_DEBUG,
97                              "%s: size:%zu: len:%d:\n",
98                              __func__,
99                              size,
100                              len);
101
102                 readdir->dirent_array[i].d_name = buf + sizeof_u32;
103                 readdir->dirent_array[i].d_length = len;
104
105                 /*
106                  * Calculate "aligned" length of this string and its
107                  * associated __u32 descriptor.
108                  */
109                 aligned_len = ((sizeof_u32 + len + 1) + 7) & ~7;
110                 gossip_debug(GOSSIP_DIR_DEBUG,
111                              "%s: aligned_len:%d:\n",
112                              __func__,
113                              aligned_len);
114
115                 /*
116                  * The end of the blob should coincide with the end
117                  * of the last sub-blob.
118                  */
119                 if (size < aligned_len + khandle_size) {
120                         gossip_err("%s: ran off the end of the blob.\n",
121                                    __func__);
122                         ret = -EINVAL;
123                         goto free;
124                 }
125                 size -= aligned_len + khandle_size;
126
127                 buf += aligned_len;
128
129                 readdir->dirent_array[i].khandle =
130                         *(struct orangefs_khandle *) buf;
131                 buf += khandle_size;
132         }
133         ret = buf - ptr;
134         gossip_debug(GOSSIP_DIR_DEBUG, "%s: returning:%ld:\n", __func__, ret);
135         goto out;
136
137 free:
138         kfree(readdir->dirent_array);
139         readdir->dirent_array = NULL;
140
141 out:
142         return ret;
143 }
144
145 static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf,
146                                 size_t size, int buffer_index)
147 {
148         long ret;
149
150         if (buf == NULL) {
151                 gossip_err
152                     ("Invalid NULL buffer specified in readdir_handle_ctor\n");
153                 return -ENOMEM;
154         }
155         if (buffer_index < 0) {
156                 gossip_err
157                     ("Invalid buffer index specified in readdir_handle_ctor\n");
158                 return -EINVAL;
159         }
160         rhandle->buffer_index = buffer_index;
161         rhandle->dents_buf = buf;
162         ret = decode_dirents(buf, size, &rhandle->readdir_response);
163         if (ret < 0) {
164                 gossip_err("Could not decode readdir from buffer %ld\n", ret);
165                 rhandle->buffer_index = -1;
166                 gossip_debug(GOSSIP_DIR_DEBUG, "vfree %p\n", buf);
167                 vfree(buf);
168                 rhandle->dents_buf = NULL;
169         }
170         return ret;
171 }
172
173 static void readdir_handle_dtor(struct orangefs_bufmap *bufmap,
174                 struct readdir_handle_s *rhandle)
175 {
176         if (rhandle == NULL)
177                 return;
178
179         /* kfree(NULL) is safe */
180         kfree(rhandle->readdir_response.dirent_array);
181         rhandle->readdir_response.dirent_array = NULL;
182
183         if (rhandle->buffer_index >= 0) {
184                 orangefs_readdir_index_put(bufmap, rhandle->buffer_index);
185                 rhandle->buffer_index = -1;
186         }
187         if (rhandle->dents_buf) {
188                 gossip_debug(GOSSIP_DIR_DEBUG, "vfree %p\n",
189                              rhandle->dents_buf);
190                 vfree(rhandle->dents_buf);
191                 rhandle->dents_buf = NULL;
192         }
193 }
194
195 /*
196  * Read directory entries from an instance of an open directory.
197  */
198 static int orangefs_readdir(struct file *file, struct dir_context *ctx)
199 {
200         struct orangefs_bufmap *bufmap = NULL;
201         int ret = 0;
202         int buffer_index;
203         /*
204          * ptoken supports Orangefs' distributed directory logic, added
205          * in 2.9.2.
206          */
207         __u64 *ptoken = file->private_data;
208         __u64 pos = 0;
209         ino_t ino = 0;
210         struct dentry *dentry = file->f_path.dentry;
211         struct orangefs_kernel_op_s *new_op = NULL;
212         struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(dentry->d_inode);
213         int buffer_full = 0;
214         struct readdir_handle_s rhandle;
215         int i = 0;
216         int len = 0;
217         ino_t current_ino = 0;
218         char *current_entry = NULL;
219         long bytes_decoded;
220
221         gossip_debug(GOSSIP_DIR_DEBUG,
222                      "%s: ctx->pos:%lld, ptoken = %llu\n",
223                      __func__,
224                      lld(ctx->pos),
225                      llu(*ptoken));
226
227         pos = (__u64) ctx->pos;
228
229         /* are we done? */
230         if (pos == ORANGEFS_READDIR_END) {
231                 gossip_debug(GOSSIP_DIR_DEBUG,
232                              "Skipping to termination path\n");
233                 return 0;
234         }
235
236         gossip_debug(GOSSIP_DIR_DEBUG,
237                      "orangefs_readdir called on %s (pos=%llu)\n",
238                      dentry->d_name.name, llu(pos));
239
240         rhandle.buffer_index = -1;
241         rhandle.dents_buf = NULL;
242         memset(&rhandle.readdir_response, 0, sizeof(rhandle.readdir_response));
243
244         new_op = op_alloc(ORANGEFS_VFS_OP_READDIR);
245         if (!new_op)
246                 return -ENOMEM;
247
248         new_op->uses_shared_memory = 1;
249         new_op->upcall.req.readdir.refn = orangefs_inode->refn;
250         new_op->upcall.req.readdir.max_dirent_count =
251             ORANGEFS_MAX_DIRENT_COUNT_READDIR;
252
253         gossip_debug(GOSSIP_DIR_DEBUG,
254                      "%s: upcall.req.readdir.refn.khandle: %pU\n",
255                      __func__,
256                      &new_op->upcall.req.readdir.refn.khandle);
257
258         new_op->upcall.req.readdir.token = *ptoken;
259
260 get_new_buffer_index:
261         ret = orangefs_readdir_index_get(&bufmap, &buffer_index);
262         if (ret < 0) {
263                 gossip_lerr("orangefs_readdir: orangefs_readdir_index_get() failure (%d)\n",
264                             ret);
265                 goto out_free_op;
266         }
267         new_op->upcall.req.readdir.buf_index = buffer_index;
268
269         ret = service_operation(new_op,
270                                 "orangefs_readdir",
271                                 get_interruptible_flag(dentry->d_inode));
272
273         gossip_debug(GOSSIP_DIR_DEBUG,
274                      "Readdir downcall status is %d.  ret:%d\n",
275                      new_op->downcall.status,
276                      ret);
277
278         if (ret == -EAGAIN && op_state_purged(new_op)) {
279                 /*
280                  * readdir shared memory aread has been wiped due to
281                  * pvfs2-client-core restarting, so we must get a new
282                  * index into the shared memory.
283                  */
284                 gossip_debug(GOSSIP_DIR_DEBUG,
285                         "%s: Getting new buffer_index for retry of readdir..\n",
286                          __func__);
287                 orangefs_readdir_index_put(bufmap, buffer_index);
288                 goto get_new_buffer_index;
289         }
290
291         if (ret == -EIO && op_state_purged(new_op)) {
292                 gossip_err("%s: Client is down. Aborting readdir call.\n",
293                         __func__);
294                 orangefs_readdir_index_put(bufmap, buffer_index);
295                 goto out_free_op;
296         }
297
298         if (ret < 0 || new_op->downcall.status != 0) {
299                 gossip_debug(GOSSIP_DIR_DEBUG,
300                              "Readdir request failed.  Status:%d\n",
301                              new_op->downcall.status);
302                 orangefs_readdir_index_put(bufmap, buffer_index);
303                 if (ret >= 0)
304                         ret = new_op->downcall.status;
305                 goto out_free_op;
306         }
307
308         bytes_decoded =
309                 readdir_handle_ctor(&rhandle,
310                                     new_op->downcall.trailer_buf,
311                                     new_op->downcall.trailer_size,
312                                     buffer_index);
313         if (bytes_decoded < 0) {
314                 gossip_err("orangefs_readdir: Could not decode trailer buffer into a readdir response %d\n",
315                         ret);
316                 ret = bytes_decoded;
317                 orangefs_readdir_index_put(bufmap, buffer_index);
318                 goto out_free_op;
319         }
320
321         if (bytes_decoded != new_op->downcall.trailer_size) {
322                 gossip_err("orangefs_readdir: # bytes decoded (%ld) "
323                            "!= trailer size (%ld)\n",
324                            bytes_decoded,
325                            (long)new_op->downcall.trailer_size);
326                 ret = -EINVAL;
327                 goto out_destroy_handle;
328         }
329
330         /*
331          *  orangefs doesn't actually store dot and dot-dot, but
332          *  we need to have them represented.
333          */
334         if (pos == 0) {
335                 ino = get_ino_from_khandle(dentry->d_inode);
336                 gossip_debug(GOSSIP_DIR_DEBUG,
337                              "%s: calling dir_emit of \".\" with pos = %llu\n",
338                              __func__,
339                              llu(pos));
340                 ret = dir_emit(ctx, ".", 1, ino, DT_DIR);
341                 pos += 1;
342         }
343
344         if (pos == 1) {
345                 ino = get_parent_ino_from_dentry(dentry);
346                 gossip_debug(GOSSIP_DIR_DEBUG,
347                              "%s: calling dir_emit of \"..\" with pos = %llu\n",
348                              __func__,
349                              llu(pos));
350                 ret = dir_emit(ctx, "..", 2, ino, DT_DIR);
351                 pos += 1;
352         }
353
354         /*
355          * we stored ORANGEFS_ITERATE_NEXT in ctx->pos last time around
356          * to prevent "finding" dot and dot-dot on any iteration
357          * other than the first.
358          */
359         if (ctx->pos == ORANGEFS_ITERATE_NEXT)
360                 ctx->pos = 0;
361
362         for (i = ctx->pos;
363              i < rhandle.readdir_response.orangefs_dirent_outcount;
364              i++) {
365                 len = rhandle.readdir_response.dirent_array[i].d_length;
366                 current_entry = rhandle.readdir_response.dirent_array[i].d_name;
367                 current_ino = orangefs_khandle_to_ino(
368                         &(rhandle.readdir_response.dirent_array[i].khandle));
369
370                 gossip_debug(GOSSIP_DIR_DEBUG,
371                              "calling dir_emit for %s with len %d"
372                              ", ctx->pos %ld\n",
373                              current_entry,
374                              len,
375                              (unsigned long)ctx->pos);
376                 /*
377                  * type is unknown. We don't return object type
378                  * in the dirent_array. This leaves getdents
379                  * clueless about type.
380                  */
381                 ret =
382                     dir_emit(ctx, current_entry, len, current_ino, DT_UNKNOWN);
383                 if (!ret)
384                         break;
385                 ctx->pos++;
386                 gossip_debug(GOSSIP_DIR_DEBUG,
387                               "%s: ctx->pos:%lld\n",
388                               __func__,
389                               lld(ctx->pos));
390
391         }
392
393         /*
394          * we ran all the way through the last batch, set up for
395          * getting another batch...
396          */
397         if (ret) {
398                 *ptoken = rhandle.readdir_response.token;
399                 ctx->pos = ORANGEFS_ITERATE_NEXT;
400         }
401
402         /*
403          * Did we hit the end of the directory?
404          */
405         if (rhandle.readdir_response.token == ORANGEFS_READDIR_END &&
406             !buffer_full) {
407                 gossip_debug(GOSSIP_DIR_DEBUG,
408                 "End of dir detected; setting ctx->pos to ORANGEFS_READDIR_END.\n");
409                 ctx->pos = ORANGEFS_READDIR_END;
410         }
411
412 out_destroy_handle:
413         readdir_handle_dtor(bufmap, &rhandle);
414 out_free_op:
415         op_release(new_op);
416         gossip_debug(GOSSIP_DIR_DEBUG, "orangefs_readdir returning %d\n", ret);
417         return ret;
418 }
419
420 static int orangefs_dir_open(struct inode *inode, struct file *file)
421 {
422         __u64 *ptoken;
423
424         file->private_data = kmalloc(sizeof(__u64), GFP_KERNEL);
425         if (!file->private_data)
426                 return -ENOMEM;
427
428         ptoken = file->private_data;
429         *ptoken = ORANGEFS_READDIR_START;
430         return 0;
431 }
432
433 static int orangefs_dir_release(struct inode *inode, struct file *file)
434 {
435         orangefs_flush_inode(inode);
436         kfree(file->private_data);
437         return 0;
438 }
439
440 /** ORANGEFS implementation of VFS directory operations */
441 const struct file_operations orangefs_dir_operations = {
442         .read = generic_read_dir,
443         .iterate = orangefs_readdir,
444         .open = orangefs_dir_open,
445         .release = orangefs_dir_release,
446 };