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
  16/*
  17 * Traditional linux readdir() handling..
  18 *
  19 * "count=1" is a special case, meaning that the buffer is one
  20 * dirent-structure in size and that the code can't handle more
  21 * anyway. Thus the special "fillonedir()" function for that
  22 * case (the low-level handlers don't need to care about this).
  23 */
  24#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
  25#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
  26
  27struct old_linux_dirent {
  28        unsigned long   d_ino;
  29        unsigned long   d_offset;
  30        unsigned short  d_namlen;
  31        char            d_name[1];
  32};
  33
  34struct readdir_callback {
  35        struct old_linux_dirent * dirent;
  36        int count;
  37};
  38
  39static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
  40{
  41        struct readdir_callback * buf = (struct readdir_callback *) __buf;
  42        struct old_linux_dirent * dirent;
  43
  44        if (buf->count)
  45                return -EINVAL;
  46        buf->count++;
  47        dirent = buf->dirent;
  48        put_user(ino, &dirent->d_ino);
  49        put_user(offset, &dirent->d_offset);
  50        put_user(namlen, &dirent->d_namlen);
  51        copy_to_user(dirent->d_name, name, namlen);
  52        put_user(0, dirent->d_name + namlen);
  53        return 0;
  54}
  55
  56asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
  57{
  58        int error;
  59        struct file * file;
  60        struct dentry * dentry;
  61        struct inode * inode;
  62        struct readdir_callback buf;
  63
  64        lock_kernel();
  65        error = -EBADF;
  66        file = fget(fd);
  67        if (!file)
  68                goto out;
  69
  70        dentry = file->f_dentry;
  71        if (!dentry)
  72                goto out_putf;
  73
  74        inode = dentry->d_inode;
  75        if (!inode)
  76                goto out_putf;
  77
  78        buf.count = 0;
  79        buf.dirent = dirent;
  80
  81        error = -ENOTDIR;
  82        if (!file->f_op || !file->f_op->readdir)
  83                goto out_putf;
  84
  85        /*
  86         * Get the inode's semaphore to prevent changes
  87         * to the directory while we read it.
  88         */
  89        down(&inode->i_sem);
  90        error = file->f_op->readdir(file, &buf, fillonedir);
  91        up(&inode->i_sem);
  92        if (error < 0)
  93                goto out_putf;
  94        error = buf.count;
  95
  96out_putf:
  97        fput(file);
  98out:
  99        unlock_kernel();
 100        return error;
 101}
 102
 103/*
 104 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
 105 * interface. 
 106 */
 107struct linux_dirent {
 108        unsigned long   d_ino;
 109        unsigned long   d_off;
 110        unsigned short  d_reclen;
 111        char            d_name[1];
 112};
 113
 114struct getdents_callback {
 115        struct linux_dirent * current_dir;
 116        struct linux_dirent * previous;
 117        int count;
 118        int error;
 119};
 120
 121static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
 122{
 123        struct linux_dirent * dirent;
 124        struct getdents_callback * buf = (struct getdents_callback *) __buf;
 125        int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
 126
 127        buf->error = -EINVAL;   /* only used if we fail.. */
 128        if (reclen > buf->count)
 129                return -EINVAL;
 130        dirent = buf->previous;
 131        if (dirent)
 132                put_user(offset, &dirent->d_off);
 133        dirent = buf->current_dir;
 134        buf->previous = dirent;
 135        put_user(ino, &dirent->d_ino);
 136        put_user(reclen, &dirent->d_reclen);
 137        copy_to_user(dirent->d_name, name, namlen);
 138        put_user(0, dirent->d_name + namlen);
 139        ((char *) dirent) += reclen;
 140        buf->current_dir = dirent;
 141        buf->count -= reclen;
 142        return 0;
 143}
 144
 145asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count)
 146{
 147        struct file * file;
 148        struct dentry * dentry;
 149        struct inode * inode;
 150        struct linux_dirent * lastdirent;
 151        struct getdents_callback buf;
 152        int error;
 153
 154        lock_kernel();
 155        error = -EBADF;
 156        file = fget(fd);
 157        if (!file)
 158                goto out;
 159
 160        dentry = file->f_dentry;
 161        if (!dentry)
 162                goto out_putf;
 163
 164        inode = dentry->d_inode;
 165        if (!inode)
 166                goto out_putf;
 167
 168        buf.current_dir = (struct linux_dirent *) dirent;
 169        buf.previous = NULL;
 170        buf.count = count;
 171        buf.error = 0;
 172
 173        error = -ENOTDIR;
 174        if (!file->f_op || !file->f_op->readdir)
 175                goto out_putf;
 176
 177        /*
 178         * Get the inode's semaphore to prevent changes
 179         * to the directory while we read it.
 180         */
 181        down(&inode->i_sem);
 182        error = file->f_op->readdir(file, &buf, filldir);
 183        up(&inode->i_sem);
 184        if (error < 0)
 185                goto out_putf;
 186        error = buf.error;
 187        lastdirent = buf.previous;
 188        if (lastdirent) {
 189                put_user(file->f_pos, &lastdirent->d_off);
 190                error = count - buf.count;
 191        }
 192
 193out_putf:
 194        fput(file);
 195out:
 196        unlock_kernel();
 197        return error;
 198}
 199
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.