The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/readdir.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 int 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);
   32 out:
   33         return res;
   34 }
   35 
   36 int 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 
   45 int dcache_dir_close(struct inode *inode, struct file *file)
   46 {
   47         dput(file->private_data);
   48         return 0;
   49 }
   50 
   51 loff_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 
   89 int 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 
  100 int 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 
  152 struct 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 
  174 struct 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 
  181 struct readdir_callback {
  182         struct old_linux_dirent * dirent;
  183         int count;
  184 };
  185 
  186 static 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 
  204 asmlinkage 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);
  223 out:
  224         return error;
  225 }
  226 
  227 #endif /* !__ia64__ */
  228 
  229 /*
  230  * New, all-improved, singing, dancing, iBCS2-compliant getdents()
  231  * interface. 
  232  */
  233 struct linux_dirent {
  234         unsigned long   d_ino;
  235         unsigned long   d_off;
  236         unsigned short  d_reclen;
  237         char            d_name[1];
  238 };
  239 
  240 struct getdents_callback {
  241         struct linux_dirent * current_dir;
  242         struct linux_dirent * previous;
  243         int count;
  244         int error;
  245 };
  246 
  247 static 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 
  272 asmlinkage 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 
  299 out_putf:
  300         fput(file);
  301 out:
  302         return error;
  303 }
  304 
  305 /*
  306  * And even better one including d_type field and 64bit d_ino and d_off.
  307  */
  308 struct 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 
  318 struct getdents_callback64 {
  319         struct linux_dirent64 * current_dir;
  320         struct linux_dirent64 * previous;
  321         int count;
  322         int error;
  323 };
  324 
  325 static 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 
  355 asmlinkage 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 
  384 out_putf:
  385         fput(file);
  386 out:
  387         return error;
  388 }

Cache object: 62667ad4bc7a8a79ac58d1230eab5c9a


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.