LCOV - code coverage report
Current view: top level - fs - readdir.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 69 80 86.2 %
Date: 2014-02-18 Functions: 5 5 100.0 %
Branches: 46 63 73.0 %

           Branch data     Line data    Source code
       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                 :          0 : int iterate_dir(struct file *file, struct dir_context *ctx)
      24                 :            : {
      25                 :            :         struct inode *inode = file_inode(file);
      26                 :            :         int res = -ENOTDIR;
      27         [ +  + ]:      48882 :         if (!file->f_op->iterate)
      28                 :            :                 goto out;
      29                 :            : 
      30                 :      48880 :         res = security_file_permission(file, MAY_READ);
      31         [ +  - ]:      48881 :         if (res)
      32                 :            :                 goto out;
      33                 :            : 
      34                 :      48881 :         res = mutex_lock_killable(&inode->i_mutex);
      35         [ +  + ]:      48880 :         if (res)
      36                 :            :                 goto out;
      37                 :            : 
      38                 :            :         res = -ENOENT;
      39         [ +  - ]:      48869 :         if (!IS_DEADDIR(inode)) {
      40                 :      48869 :                 ctx->pos = file->f_pos;
      41                 :      48869 :                 res = file->f_op->iterate(file, ctx);
      42                 :      48869 :                 file->f_pos = ctx->pos;
      43                 :            :                 file_accessed(file);
      44                 :            :         }
      45                 :      48870 :         mutex_unlock(&inode->i_mutex);
      46                 :            : out:
      47                 :      48883 :         return res;
      48                 :            : }
      49                 :            : EXPORT_SYMBOL(iterate_dir);
      50                 :            : 
      51                 :            : /*
      52                 :            :  * Traditional linux readdir() handling..
      53                 :            :  *
      54                 :            :  * "count=1" is a special case, meaning that the buffer is one
      55                 :            :  * dirent-structure in size and that the code can't handle more
      56                 :            :  * anyway. Thus the special "fillonedir()" function for that
      57                 :            :  * case (the low-level handlers don't need to care about this).
      58                 :            :  */
      59                 :            : 
      60                 :            : #ifdef __ARCH_WANT_OLD_READDIR
      61                 :            : 
      62                 :            : struct old_linux_dirent {
      63                 :            :         unsigned long   d_ino;
      64                 :            :         unsigned long   d_offset;
      65                 :            :         unsigned short  d_namlen;
      66                 :            :         char            d_name[1];
      67                 :            : };
      68                 :            : 
      69                 :            : struct readdir_callback {
      70                 :            :         struct dir_context ctx;
      71                 :            :         struct old_linux_dirent __user * dirent;
      72                 :            :         int result;
      73                 :            : };
      74                 :            : 
      75                 :            : static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
      76                 :            :                       u64 ino, unsigned int d_type)
      77                 :            : {
      78                 :            :         struct readdir_callback *buf = (struct readdir_callback *) __buf;
      79                 :            :         struct old_linux_dirent __user * dirent;
      80                 :            :         unsigned long d_ino;
      81                 :            : 
      82                 :            :         if (buf->result)
      83                 :            :                 return -EINVAL;
      84                 :            :         d_ino = ino;
      85                 :            :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
      86                 :            :                 buf->result = -EOVERFLOW;
      87                 :            :                 return -EOVERFLOW;
      88                 :            :         }
      89                 :            :         buf->result++;
      90                 :            :         dirent = buf->dirent;
      91                 :            :         if (!access_ok(VERIFY_WRITE, dirent,
      92                 :            :                         (unsigned long)(dirent->d_name + namlen + 1) -
      93                 :            :                                 (unsigned long)dirent))
      94                 :            :                 goto efault;
      95                 :            :         if (    __put_user(d_ino, &dirent->d_ino) ||
      96                 :            :                 __put_user(offset, &dirent->d_offset) ||
      97                 :            :                 __put_user(namlen, &dirent->d_namlen) ||
      98                 :            :                 __copy_to_user(dirent->d_name, name, namlen) ||
      99                 :            :                 __put_user(0, dirent->d_name + namlen))
     100                 :            :                 goto efault;
     101                 :            :         return 0;
     102                 :            : efault:
     103                 :            :         buf->result = -EFAULT;
     104                 :            :         return -EFAULT;
     105                 :            : }
     106                 :            : 
     107                 :            : SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
     108                 :            :                 struct old_linux_dirent __user *, dirent, unsigned int, count)
     109                 :            : {
     110                 :            :         int error;
     111                 :            :         struct fd f = fdget(fd);
     112                 :            :         struct readdir_callback buf = {
     113                 :            :                 .ctx.actor = fillonedir,
     114                 :            :                 .dirent = dirent
     115                 :            :         };
     116                 :            : 
     117                 :            :         if (!f.file)
     118                 :            :                 return -EBADF;
     119                 :            : 
     120                 :            :         error = iterate_dir(f.file, &buf.ctx);
     121                 :            :         if (buf.result)
     122                 :            :                 error = buf.result;
     123                 :            : 
     124                 :            :         fdput(f);
     125                 :            :         return error;
     126                 :            : }
     127                 :            : 
     128                 :            : #endif /* __ARCH_WANT_OLD_READDIR */
     129                 :            : 
     130                 :            : /*
     131                 :            :  * New, all-improved, singing, dancing, iBCS2-compliant getdents()
     132                 :            :  * interface. 
     133                 :            :  */
     134                 :            : struct linux_dirent {
     135                 :            :         unsigned long   d_ino;
     136                 :            :         unsigned long   d_off;
     137                 :            :         unsigned short  d_reclen;
     138                 :            :         char            d_name[1];
     139                 :            : };
     140                 :            : 
     141                 :            : struct getdents_callback {
     142                 :            :         struct dir_context ctx;
     143                 :            :         struct linux_dirent __user * current_dir;
     144                 :            :         struct linux_dirent __user * previous;
     145                 :            :         int count;
     146                 :            :         int error;
     147                 :            : };
     148                 :            : 
     149                 :          0 : static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
     150                 :            :                    u64 ino, unsigned int d_type)
     151                 :            : {
     152                 :            :         struct linux_dirent __user * dirent;
     153                 :            :         struct getdents_callback * buf = (struct getdents_callback *) __buf;
     154                 :            :         unsigned long d_ino;
     155                 :      41759 :         int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
     156                 :            :                 sizeof(long));
     157                 :            : 
     158                 :      41759 :         buf->error = -EINVAL;        /* only used if we fail.. */
     159         [ +  + ]:      41759 :         if (reclen > buf->count)
     160                 :            :                 return -EINVAL;
     161                 :      41752 :         d_ino = ino;
     162         [ -  + ]:      41752 :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     163                 :          0 :                 buf->error = -EOVERFLOW;
     164                 :          0 :                 return -EOVERFLOW;
     165                 :            :         }
     166                 :      41752 :         dirent = buf->previous;
     167         [ +  + ]:      41752 :         if (dirent) {
     168         [ +  - ]:      40491 :                 if (__put_user(offset, &dirent->d_off))
     169                 :            :                         goto efault;
     170                 :            :         }
     171                 :      41752 :         dirent = buf->current_dir;
     172         [ +  - ]:      41752 :         if (__put_user(d_ino, &dirent->d_ino))
     173                 :            :                 goto efault;
     174         [ +  - ]:      41752 :         if (__put_user(reclen, &dirent->d_reclen))
     175                 :            :                 goto efault;
     176         [ +  - ]:      41752 :         if (copy_to_user(dirent->d_name, name, namlen))
     177                 :            :                 goto efault;
     178         [ +  - ]:      41752 :         if (__put_user(0, dirent->d_name + namlen))
     179                 :            :                 goto efault;
     180         [ +  - ]:      41752 :         if (__put_user(d_type, (char __user *) dirent + reclen - 1))
     181                 :            :                 goto efault;
     182                 :      41752 :         buf->previous = dirent;
     183                 :      41752 :         dirent = (void __user *)dirent + reclen;
     184                 :      41752 :         buf->current_dir = dirent;
     185                 :      41752 :         buf->count -= reclen;
     186                 :      41752 :         return 0;
     187                 :            : efault:
     188                 :          0 :         buf->error = -EFAULT;
     189                 :          0 :         return -EFAULT;
     190                 :            : }
     191                 :            : 
     192                 :          0 : SYSCALL_DEFINE3(getdents, unsigned int, fd,
     193                 :            :                 struct linux_dirent __user *, dirent, unsigned int, count)
     194                 :            : {
     195                 :            :         struct fd f;
     196                 :            :         struct linux_dirent __user * lastdirent;
     197                 :       2519 :         struct getdents_callback buf = {
     198                 :            :                 .ctx.actor = filldir,
     199                 :            :                 .count = count,
     200                 :            :                 .current_dir = dirent
     201                 :            :         };
     202                 :            :         int error;
     203                 :            : 
     204         [ +  - ]:       2519 :         if (!access_ok(VERIFY_WRITE, dirent, count))
     205                 :            :                 return -EFAULT;
     206                 :            : 
     207                 :            :         f = fdget(fd);
     208         [ +  + ]:       2519 :         if (!f.file)
     209                 :            :                 return -EBADF;
     210                 :            : 
     211                 :       2517 :         error = iterate_dir(f.file, &buf.ctx);
     212         [ +  + ]:       2517 :         if (error >= 0)
     213                 :       2516 :                 error = buf.error;
     214                 :       2517 :         lastdirent = buf.previous;
     215         [ +  + ]:       2517 :         if (lastdirent) {
     216         [ +  - ]:       1261 :                 if (put_user(buf.ctx.pos, &lastdirent->d_off))
     217                 :            :                         error = -EFAULT;
     218                 :            :                 else
     219                 :       1261 :                         error = count - buf.count;
     220                 :            :         }
     221                 :            :         fdput(f);
     222                 :            :         return error;
     223                 :            : }
     224                 :            : 
     225                 :            : struct getdents_callback64 {
     226                 :            :         struct dir_context ctx;
     227                 :            :         struct linux_dirent64 __user * current_dir;
     228                 :            :         struct linux_dirent64 __user * previous;
     229                 :            :         int count;
     230                 :            :         int error;
     231                 :            : };
     232                 :            : 
     233                 :          0 : static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
     234                 :            :                      u64 ino, unsigned int d_type)
     235                 :            : {
     236                 :            :         struct linux_dirent64 __user *dirent;
     237                 :            :         struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
     238                 :     349523 :         int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
     239                 :            :                 sizeof(u64));
     240                 :            : 
     241                 :     349523 :         buf->error = -EINVAL;        /* only used if we fail.. */
     242         [ +  + ]:     349523 :         if (reclen > buf->count)
     243                 :            :                 return -EINVAL;
     244                 :     349508 :         dirent = buf->previous;
     245         [ +  + ]:     349508 :         if (dirent) {
     246         [ +  + ]:     324782 :                 if (__put_user(offset, &dirent->d_off))
     247                 :            :                         goto efault;
     248                 :            :         }
     249                 :     349509 :         dirent = buf->current_dir;
     250         [ +  - ]:     349509 :         if (__put_user(ino, &dirent->d_ino))
     251                 :            :                 goto efault;
     252         [ +  - ]:     349504 :         if (__put_user(0, &dirent->d_off))
     253                 :            :                 goto efault;
     254         [ +  - ]:     349506 :         if (__put_user(reclen, &dirent->d_reclen))
     255                 :            :                 goto efault;
     256         [ +  - ]:     349509 :         if (__put_user(d_type, &dirent->d_type))
     257                 :            :                 goto efault;
     258         [ +  - ]:     349508 :         if (copy_to_user(dirent->d_name, name, namlen))
     259                 :            :                 goto efault;
     260            [ + ]:     349508 :         if (__put_user(0, dirent->d_name + namlen))
     261                 :            :                 goto efault;
     262                 :     349509 :         buf->previous = dirent;
     263                 :     349509 :         dirent = (void __user *)dirent + reclen;
     264                 :     349509 :         buf->current_dir = dirent;
     265                 :     349509 :         buf->count -= reclen;
     266                 :     349509 :         return 0;
     267                 :            : efault:
     268                 :          0 :         buf->error = -EFAULT;
     269                 :          0 :         return -EFAULT;
     270                 :            : }
     271                 :            : 
     272                 :          0 : SYSCALL_DEFINE3(getdents64, unsigned int, fd,
     273                 :            :                 struct linux_dirent64 __user *, dirent, unsigned int, count)
     274                 :            : {
     275                 :            :         struct fd f;
     276                 :            :         struct linux_dirent64 __user * lastdirent;
     277                 :      46365 :         struct getdents_callback64 buf = {
     278                 :            :                 .ctx.actor = filldir64,
     279                 :            :                 .count = count,
     280                 :            :                 .current_dir = dirent
     281                 :            :         };
     282                 :            :         int error;
     283                 :            : 
     284         [ +  + ]:      46365 :         if (!access_ok(VERIFY_WRITE, dirent, count))
     285                 :            :                 return -EFAULT;
     286                 :            : 
     287                 :            :         f = fdget(fd);
     288         [ +  + ]:      46364 :         if (!f.file)
     289                 :            :                 return -EBADF;
     290                 :            : 
     291                 :      46363 :         error = iterate_dir(f.file, &buf.ctx);
     292         [ +  + ]:      46365 :         if (error >= 0)
     293                 :      46353 :                 error = buf.error;
     294                 :      46365 :         lastdirent = buf.previous;
     295         [ +  + ]:      46365 :         if (lastdirent) {
     296                 :      24728 :                 typeof(lastdirent->d_off) d_off = buf.ctx.pos;
     297         [ +  - ]:      24728 :                 if (__put_user(d_off, &lastdirent->d_off))
     298                 :            :                         error = -EFAULT;
     299                 :            :                 else
     300                 :      24728 :                         error = count - buf.count;
     301                 :            :         }
     302                 :            :         fdput(f);
     303                 :            :         return error;
     304                 :            : }

Generated by: LCOV version 1.9