linux/fs/readdir.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/readdir.c
   3 *
   4 *  Copyright (C) 1995  Linus Torvalds
   5 */
   6
   7#include <linux/stddef.h>
   8#include <linux/kernel.h>
   9#include <linux/export.h>
  10#include <linux/time.h>
  11#include <linux/mm.h>
  12#include <linux/errno.h>
  13#include <linux/stat.h>
  14#include <linux/file.h>
  15#include <linux/fs.h>
  16#include <linux/fsnotify.h>
  17#include <linux/dirent.h>
  18#include <linux/security.h>
  19#include <linux/syscalls.h>
  20#include <linux/unistd.h>
  21
  22#include <asm/uaccess.h>
  23
  24int iterate_dir(struct file *file, struct dir_context *ctx)
  25{
  26        struct inode *inode = file_inode(file);
  27        int res = -ENOTDIR;
  28        if (!file->f_op->iterate)
  29                goto out;
  30
  31        res = security_file_permission(file, MAY_READ);
  32        if (res)
  33                goto out;
  34
  35        res = mutex_lock_killable(&inode->i_mutex);
  36        if (res)
  37                goto out;
  38
  39        res = -ENOENT;
  40        if (!IS_DEADDIR(inode)) {
  41                ctx->pos = file->f_pos;
  42                res = file->f_op->iterate(file, ctx);
  43                file->f_pos = ctx->pos;
  44                fsnotify_access(file);
  45                file_accessed(file);
  46        }
  47        inode_unlock(inode);
  48out:
  49        return res;
  50}
  51EXPORT_SYMBOL(iterate_dir);
  52
  53/*
  54 * Traditional linux readdir() handling..
  55 *
  56 * "count=1" is a special case, meaning that the buffer is one
  57 * dirent-structure in size and that the code can't handle more
  58 * anyway. Thus the special "fillonedir()" function for that
  59 * case (the low-level handlers don't need to care about this).
  60 */
  61
  62#ifdef __ARCH_WANT_OLD_READDIR
  63
  64struct old_linux_dirent {
  65        unsigned long   d_ino;
  66        unsigned long   d_offset;
  67        unsigned short  d_namlen;
  68        char            d_name[1];
  69};
  70
  71struct readdir_callback {
  72        struct dir_context ctx;
  73        struct old_linux_dirent __user * dirent;
  74        int result;
  75};
  76
  77static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
  78                      loff_t offset, u64 ino, unsigned int d_type)
  79{
  80        struct readdir_callback *buf =
  81                container_of(ctx, struct readdir_callback, ctx);
  82        struct old_linux_dirent __user * dirent;
  83        unsigned long d_ino;
  84
  85        if (buf->result)
  86                return -EINVAL;
  87        d_ino = ino;
  88        if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
  89                buf->result = -EOVERFLOW;
  90                return -EOVERFLOW;
  91        }
  92        buf->result++;
  93        dirent = buf->dirent;
  94        if (!access_ok(VERIFY_WRITE, dirent,
  95                        (unsigned long)(dirent->d_name + namlen + 1) -
  96                                (unsigned long)dirent))
  97                goto efault;
  98        if (    __put_user(d_ino, &dirent->d_ino) ||
  99                __put_user(offset, &dirent->d_offset) ||
 100                __put_user(namlen, &dirent->d_namlen) ||
 101                __copy_to_user(dirent->d_name, name, namlen) ||
 102                __put_user(0, dirent->d_name + namlen))
 103                goto efault;
 104        return 0;
 105efault:
 106        buf->result = -EFAULT;
 107        return -EFAULT;
 108}
 109
 110SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
 111                struct old_linux_dirent __user *, dirent, unsigned int, count)
 112{
 113        int error;
 114        struct fd f = fdget(fd);
 115        struct readdir_callback buf = {
 116                .ctx.actor = fillonedir,
 117                .dirent = dirent
 118        };
 119
 120        if (!f.file)
 121                return -EBADF;
 122
 123        error = iterate_dir(f.file, &buf.ctx);
 124        if (buf.result)
 125                error = buf.result;
 126
 127        fdput(f);
 128        return error;
 129}
 130
 131#endif /* __ARCH_WANT_OLD_READDIR */
 132
 133/*
 134 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
 135 * interface. 
 136 */
 137struct linux_dirent {
 138        unsigned long   d_ino;
 139        unsigned long   d_off;
 140        unsigned short  d_reclen;
 141        char            d_name[1];
 142};
 143
 144struct getdents_callback {
 145        struct dir_context ctx;
 146        struct linux_dirent __user * current_dir;
 147        struct linux_dirent __user * previous;
 148        int count;
 149        int error;
 150};
 151
 152static int filldir(struct dir_context *ctx, const char *name, int namlen,
 153                   loff_t offset, u64 ino, unsigned int d_type)
 154{
 155        struct linux_dirent __user * dirent;
 156        struct getdents_callback *buf =
 157                container_of(ctx, struct getdents_callback, ctx);
 158        unsigned long d_ino;
 159        int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
 160                sizeof(long));
 161
 162        buf->error = -EINVAL;   /* only used if we fail.. */
 163        if (reclen > buf->count)
 164                return -EINVAL;
 165        d_ino = ino;
 166        if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
 167                buf->error = -EOVERFLOW;
 168                return -EOVERFLOW;
 169        }
 170        dirent = buf->previous;
 171        if (dirent) {
 172                if (__put_user(offset, &dirent->d_off))
 173                        goto efault;
 174        }
 175        dirent = buf->current_dir;
 176        if (__put_user(d_ino, &dirent->d_ino))
 177                goto efault;
 178        if (__put_user(reclen, &dirent->d_reclen))
 179                goto efault;
 180        if (copy_to_user(dirent->d_name, name, namlen))
 181                goto efault;
 182        if (__put_user(0, dirent->d_name + namlen))
 183                goto efault;
 184        if (__put_user(d_type, (char __user *) dirent + reclen - 1))
 185                goto efault;
 186        buf->previous = dirent;
 187        dirent = (void __user *)dirent + reclen;
 188        buf->current_dir = dirent;
 189        buf->count -= reclen;
 190        return 0;
 191efault:
 192        buf->error = -EFAULT;
 193        return -EFAULT;
 194}
 195
 196SYSCALL_DEFINE3(getdents, unsigned int, fd,
 197                struct linux_dirent __user *, dirent, unsigned int, count)
 198{
 199        struct fd f;
 200        struct linux_dirent __user * lastdirent;
 201        struct getdents_callback buf = {
 202                .ctx.actor = filldir,
 203                .count = count,
 204                .current_dir = dirent
 205        };
 206        int error;
 207
 208        if (!access_ok(VERIFY_WRITE, dirent, count))
 209                return -EFAULT;
 210
 211        f = fdget(fd);
 212        if (!f.file)
 213                return -EBADF;
 214
 215        error = iterate_dir(f.file, &buf.ctx);
 216        if (error >= 0)
 217                error = buf.error;
 218        lastdirent = buf.previous;
 219        if (lastdirent) {
 220                if (put_user(buf.ctx.pos, &lastdirent->d_off))
 221                        error = -EFAULT;
 222                else
 223                        error = count - buf.count;
 224        }
 225        fdput(f);
 226        return error;
 227}
 228
 229struct getdents_callback64 {
 230        struct dir_context ctx;
 231        struct linux_dirent64 __user * current_dir;
 232        struct linux_dirent64 __user * previous;
 233        int count;
 234        int error;
 235};
 236
 237static int filldir64(struct dir_context *ctx, const char *name, int namlen,
 238                     loff_t offset, u64 ino, unsigned int d_type)
 239{
 240        struct linux_dirent64 __user *dirent;
 241        struct getdents_callback64 *buf =
 242                container_of(ctx, struct getdents_callback64, ctx);
 243        int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
 244                sizeof(u64));
 245
 246        buf->error = -EINVAL;   /* only used if we fail.. */
 247        if (reclen > buf->count)
 248                return -EINVAL;
 249        dirent = buf->previous;
 250        if (dirent) {
 251                if (__put_user(offset, &dirent->d_off))
 252                        goto efault;
 253        }
 254        dirent = buf->current_dir;
 255        if (__put_user(ino, &dirent->d_ino))
 256                goto efault;
 257        if (__put_user(0, &dirent->d_off))
 258                goto efault;
 259        if (__put_user(reclen, &dirent->d_reclen))
 260                goto efault;
 261        if (__put_user(d_type, &dirent->d_type))
 262                goto efault;
 263        if (copy_to_user(dirent->d_name, name, namlen))
 264                goto efault;
 265        if (__put_user(0, dirent->d_name + namlen))
 266                goto efault;
 267        buf->previous = dirent;
 268        dirent = (void __user *)dirent + reclen;
 269        buf->current_dir = dirent;
 270        buf->count -= reclen;
 271        return 0;
 272efault:
 273        buf->error = -EFAULT;
 274        return -EFAULT;
 275}
 276
 277SYSCALL_DEFINE3(getdents64, unsigned int, fd,
 278                struct linux_dirent64 __user *, dirent, unsigned int, count)
 279{
 280        struct fd f;
 281        struct linux_dirent64 __user * lastdirent;
 282        struct getdents_callback64 buf = {
 283                .ctx.actor = filldir64,
 284                .count = count,
 285                .current_dir = dirent
 286        };
 287        int error;
 288
 289        if (!access_ok(VERIFY_WRITE, dirent, count))
 290                return -EFAULT;
 291
 292        f = fdget(fd);
 293        if (!f.file)
 294                return -EBADF;
 295
 296        error = iterate_dir(f.file, &buf.ctx);
 297        if (error >= 0)
 298                error = buf.error;
 299        lastdirent = buf.previous;
 300        if (lastdirent) {
 301                typeof(lastdirent->d_off) d_off = buf.ctx.pos;
 302                if (__put_user(d_off, &lastdirent->d_off))
 303                        error = -EFAULT;
 304                else
 305                        error = count - buf.count;
 306        }
 307        fdput(f);
 308        return error;
 309}
 310