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 vfs_readdir(struct file *file, filldir_t filler, void *buf)
  24{
  25        struct inode *inode = file->f_path.dentry->d_inode;
  26        int res = -ENOTDIR;
  27        if (!file->f_op || !file->f_op->readdir)
  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                res = file->f_op->readdir(file, buf, filler);
  41                file_accessed(file);
  42        }
  43        mutex_unlock(&inode->i_mutex);
  44out:
  45        return res;
  46}
  47
  48EXPORT_SYMBOL(vfs_readdir);
  49
  50/*
  51 * Traditional linux readdir() handling..
  52 *
  53 * "count=1" is a special case, meaning that the buffer is one
  54 * dirent-structure in size and that the code can't handle more
  55 * anyway. Thus the special "fillonedir()" function for that
  56 * case (the low-level handlers don't need to care about this).
  57 */
  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 fd f = fdget(fd);
 110        struct readdir_callback buf;
 111
 112        if (!f.file)
 113                return -EBADF;
 114
 115        buf.result = 0;
 116        buf.dirent = dirent;
 117
 118        error = vfs_readdir(f.file, fillonedir, &buf);
 119        if (buf.result)
 120                error = buf.result;
 121
 122        fdput(f);
 123        return error;
 124}
 125
 126#endif /* __ARCH_WANT_OLD_READDIR */
 127
 128/*
 129 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
 130 * interface. 
 131 */
 132struct linux_dirent {
 133        unsigned long   d_ino;
 134        unsigned long   d_off;
 135        unsigned short  d_reclen;
 136        char            d_name[1];
 137};
 138
 139struct getdents_callback {
 140        struct linux_dirent __user * current_dir;
 141        struct linux_dirent __user * previous;
 142        int count;
 143        int error;
 144};
 145
 146static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
 147                   u64 ino, unsigned int d_type)
 148{
 149        struct linux_dirent __user * dirent;
 150        struct getdents_callback * buf = (struct getdents_callback *) __buf;
 151        unsigned long d_ino;
 152        int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
 153                sizeof(long));
 154
 155        buf->error = -EINVAL;   /* only used if we fail.. */
 156        if (reclen > buf->count)
 157                return -EINVAL;
 158        d_ino = ino;
 159        if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
 160                buf->error = -EOVERFLOW;
 161                return -EOVERFLOW;
 162        }
 163        dirent = buf->previous;
 164        if (dirent) {
 165                if (__put_user(offset, &dirent->d_off))
 166                        goto efault;
 167        }
 168        dirent = buf->current_dir;
 169        if (__put_user(d_ino, &dirent->d_ino))
 170                goto efault;
 171        if (__put_user(reclen, &dirent->d_reclen))
 172                goto efault;
 173        if (copy_to_user(dirent->d_name, name, namlen))
 174                goto efault;
 175        if (__put_user(0, dirent->d_name + namlen))
 176                goto efault;
 177        if (__put_user(d_type, (char __user *) dirent + reclen - 1))
 178                goto efault;
 179        buf->previous = dirent;
 180        dirent = (void __user *)dirent + reclen;
 181        buf->current_dir = dirent;
 182        buf->count -= reclen;
 183        return 0;
 184efault:
 185        buf->error = -EFAULT;
 186        return -EFAULT;
 187}
 188
 189SYSCALL_DEFINE3(getdents, unsigned int, fd,
 190                struct linux_dirent __user *, dirent, unsigned int, count)
 191{
 192        struct fd f;
 193        struct linux_dirent __user * lastdirent;
 194        struct getdents_callback buf;
 195        int error;
 196
 197        if (!access_ok(VERIFY_WRITE, dirent, count))
 198                return -EFAULT;
 199
 200        f = fdget(fd);
 201        if (!f.file)
 202                return -EBADF;
 203
 204        buf.current_dir = dirent;
 205        buf.previous = NULL;
 206        buf.count = count;
 207        buf.error = 0;
 208
 209        error = vfs_readdir(f.file, filldir, &buf);
 210        if (error >= 0)
 211                error = buf.error;
 212        lastdirent = buf.previous;
 213        if (lastdirent) {
 214                if (put_user(f.file->f_pos, &lastdirent->d_off))
 215                        error = -EFAULT;
 216                else
 217                        error = count - buf.count;
 218        }
 219        fdput(f);
 220        return error;
 221}
 222
 223struct getdents_callback64 {
 224        struct linux_dirent64 __user * current_dir;
 225        struct linux_dirent64 __user * previous;
 226        int count;
 227        int error;
 228};
 229
 230static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
 231                     u64 ino, unsigned int d_type)
 232{
 233        struct linux_dirent64 __user *dirent;
 234        struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
 235        int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
 236                sizeof(u64));
 237
 238        buf->error = -EINVAL;   /* only used if we fail.. */
 239        if (reclen > buf->count)
 240                return -EINVAL;
 241        dirent = buf->previous;
 242        if (dirent) {
 243                if (__put_user(offset, &dirent->d_off))
 244                        goto efault;
 245        }
 246        dirent = buf->current_dir;
 247        if (__put_user(ino, &dirent->d_ino))
 248                goto efault;
 249        if (__put_user(0, &dirent->d_off))
 250                goto efault;
 251        if (__put_user(reclen, &dirent->d_reclen))
 252                goto efault;
 253        if (__put_user(d_type, &dirent->d_type))
 254                goto efault;
 255        if (copy_to_user(dirent->d_name, name, namlen))
 256                goto efault;
 257        if (__put_user(0, dirent->d_name + namlen))
 258                goto efault;
 259        buf->previous = dirent;
 260        dirent = (void __user *)dirent + reclen;
 261        buf->current_dir = dirent;
 262        buf->count -= reclen;
 263        return 0;
 264efault:
 265        buf->error = -EFAULT;
 266        return -EFAULT;
 267}
 268
 269SYSCALL_DEFINE3(getdents64, unsigned int, fd,
 270                struct linux_dirent64 __user *, dirent, unsigned int, count)
 271{
 272        struct fd f;
 273        struct linux_dirent64 __user * lastdirent;
 274        struct getdents_callback64 buf;
 275        int error;
 276
 277        if (!access_ok(VERIFY_WRITE, dirent, count))
 278                return -EFAULT;
 279
 280        f = fdget(fd);
 281        if (!f.file)
 282                return -EBADF;
 283
 284        buf.current_dir = dirent;
 285        buf.previous = NULL;
 286        buf.count = count;
 287        buf.error = 0;
 288
 289        error = vfs_readdir(f.file, filldir64, &buf);
 290        if (error >= 0)
 291                error = buf.error;
 292        lastdirent = buf.previous;
 293        if (lastdirent) {
 294                typeof(lastdirent->d_off) d_off = f.file->f_pos;
 295                if (__put_user(d_off, &lastdirent->d_off))
 296                        error = -EFAULT;
 297                else
 298                        error = count - buf.count;
 299        }
 300        fdput(f);
 301        return error;
 302}
 303
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.