linux-old/fs/readdir.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/readdir.c
   3 *
   4 *  Copyright (C) 1995  Linus Torvalds
   5 */
   6
   7#include <linux/sched.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
  14#include <asm/uaccess.h>
  15
  16int vfs_readdir(struct file *file, filldir_t filler, void *buf)
  17{
  18        struct inode *inode = file->f_dentry->d_inode;
  19        int res = -ENOTDIR;
  20        if (!file->f_op || !file->f_op->readdir)
  21                goto out;
  22        down(&inode->i_sem);
  23        down(&inode->i_zombie);
  24        res = -ENOENT;
  25        if (!IS_DEADDIR(inode)) {
  26                lock_kernel();
  27                res = file->f_op->readdir(file, buf, filler);
  28                unlock_kernel();
  29        }
  30        up(&inode->i_zombie);
  31        up(&inode->i_sem);
  32out:
  33        return res;
  34}
  35
  36int dcache_dir_open(struct inode *inode, struct file *file)
  37{
  38        static struct qstr cursor_name = {len:1, name:"."};
  39
  40        file->private_data = d_alloc(file->f_dentry, &cursor_name);
  41
  42        return file->private_data ? 0 : -ENOMEM;
  43}
  44
  45int dcache_dir_close(struct inode *inode, struct file *file)
  46{
  47        dput(file->private_data);
  48        return 0;
  49}
  50
  51loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
  52{
  53        down(&file->f_dentry->d_inode->i_sem);
  54        switch (origin) {
  55                case 1:
  56                        offset += file->f_pos;
  57                case 0:
  58                        if (offset >= 0)
  59                                break;
  60                default:
  61                        up(&file->f_dentry->d_inode->i_sem);
  62                        return -EINVAL;
  63        }
  64        if (offset != file->f_pos) {
  65                file->f_pos = offset;
  66                if (file->f_pos >= 2) {
  67                        struct list_head *p;
  68                        struct dentry *cursor = file->private_data;
  69                        loff_t n = file->f_pos - 2;
  70
  71                        spin_lock(&dcache_lock);
  72                        p = file->f_dentry->d_subdirs.next;
  73                        while (n && p != &file->f_dentry->d_subdirs) {
  74                                struct dentry *next;
  75                                next = list_entry(p, struct dentry, d_child);
  76                                if (!list_empty(&next->d_hash) && next->d_inode)
  77                                        n--;
  78                                p = p->next;
  79                        }
  80                        list_del(&cursor->d_child);
  81                        list_add_tail(&cursor->d_child, p);
  82                        spin_unlock(&dcache_lock);
  83                }
  84        }
  85        up(&file->f_dentry->d_inode->i_sem);
  86        return offset;
  87}
  88
  89int dcache_dir_fsync(struct file * file, struct dentry *dentry, int datasync)
  90{
  91        return 0;
  92}
  93
  94/*
  95 * Directory is locked and all positive dentries in it are safe, since
  96 * for ramfs-type trees they can't go away without unlink() or rmdir(),
  97 * both impossible due to the lock on directory.
  98 */
  99
 100int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
 101{
 102        struct dentry *dentry = filp->f_dentry;
 103        struct dentry *cursor = filp->private_data;
 104        struct list_head *p, *q = &cursor->d_child;
 105        ino_t ino;
 106        int i = filp->f_pos;
 107
 108        switch (i) {
 109                case 0:
 110                        ino = dentry->d_inode->i_ino;
 111                        if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
 112                                break;
 113                        filp->f_pos++;
 114                        i++;
 115                        /* fallthrough */
 116                case 1:
 117                        spin_lock(&dcache_lock);
 118                        ino = dentry->d_parent->d_inode->i_ino;
 119                        spin_unlock(&dcache_lock);
 120                        if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
 121                                break;
 122                        filp->f_pos++;
 123                        i++;
 124                        /* fallthrough */
 125                default:
 126                        spin_lock(&dcache_lock);
 127                        if (filp->f_pos == 2) {
 128                                list_del(q);
 129                                list_add(q, &dentry->d_subdirs);
 130                        }
 131                        for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
 132                                struct dentry *next;
 133                                next = list_entry(p, struct dentry, d_child);
 134                                if (list_empty(&next->d_hash) || !next->d_inode)
 135                                        continue;
 136
 137                                spin_unlock(&dcache_lock);
 138                                if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, DT_UNKNOWN) < 0)
 139                                        return 0;
 140                                spin_lock(&dcache_lock);
 141                                /* next is still alive */
 142                                list_del(q);
 143                                list_add(q, p);
 144                                p = q;
 145                                filp->f_pos++;
 146                        }
 147                        spin_unlock(&dcache_lock);
 148        }
 149        return 0;
 150}
 151
 152struct file_operations dcache_dir_ops = {
 153        open:           dcache_dir_open,
 154        release:        dcache_dir_close,
 155        llseek:         dcache_dir_lseek,
 156        read:           generic_read_dir,
 157        readdir:        dcache_readdir,
 158        fsync:          dcache_dir_fsync,
 159};
 160
 161/*
 162 * Traditional linux readdir() handling..
 163 *
 164 * "count=1" is a special case, meaning that the buffer is one
 165 * dirent-structure in size and that the code can't handle more
 166 * anyway. Thus the special "fillonedir()" function for that
 167 * case (the low-level handlers don't need to care about this).
 168 */
 169#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
 170#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
 171
 172#ifndef __ia64__
 173
 174struct old_linux_dirent {
 175        unsigned long   d_ino;
 176        unsigned long   d_offset;
 177        unsigned short  d_namlen;
 178        char            d_name[1];
 179};
 180
 181struct readdir_callback {
 182        struct old_linux_dirent * dirent;
 183        int count;
 184};
 185
 186static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
 187                      ino_t ino, unsigned int d_type)
 188{
 189        struct readdir_callback * buf = (struct readdir_callback *) __buf;
 190        struct old_linux_dirent * dirent;
 191
 192        if (buf->count)
 193                return -EINVAL;
 194        buf->count++;
 195        dirent = buf->dirent;
 196        put_user(ino, &dirent->d_ino);
 197        put_user(offset, &dirent->d_offset);
 198        put_user(namlen, &dirent->d_namlen);
 199        copy_to_user(dirent->d_name, name, namlen);
 200        put_user(0, dirent->d_name + namlen);
 201        return 0;
 202}
 203
 204asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
 205{
 206        int error;
 207        struct file * file;
 208        struct readdir_callback buf;
 209
 210        error = -EBADF;
 211        file = fget(fd);
 212        if (!file)
 213                goto out;
 214
 215        buf.count = 0;
 216        buf.dirent = dirent;
 217
 218        error = vfs_readdir(file, fillonedir, &buf);
 219        if (error >= 0)
 220                error = buf.count;
 221
 222        fput(file);
 223out:
 224        return error;
 225}
 226
 227#endif /* !__ia64__ */
 228
 229/*
 230 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
 231 * interface. 
 232 */
 233struct linux_dirent {
 234        unsigned long   d_ino;
 235        unsigned long   d_off;
 236        unsigned short  d_reclen;
 237        char            d_name[1];
 238};
 239
 240struct getdents_callback {
 241        struct linux_dirent * current_dir;
 242        struct linux_dirent * previous;
 243        int count;
 244        int error;
 245};
 246
 247static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
 248                   ino_t ino, unsigned int d_type)
 249{
 250        struct linux_dirent * dirent;
 251        struct getdents_callback * buf = (struct getdents_callback *) __buf;
 252        int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
 253
 254        buf->error = -EINVAL;   /* only used if we fail.. */
 255        if (reclen > buf->count)
 256                return -EINVAL;
 257        dirent = buf->previous;
 258        if (dirent)
 259                put_user(offset, &dirent->d_off);
 260        dirent = buf->current_dir;
 261        buf->previous = dirent;
 262        put_user(ino, &dirent->d_ino);
 263        put_user(reclen, &dirent->d_reclen);
 264        copy_to_user(dirent->d_name, name, namlen);
 265        put_user(0, dirent->d_name + namlen);
 266        ((char *) dirent) += reclen;
 267        buf->current_dir = dirent;
 268        buf->count -= reclen;
 269        return 0;
 270}
 271
 272asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
 273{
 274        struct file * file;
 275        struct linux_dirent * lastdirent;
 276        struct getdents_callback buf;
 277        int error;
 278
 279        error = -EBADF;
 280        file = fget(fd);
 281        if (!file)
 282                goto out;
 283
 284        buf.current_dir = (struct linux_dirent *) dirent;
 285        buf.previous = NULL;
 286        buf.count = count;
 287        buf.error = 0;
 288
 289        error = vfs_readdir(file, filldir, &buf);
 290        if (error < 0)
 291                goto out_putf;
 292        error = buf.error;
 293        lastdirent = buf.previous;
 294        if (lastdirent) {
 295                put_user(file->f_pos, &lastdirent->d_off);
 296                error = count - buf.count;
 297        }
 298
 299out_putf:
 300        fput(file);
 301out:
 302        return error;
 303}
 304
 305/*
 306 * And even better one including d_type field and 64bit d_ino and d_off.
 307 */
 308struct linux_dirent64 {
 309        u64             d_ino;
 310        s64             d_off;
 311        unsigned short  d_reclen;
 312        unsigned char   d_type;
 313        char            d_name[0];
 314};
 315
 316#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
 317
 318struct getdents_callback64 {
 319        struct linux_dirent64 * current_dir;
 320        struct linux_dirent64 * previous;
 321        int count;
 322        int error;
 323};
 324
 325static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
 326                     ino_t ino, unsigned int d_type)
 327{
 328        struct linux_dirent64 * dirent, d;
 329        struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
 330        int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
 331
 332        buf->error = -EINVAL;   /* only used if we fail.. */
 333        if (reclen > buf->count)
 334                return -EINVAL;
 335        dirent = buf->previous;
 336        if (dirent) {
 337                d.d_off = offset;
 338                copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
 339        }
 340        dirent = buf->current_dir;
 341        buf->previous = dirent;
 342        memset(&d, 0, NAME_OFFSET(&d));
 343        d.d_ino = ino;
 344        d.d_reclen = reclen;
 345        d.d_type = d_type;
 346        copy_to_user(dirent, &d, NAME_OFFSET(&d));
 347        copy_to_user(dirent->d_name, name, namlen);
 348        put_user(0, dirent->d_name + namlen);
 349        ((char *) dirent) += reclen;
 350        buf->current_dir = dirent;
 351        buf->count -= reclen;
 352        return 0;
 353}
 354
 355asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count)
 356{
 357        struct file * file;
 358        struct linux_dirent64 * lastdirent;
 359        struct getdents_callback64 buf;
 360        int error;
 361
 362        error = -EBADF;
 363        file = fget(fd);
 364        if (!file)
 365                goto out;
 366
 367        buf.current_dir = (struct linux_dirent64 *) dirent;
 368        buf.previous = NULL;
 369        buf.count = count;
 370        buf.error = 0;
 371
 372        error = vfs_readdir(file, filldir64, &buf);
 373        if (error < 0)
 374                goto out_putf;
 375        error = buf.error;
 376        lastdirent = buf.previous;
 377        if (lastdirent) {
 378                struct linux_dirent64 d;
 379                d.d_off = file->f_pos;
 380                copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off));
 381                error = count - buf.count;
 382        }
 383
 384out_putf:
 385        fput(file);
 386out:
 387        return error;
 388}
 389
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.