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        mutex_unlock(&inode->i_mutex);
  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(void * __buf, const char * name, int namlen, loff_t offset,
  78                      u64 ino, unsigned int d_type)
  79{
  80        struct readdir_callback *buf = (struct readdir_callback *) __buf;
  81        struct old_linux_dirent __user * dirent;
  82        unsigned long d_ino;
  83
  84        if (buf->result)
  85                return -EINVAL;
  86        d_ino = ino;
  87        if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
  88                buf->result = -EOVERFLOW;
  89                return -EOVERFLOW;
  90        }
  91        buf->result++;
  92        dirent = buf->dirent;
  93        if (!access_ok(VERIFY_WRITE, dirent,
  94                        (unsigned long)(dirent->d_name + namlen + 1) -
  95                                (unsigned long)dirent))
  96                goto efault;
  97        if (    __put_user(d_ino, &dirent->d_ino) ||
  98                __put_user(offset, &dirent->d_offset) ||
  99                __put_user(namlen, &dirent->d_namlen) ||
 100                __copy_to_user(dirent->d_name, name, namlen) ||
 101                __put_user(0, dirent->d_name + namlen))
 102                goto efault;
 103        return 0;
 104efault:
 105        buf->result = -EFAULT;
 106        return -EFAULT;
 107}
 108
 109SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
 110                struct old_linux_dirent __user *, dirent, unsigned int, count)
 111{
 112        int error;
 113        struct fd f = fdget(fd);
 114        struct readdir_callback buf = {
 115                .ctx.actor = fillonedir,
 116                .dirent = dirent
 117        };
 118
 119        if (!f.file)
 120                return -EBADF;
 121
 122        error = iterate_dir(f.file, &buf.ctx);
 123        if (buf.result)
 124                error = buf.result;
 125
 126        fdput(f);
 127        return error;
 128}
 129
 130#endif /* __ARCH_WANT_OLD_READDIR */
 131
 132/*
 133 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
 134 * interface. 
 135 */
 136struct linux_dirent {
 137        unsigned long   d_ino;
 138        unsigned long   d_off;
 139        unsigned short  d_reclen;
 140        char            d_name[1];
 141};
 142
 143struct getdents_callback {
 144        struct dir_context ctx;
 145        struct linux_dirent __user * current_dir;
 146        struct linux_dirent __user * previous;
 147        int count;
 148        int error;
 149};
 150
 151static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
 152                   u64 ino, unsigned int d_type)
 153{
 154        struct linux_dirent __user * dirent;
 155        struct getdents_callback * buf = (struct getdents_callback *) __buf;
 156        unsigned long d_ino;
 157        int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
 158                sizeof(long));
 159
 160        buf->error = -EINVAL;   /* only used if we fail.. */
 161        if (reclen > buf->count)
 162                return -EINVAL;
 163        d_ino = ino;
 164        if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
 165                buf->error = -EOVERFLOW;
 166                return -EOVERFLOW;
 167        }
 168        dirent = buf->previous;
 169        if (dirent) {
 170                if (__put_user(offset, &dirent->d_off))
 171                        goto efault;
 172        }
 173        dirent = buf->current_dir;
 174        if (__put_user(d_ino, &dirent->d_ino))
 175                goto efault;
 176        if (__put_user(reclen, &dirent->d_reclen))
 177                goto efault;
 178        if (copy_to_user(dirent->d_name, name, namlen))
 179                goto efault;
 180        if (__put_user(0, dirent->d_name + namlen))
 181                goto efault;
 182        if (__put_user(d_type, (char __user *) dirent + reclen - 1))
 183                goto efault;
 184        buf->previous = dirent;
 185        dirent = (void __user *)dirent + reclen;
 186        buf->current_dir = dirent;
 187        buf->count -= reclen;
 188        return 0;
 189efault:
 190        buf->error = -EFAULT;
 191        return -EFAULT;
 192}
 193
 194SYSCALL_DEFINE3(getdents, unsigned int, fd,
 195                struct linux_dirent __user *, dirent, unsigned int, count)
 196{
 197        struct fd f;
 198        struct linux_dirent __user * lastdirent;
 199        struct getdents_callback buf = {
 200                .ctx.actor = filldir,
 201                .count = count,
 202                .current_dir = dirent
 203        };
 204        int error;
 205
 206        if (!access_ok(VERIFY_WRITE, dirent, count))
 207                return -EFAULT;
 208
 209        f = fdget(fd);
 210        if (!f.file)
 211                return -EBADF;
 212
 213        error = iterate_dir(f.file, &buf.ctx);
 214        if (error >= 0)
 215                error = buf.error;
 216        lastdirent = buf.previous;
 217        if (lastdirent) {
 218                if (put_user(buf.ctx.pos, &lastdirent->d_off))
 219                        error = -EFAULT;
 220                else
 221                        error = count - buf.count;
 222        }
 223        fdput(f);
 224        return error;
 225}
 226
 227struct getdents_callback64 {
 228        struct dir_context ctx;
 229        struct linux_dirent64 __user * current_dir;
 230        struct linux_dirent64 __user * previous;
 231        int count;
 232        int error;
 233};
 234
 235static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
 236                     u64 ino, unsigned int d_type)
 237{
 238        struct linux_dirent64 __user *dirent;
 239        struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
 240        int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
 241                sizeof(u64));
 242
 243        buf->error = -EINVAL;   /* only used if we fail.. */
 244        if (reclen > buf->count)
 245                return -EINVAL;
 246        dirent = buf->previous;
 247        if (dirent) {
 248                if (__put_user(offset, &dirent->d_off))
 249                        goto efault;
 250        }
 251        dirent = buf->current_dir;
 252        if (__put_user(ino, &dirent->d_ino))
 253                goto efault;
 254        if (__put_user(0, &dirent->d_off))
 255                goto efault;
 256        if (__put_user(reclen, &dirent->d_reclen))
 257                goto efault;
 258        if (__put_user(d_type, &dirent->d_type))
 259                goto efault;
 260        if (copy_to_user(dirent->d_name, name, namlen))
 261                goto efault;
 262        if (__put_user(0, dirent->d_name + namlen))
 263                goto efault;
 264        buf->previous = dirent;
 265        dirent = (void __user *)dirent + reclen;
 266        buf->current_dir = dirent;
 267        buf->count -= reclen;
 268        return 0;
 269efault:
 270        buf->error = -EFAULT;
 271        return -EFAULT;
 272}
 273
 274SYSCALL_DEFINE3(getdents64, unsigned int, fd,
 275                struct linux_dirent64 __user *, dirent, unsigned int, count)
 276{
 277        struct fd f;
 278        struct linux_dirent64 __user * lastdirent;
 279        struct getdents_callback64 buf = {
 280                .ctx.actor = filldir64,
 281                .count = count,
 282                .current_dir = dirent
 283        };
 284        int error;
 285
 286        if (!access_ok(VERIFY_WRITE, dirent, count))
 287                return -EFAULT;
 288
 289        f = fdget(fd);
 290        if (!f.file)
 291                return -EBADF;
 292
 293        error = iterate_dir(f.file, &buf.ctx);
 294        if (error >= 0)
 295                error = buf.error;
 296        lastdirent = buf.previous;
 297        if (lastdirent) {
 298                typeof(lastdirent->d_off) d_off = buf.ctx.pos;
 299                if (__put_user(d_off, &lastdirent->d_off))
 300                        error = -EFAULT;
 301                else
 302                        error = count - buf.count;
 303        }
 304        fdput(f);
 305        return error;
 306}
 307