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/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 
   23 int 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);
   44 out:
   45         return res;
   46 }
   47 
   48 EXPORT_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 
   61 struct 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 
   68 struct readdir_callback {
   69         struct old_linux_dirent __user * dirent;
   70         int result;
   71 };
   72 
   73 static 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;
  100 efault:
  101         buf->result = -EFAULT;
  102         return -EFAULT;
  103 }
  104 
  105 SYSCALL_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  */
  132 struct linux_dirent {
  133         unsigned long   d_ino;
  134         unsigned long   d_off;
  135         unsigned short  d_reclen;
  136         char            d_name[1];
  137 };
  138 
  139 struct getdents_callback {
  140         struct linux_dirent __user * current_dir;
  141         struct linux_dirent __user * previous;
  142         int count;
  143         int error;
  144 };
  145 
  146 static 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;
  184 efault:
  185         buf->error = -EFAULT;
  186         return -EFAULT;
  187 }
  188 
  189 SYSCALL_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 
  223 struct getdents_callback64 {
  224         struct linux_dirent64 __user * current_dir;
  225         struct linux_dirent64 __user * previous;
  226         int count;
  227         int error;
  228 };
  229 
  230 static 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;
  264 efault:
  265         buf->error = -EFAULT;
  266         return -EFAULT;
  267 }
  268 
  269 SYSCALL_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 }

Cache object: 59fa4647950d4afc5bcd21b0dd9022b9


[ 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.