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