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