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