linux-bk/fs/readdir.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/readdir.c
   3 *
   4 *  Copyright (C) 1995  Linus Torvalds
   5 */
   6
   7#include <linux/time.h>
   8#include <linux/mm.h>
   9#include <linux/errno.h>
  10#include <linux/stat.h>
  11#include <linux/file.h>
  12#include <linux/smp_lock.h>
  13#include <linux/fs.h>
  14
  15#include <asm/uaccess.h>
  16
  17int vfs_readdir(struct file *file, filldir_t filler, void *buf)
  18{
  19        struct inode *inode = file->f_dentry->d_inode;
  20        int res = -ENOTDIR;
  21        if (!file->f_op || !file->f_op->readdir)
  22                goto out;
  23
  24        res = security_ops->file_permission(file, MAY_READ);
  25        if (res)
  26                goto out;
  27
  28        down(&inode->i_sem);
  29        res = -ENOENT;
  30        if (!IS_DEADDIR(inode)) {
  31                res = file->f_op->readdir(file, buf, filler);
  32        }
  33        up(&inode->i_sem);
  34out:
  35        return res;
  36}
  37
  38/*
  39 * Traditional linux readdir() handling..
  40 *
  41 * "count=1" is a special case, meaning that the buffer is one
  42 * dirent-structure in size and that the code can't handle more
  43 * anyway. Thus the special "fillonedir()" function for that
  44 * case (the low-level handlers don't need to care about this).
  45 */
  46#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
  47#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
  48
  49#ifndef __ia64__
  50
  51struct old_linux_dirent {
  52        unsigned long   d_ino;
  53        unsigned long   d_offset;
  54        unsigned short  d_namlen;
  55        char            d_name[1];
  56};
  57
  58struct readdir_callback {
  59        struct old_linux_dirent * dirent;
  60        int count;
  61};
  62
  63static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
  64                      ino_t ino, unsigned int d_type)
  65{
  66        struct readdir_callback * buf = (struct readdir_callback *) __buf;
  67        struct old_linux_dirent * dirent;
  68
  69        if (buf->count)
  70                return -EINVAL;
  71        buf->count++;
  72        dirent = buf->dirent;
  73        put_user(ino, &dirent->d_ino);
  74        put_user(offset, &dirent->d_offset);
  75        put_user(namlen, &dirent->d_namlen);
  76        copy_to_user(dirent->d_name, name, namlen);
  77        put_user(0, dirent->d_name + namlen);
  78        return 0;
  79}
  80
  81asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
  82{
  83        int error;
  84        struct file * file;
  85        struct readdir_callback buf;
  86
  87        error = -EBADF;
  88        file = fget(fd);
  89        if (!file)
  90                goto out;
  91
  92        buf.count = 0;
  93        buf.dirent = dirent;
  94
  95        error = vfs_readdir(file, fillonedir, &buf);
  96        if (error >= 0)
  97                error = buf.count;
  98
  99        fput(file);
 100out:
 101        return error;
 102}
 103
 104#endif /* !__ia64__ */
 105
 106/*
 107 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
 108 * interface. 
 109 */
 110struct linux_dirent {
 111        unsigned long   d_ino;
 112        unsigned long   d_off;
 113        unsigned short  d_reclen;
 114        char            d_name[1];
 115};
 116
 117struct getdents_callback {
 118        struct linux_dirent * current_dir;
 119        struct linux_dirent * previous;
 120        int count;
 121        int error;
 122};
 123
 124static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
 125                   ino_t ino, unsigned int d_type)
 126{
 127        struct linux_dirent * dirent;
 128        struct getdents_callback * buf = (struct getdents_callback *) __buf;
 129        int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
 130
 131        buf->error = -EINVAL;   /* only used if we fail.. */
 132        if (reclen > buf->count)
 133                return -EINVAL;
 134        dirent = buf->previous;
 135        if (dirent)
 136                put_user(offset, &dirent->d_off);
 137        dirent = buf->current_dir;
 138        buf->previous = dirent;
 139        put_user(ino, &dirent->d_ino);
 140        put_user(reclen, &dirent->d_reclen);
 141        copy_to_user(dirent->d_name, name, namlen);
 142        put_user(0, dirent->d_name + namlen);
 143        ((char *) dirent) += reclen;
 144        buf->current_dir = dirent;
 145        buf->count -= reclen;
 146        return 0;
 147}
 148
 149asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
 150{
 151        struct file * file;
 152        struct linux_dirent * lastdirent;
 153        struct getdents_callback buf;
 154        int error;
 155
 156        error = -EBADF;
 157        file = fget(fd);
 158        if (!file)
 159                goto out;
 160
 161        buf.current_dir = (struct linux_dirent *) dirent;
 162        buf.previous = NULL;
 163        buf.count = count;
 164        buf.error = 0;
 165
 166        error = vfs_readdir(file, filldir, &buf);
 167        if (error < 0)
 168                goto out_putf;
 169        error = buf.error;
 170        lastdirent = buf.previous;
 171        if (lastdirent) {
 172                put_user(file->f_pos, &lastdirent->d_off);
 173                error = count - buf.count;
 174        }
 175
 176out_putf:
 177        fput(file);
 178out:
 179        return error;
 180}
 181
 182/*
 183 * And even better one including d_type field and 64bit d_ino and d_off.
 184 */
 185struct linux_dirent64 {
 186        u64             d_ino;
 187        s64             d_off;
 188        unsigned short  d_reclen;
 189        unsigned char   d_type;
 190        char            d_name[0];
 191};
 192
 193#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
 194
 195struct getdents_callback64 {
 196        struct linux_dirent64 * current_dir;
 197        struct linux_dirent64 * previous;
 198        int count;
 199        int error;
 200};
 201
 202static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
 203                     ino_t ino, unsigned int d_type)
 204{
 205        struct linux_dirent64 * dirent, d;
 206        struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
 207        int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
 208
 209        buf->error = -EINVAL;   /* only used if we fail.. */
 210        if (reclen > buf->count)
 211                return -EINVAL;
 212        dirent = buf->previous;
 213        if (dirent) {
 214                d.d_off = offset;
 215                copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
 216        }
 217        dirent = buf->current_dir;
 218        buf->previous = dirent;
 219        memset(&d, 0, NAME_OFFSET(&d));
 220        d.d_ino = ino;
 221        d.d_reclen = reclen;
 222        d.d_type = d_type;
 223        copy_to_user(dirent, &d, NAME_OFFSET(&d));
 224        copy_to_user(dirent->d_name, name, namlen);
 225        put_user(0, dirent->d_name + namlen);
 226        ((char *) dirent) += reclen;
 227        buf->current_dir = dirent;
 228        buf->count -= reclen;
 229        return 0;
 230}
 231
 232asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count)
 233{
 234        struct file * file;
 235        struct linux_dirent64 * lastdirent;
 236        struct getdents_callback64 buf;
 237        int error;
 238
 239        error = -EBADF;
 240        file = fget(fd);
 241        if (!file)
 242                goto out;
 243
 244        buf.current_dir = (struct linux_dirent64 *) dirent;
 245        buf.previous = NULL;
 246        buf.count = count;
 247        buf.error = 0;
 248
 249        error = vfs_readdir(file, filldir64, &buf);
 250        if (error < 0)
 251                goto out_putf;
 252        error = buf.error;
 253        lastdirent = buf.previous;
 254        if (lastdirent) {
 255                struct linux_dirent64 d;
 256                d.d_off = file->f_pos;
 257                copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off));
 258                error = count - buf.count;
 259        }
 260
 261out_putf:
 262        fput(file);
 263out:
 264        return error;
 265}
 266
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.