LCOV - code coverage report
Current view: top level - fs - readdir.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 70 234 29.9 %
Date: 2022-04-01 13:59:58 Functions: 4 17 23.5 %
Branches: 43 220 19.5 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : /*
       3                 :            :  *  linux/fs/readdir.c
       4                 :            :  *
       5                 :            :  *  Copyright (C) 1995  Linus Torvalds
       6                 :            :  */
       7                 :            : 
       8                 :            : #include <linux/stddef.h>
       9                 :            : #include <linux/kernel.h>
      10                 :            : #include <linux/export.h>
      11                 :            : #include <linux/time.h>
      12                 :            : #include <linux/mm.h>
      13                 :            : #include <linux/errno.h>
      14                 :            : #include <linux/stat.h>
      15                 :            : #include <linux/file.h>
      16                 :            : #include <linux/fs.h>
      17                 :            : #include <linux/fsnotify.h>
      18                 :            : #include <linux/dirent.h>
      19                 :            : #include <linux/security.h>
      20                 :            : #include <linux/syscalls.h>
      21                 :            : #include <linux/unistd.h>
      22                 :            : #include <linux/compat.h>
      23                 :            : #include <linux/uaccess.h>
      24                 :            : 
      25                 :            : #include <asm/unaligned.h>
      26                 :            : 
      27                 :            : /*
      28                 :            :  * Note the "unsafe_put_user() semantics: we goto a
      29                 :            :  * label for errors.
      30                 :            :  */
      31                 :            : #define unsafe_copy_dirent_name(_dst, _src, _len, label) do {   \
      32                 :            :         char __user *dst = (_dst);                              \
      33                 :            :         const char *src = (_src);                               \
      34                 :            :         size_t len = (_len);                                    \
      35                 :            :         unsafe_put_user(0, dst+len, label);                     \
      36                 :            :         unsafe_copy_to_user(dst, src, len, label);              \
      37                 :            : } while (0)
      38                 :            : 
      39                 :            : 
      40                 :     100215 : int iterate_dir(struct file *file, struct dir_context *ctx)
      41                 :            : {
      42         [ -  + ]:     100215 :         struct inode *inode = file_inode(file);
      43                 :     100215 :         bool shared = false;
      44                 :     100215 :         int res = -ENOTDIR;
      45         [ -  + ]:     100215 :         if (file->f_op->iterate_shared)
      46                 :            :                 shared = true;
      47         [ #  # ]:          0 :         else if (!file->f_op->iterate)
      48                 :          0 :                 goto out;
      49                 :            : 
      50                 :     100215 :         res = security_file_permission(file, MAY_READ);
      51         [ -  + ]:     100215 :         if (res)
      52                 :          0 :                 goto out;
      53                 :            : 
      54         [ +  - ]:     100215 :         if (shared)
      55                 :     100215 :                 res = down_read_killable(&inode->i_rwsem);
      56                 :            :         else
      57                 :          0 :                 res = down_write_killable(&inode->i_rwsem);
      58         [ -  + ]:     100215 :         if (res)
      59                 :          0 :                 goto out;
      60                 :            : 
      61                 :     100215 :         res = -ENOENT;
      62         [ +  - ]:     100215 :         if (!IS_DEADDIR(inode)) {
      63                 :     100215 :                 ctx->pos = file->f_pos;
      64         [ +  - ]:     100215 :                 if (shared)
      65                 :     100215 :                         res = file->f_op->iterate_shared(file, ctx);
      66                 :            :                 else
      67                 :          0 :                         res = file->f_op->iterate(file, ctx);
      68                 :     100215 :                 file->f_pos = ctx->pos;
      69                 :     100215 :                 fsnotify_access(file);
      70         [ +  + ]:     100215 :                 file_accessed(file);
      71                 :            :         }
      72         [ +  - ]:     100215 :         if (shared)
      73                 :     100215 :                 inode_unlock_shared(inode);
      74                 :            :         else
      75                 :          0 :                 inode_unlock(inode);
      76                 :     100215 : out:
      77                 :     100215 :         return res;
      78                 :            : }
      79                 :            : EXPORT_SYMBOL(iterate_dir);
      80                 :            : 
      81                 :            : /*
      82                 :            :  * POSIX says that a dirent name cannot contain NULL or a '/'.
      83                 :            :  *
      84                 :            :  * It's not 100% clear what we should really do in this case.
      85                 :            :  * The filesystem is clearly corrupted, but returning a hard
      86                 :            :  * error means that you now don't see any of the other names
      87                 :            :  * either, so that isn't a perfect alternative.
      88                 :            :  *
      89                 :            :  * And if you return an error, what error do you use? Several
      90                 :            :  * filesystems seem to have decided on EUCLEAN being the error
      91                 :            :  * code for EFSCORRUPTED, and that may be the error to use. Or
      92                 :            :  * just EIO, which is perhaps more obvious to users.
      93                 :            :  *
      94                 :            :  * In order to see the other file names in the directory, the
      95                 :            :  * caller might want to make this a "soft" error: skip the
      96                 :            :  * entry, and return the error at the end instead.
      97                 :            :  *
      98                 :            :  * Note that this should likely do a "memchr(name, 0, len)"
      99                 :            :  * check too, since that would be filesystem corruption as
     100                 :            :  * well. However, that case can't actually confuse user space,
     101                 :            :  * which has to do a strlen() on the name anyway to find the
     102                 :            :  * filename length, and the above "soft error" worry means
     103                 :            :  * that it's probably better left alone until we have that
     104                 :            :  * issue clarified.
     105                 :            :  *
     106                 :            :  * Note the PATH_MAX check - it's arbitrary but the real
     107                 :            :  * kernel limit on a possible path component, not NAME_MAX,
     108                 :            :  * which is the technical standard limit.
     109                 :            :  */
     110                 :     832917 : static int verify_dirent_name(const char *name, int len)
     111                 :            : {
     112                 :     832917 :         if (len <= 0 || len >= PATH_MAX)
     113                 :            :                 return -EIO;
     114   [ -  +  -  - ]:     832917 :         if (memchr(name, '/', len))
     115                 :          0 :                 return -EIO;
     116                 :            :         return 0;
     117                 :            : }
     118                 :            : 
     119                 :            : /*
     120                 :            :  * Traditional linux readdir() handling..
     121                 :            :  *
     122                 :            :  * "count=1" is a special case, meaning that the buffer is one
     123                 :            :  * dirent-structure in size and that the code can't handle more
     124                 :            :  * anyway. Thus the special "fillonedir()" function for that
     125                 :            :  * case (the low-level handlers don't need to care about this).
     126                 :            :  */
     127                 :            : 
     128                 :            : #ifdef __ARCH_WANT_OLD_READDIR
     129                 :            : 
     130                 :            : struct old_linux_dirent {
     131                 :            :         unsigned long   d_ino;
     132                 :            :         unsigned long   d_offset;
     133                 :            :         unsigned short  d_namlen;
     134                 :            :         char            d_name[1];
     135                 :            : };
     136                 :            : 
     137                 :            : struct readdir_callback {
     138                 :            :         struct dir_context ctx;
     139                 :            :         struct old_linux_dirent __user * dirent;
     140                 :            :         int result;
     141                 :            : };
     142                 :            : 
     143                 :          0 : static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
     144                 :            :                       loff_t offset, u64 ino, unsigned int d_type)
     145                 :            : {
     146                 :          0 :         struct readdir_callback *buf =
     147                 :          0 :                 container_of(ctx, struct readdir_callback, ctx);
     148                 :          0 :         struct old_linux_dirent __user * dirent;
     149                 :          0 :         unsigned long d_ino;
     150                 :            : 
     151         [ #  # ]:          0 :         if (buf->result)
     152                 :            :                 return -EINVAL;
     153                 :          0 :         d_ino = ino;
     154                 :          0 :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     155                 :            :                 buf->result = -EOVERFLOW;
     156                 :            :                 return -EOVERFLOW;
     157                 :            :         }
     158                 :          0 :         buf->result++;
     159                 :          0 :         dirent = buf->dirent;
     160   [ #  #  #  # ]:          0 :         if (!access_ok(dirent,
     161                 :            :                         (unsigned long)(dirent->d_name + namlen + 1) -
     162                 :            :                                 (unsigned long)dirent))
     163                 :          0 :                 goto efault;
     164   [ #  #  #  # ]:          0 :         if (    __put_user(d_ino, &dirent->d_ino) ||
     165   [ #  #  #  # ]:          0 :                 __put_user(offset, &dirent->d_offset) ||
     166   [ #  #  #  #  :          0 :                 __put_user(namlen, &dirent->d_namlen) ||
                   #  # ]
     167                 :          0 :                 __copy_to_user(dirent->d_name, name, namlen) ||
     168   [ #  #  #  # ]:          0 :                 __put_user(0, dirent->d_name + namlen))
     169                 :          0 :                 goto efault;
     170                 :            :         return 0;
     171                 :          0 : efault:
     172                 :          0 :         buf->result = -EFAULT;
     173                 :          0 :         return -EFAULT;
     174                 :            : }
     175                 :            : 
     176                 :          0 : SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
     177                 :            :                 struct old_linux_dirent __user *, dirent, unsigned int, count)
     178                 :            : {
     179                 :            :         int error;
     180                 :            :         struct fd f = fdget_pos(fd);
     181                 :            :         struct readdir_callback buf = {
     182                 :            :                 .ctx.actor = fillonedir,
     183                 :            :                 .dirent = dirent
     184                 :            :         };
     185                 :            : 
     186                 :            :         if (!f.file)
     187                 :            :                 return -EBADF;
     188                 :            : 
     189                 :            :         error = iterate_dir(f.file, &buf.ctx);
     190                 :            :         if (buf.result)
     191                 :            :                 error = buf.result;
     192                 :            : 
     193                 :            :         fdput_pos(f);
     194                 :            :         return error;
     195                 :            : }
     196                 :            : 
     197                 :            : #endif /* __ARCH_WANT_OLD_READDIR */
     198                 :            : 
     199                 :            : /*
     200                 :            :  * New, all-improved, singing, dancing, iBCS2-compliant getdents()
     201                 :            :  * interface. 
     202                 :            :  */
     203                 :            : struct linux_dirent {
     204                 :            :         unsigned long   d_ino;
     205                 :            :         unsigned long   d_off;
     206                 :            :         unsigned short  d_reclen;
     207                 :            :         char            d_name[1];
     208                 :            : };
     209                 :            : 
     210                 :            : struct getdents_callback {
     211                 :            :         struct dir_context ctx;
     212                 :            :         struct linux_dirent __user * current_dir;
     213                 :            :         int prev_reclen;
     214                 :            :         int count;
     215                 :            :         int error;
     216                 :            : };
     217                 :            : 
     218                 :          0 : static int filldir(struct dir_context *ctx, const char *name, int namlen,
     219                 :            :                    loff_t offset, u64 ino, unsigned int d_type)
     220                 :            : {
     221                 :          0 :         struct linux_dirent __user *dirent, *prev;
     222                 :          0 :         struct getdents_callback *buf =
     223                 :          0 :                 container_of(ctx, struct getdents_callback, ctx);
     224                 :          0 :         unsigned long d_ino;
     225                 :          0 :         int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
     226                 :            :                 sizeof(long));
     227                 :          0 :         int prev_reclen;
     228                 :            : 
     229         [ #  # ]:          0 :         buf->error = verify_dirent_name(name, namlen);
     230         [ #  # ]:          0 :         if (unlikely(buf->error))
     231                 :            :                 return buf->error;
     232                 :          0 :         buf->error = -EINVAL;        /* only used if we fail.. */
     233         [ #  # ]:          0 :         if (reclen > buf->count)
     234                 :            :                 return -EINVAL;
     235                 :          0 :         d_ino = ino;
     236                 :          0 :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     237                 :            :                 buf->error = -EOVERFLOW;
     238                 :            :                 return -EOVERFLOW;
     239                 :            :         }
     240                 :          0 :         prev_reclen = buf->prev_reclen;
     241   [ #  #  #  # ]:          0 :         if (prev_reclen && signal_pending(current))
     242                 :            :                 return -EINTR;
     243                 :          0 :         dirent = buf->current_dir;
     244                 :          0 :         prev = (void __user *) dirent - prev_reclen;
     245         [ #  # ]:          0 :         if (!user_access_begin(prev, reclen + prev_reclen))
     246                 :          0 :                 goto efault;
     247                 :            : 
     248                 :            :         /* This might be 'dirent->d_off', but if so it will get overwritten */
     249         [ #  # ]:          0 :         unsafe_put_user(offset, &prev->d_off, efault_end);
     250         [ #  # ]:          0 :         unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
     251         [ #  # ]:          0 :         unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
     252         [ #  # ]:          0 :         unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
     253   [ #  #  #  #  :          0 :         unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
     254                 :          0 :         user_access_end();
     255                 :            : 
     256                 :          0 :         buf->current_dir = (void __user *)dirent + reclen;
     257                 :          0 :         buf->prev_reclen = reclen;
     258                 :          0 :         buf->count -= reclen;
     259                 :          0 :         return 0;
     260                 :          0 : efault_end:
     261                 :          0 :         user_access_end();
     262                 :          0 : efault:
     263                 :          0 :         buf->error = -EFAULT;
     264                 :          0 :         return -EFAULT;
     265                 :            : }
     266                 :            : 
     267                 :          0 : SYSCALL_DEFINE3(getdents, unsigned int, fd,
     268                 :            :                 struct linux_dirent __user *, dirent, unsigned int, count)
     269                 :            : {
     270                 :          0 :         struct fd f;
     271                 :          0 :         struct getdents_callback buf = {
     272                 :            :                 .ctx.actor = filldir,
     273                 :            :                 .count = count,
     274                 :            :                 .current_dir = dirent
     275                 :            :         };
     276                 :          0 :         int error;
     277                 :            : 
     278   [ #  #  #  # ]:          0 :         if (!access_ok(dirent, count))
     279                 :            :                 return -EFAULT;
     280                 :            : 
     281                 :          0 :         f = fdget_pos(fd);
     282         [ #  # ]:          0 :         if (!f.file)
     283                 :            :                 return -EBADF;
     284                 :            : 
     285                 :          0 :         error = iterate_dir(f.file, &buf.ctx);
     286         [ #  # ]:          0 :         if (error >= 0)
     287                 :          0 :                 error = buf.error;
     288         [ #  # ]:          0 :         if (buf.prev_reclen) {
     289                 :          0 :                 struct linux_dirent __user * lastdirent;
     290                 :          0 :                 lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
     291                 :            : 
     292         [ #  # ]:          0 :                 if (put_user(buf.ctx.pos, &lastdirent->d_off))
     293                 :            :                         error = -EFAULT;
     294                 :            :                 else
     295                 :          0 :                         error = count - buf.count;
     296                 :            :         }
     297                 :          0 :         fdput_pos(f);
     298                 :          0 :         return error;
     299                 :            : }
     300                 :            : 
     301                 :            : struct getdents_callback64 {
     302                 :            :         struct dir_context ctx;
     303                 :            :         struct linux_dirent64 __user * current_dir;
     304                 :            :         int prev_reclen;
     305                 :            :         int count;
     306                 :            :         int error;
     307                 :            : };
     308                 :            : 
     309                 :     832917 : static int filldir64(struct dir_context *ctx, const char *name, int namlen,
     310                 :            :                      loff_t offset, u64 ino, unsigned int d_type)
     311                 :            : {
     312                 :     832917 :         struct linux_dirent64 __user *dirent, *prev;
     313                 :     832917 :         struct getdents_callback64 *buf =
     314                 :     832917 :                 container_of(ctx, struct getdents_callback64, ctx);
     315                 :     832917 :         int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
     316                 :            :                 sizeof(u64));
     317                 :     832917 :         int prev_reclen;
     318                 :            : 
     319         [ +  - ]:     832917 :         buf->error = verify_dirent_name(name, namlen);
     320         [ +  - ]:     832917 :         if (unlikely(buf->error))
     321                 :            :                 return buf->error;
     322                 :     832917 :         buf->error = -EINVAL;        /* only used if we fail.. */
     323         [ +  - ]:     832917 :         if (reclen > buf->count)
     324                 :            :                 return -EINVAL;
     325                 :     832917 :         prev_reclen = buf->prev_reclen;
     326   [ +  +  +  - ]:    1615281 :         if (prev_reclen && signal_pending(current))
     327                 :            :                 return -EINTR;
     328                 :     832917 :         dirent = buf->current_dir;
     329                 :     832917 :         prev = (void __user *)dirent - prev_reclen;
     330         [ -  + ]:     832917 :         if (!user_access_begin(prev, reclen + prev_reclen))
     331                 :          0 :                 goto efault;
     332                 :            : 
     333                 :            :         /* This might be 'dirent->d_off', but if so it will get overwritten */
     334         [ +  - ]:     832917 :         unsafe_put_user(offset, &prev->d_off, efault_end);
     335         [ +  - ]:     832917 :         unsafe_put_user(ino, &dirent->d_ino, efault_end);
     336         [ +  - ]:     832917 :         unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
     337         [ +  - ]:     832917 :         unsafe_put_user(d_type, &dirent->d_type, efault_end);
     338   [ +  -  +  -  :    2694515 :         unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
          +  +  +  -  +  
          +  +  -  +  +  
             +  -  +  + ]
     339                 :     832917 :         user_access_end();
     340                 :            : 
     341                 :     832917 :         buf->prev_reclen = reclen;
     342                 :     832917 :         buf->current_dir = (void __user *)dirent + reclen;
     343                 :     832917 :         buf->count -= reclen;
     344                 :     832917 :         return 0;
     345                 :            : 
     346                 :          0 : efault_end:
     347                 :          0 :         user_access_end();
     348                 :          0 : efault:
     349                 :          0 :         buf->error = -EFAULT;
     350                 :          0 :         return -EFAULT;
     351                 :            : }
     352                 :            : 
     353                 :     100215 : int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent,
     354                 :            :                     unsigned int count)
     355                 :            : {
     356                 :     100215 :         struct fd f;
     357                 :     100215 :         struct getdents_callback64 buf = {
     358                 :            :                 .ctx.actor = filldir64,
     359                 :            :                 .count = count,
     360                 :            :                 .current_dir = dirent
     361                 :            :         };
     362                 :     100215 :         int error;
     363                 :            : 
     364   [ -  +  +  - ]:     200430 :         if (!access_ok(dirent, count))
     365                 :            :                 return -EFAULT;
     366                 :            : 
     367                 :     100215 :         f = fdget_pos(fd);
     368         [ +  - ]:     100215 :         if (!f.file)
     369                 :            :                 return -EBADF;
     370                 :            : 
     371                 :     100215 :         error = iterate_dir(f.file, &buf.ctx);
     372         [ +  - ]:     100215 :         if (error >= 0)
     373                 :     100215 :                 error = buf.error;
     374         [ +  + ]:     100215 :         if (buf.prev_reclen) {
     375                 :      50553 :                 struct linux_dirent64 __user * lastdirent;
     376                 :      50553 :                 typeof(lastdirent->d_off) d_off = buf.ctx.pos;
     377                 :            : 
     378                 :      50553 :                 lastdirent = (void __user *) buf.current_dir - buf.prev_reclen;
     379   [ +  -  +  - ]:      50553 :                 if (__put_user(d_off, &lastdirent->d_off))
     380                 :            :                         error = -EFAULT;
     381                 :            :                 else
     382                 :      50553 :                         error = count - buf.count;
     383                 :            :         }
     384                 :     100215 :         fdput_pos(f);
     385                 :     100215 :         return error;
     386                 :            : }
     387                 :            : 
     388                 :            : 
     389                 :     200430 : SYSCALL_DEFINE3(getdents64, unsigned int, fd,
     390                 :            :                 struct linux_dirent64 __user *, dirent, unsigned int, count)
     391                 :            : {
     392                 :     100215 :         return ksys_getdents64(fd, dirent, count);
     393                 :            : }
     394                 :            : 
     395                 :            : #ifdef CONFIG_COMPAT
     396                 :            : struct compat_old_linux_dirent {
     397                 :            :         compat_ulong_t  d_ino;
     398                 :            :         compat_ulong_t  d_offset;
     399                 :            :         unsigned short  d_namlen;
     400                 :            :         char            d_name[1];
     401                 :            : };
     402                 :            : 
     403                 :            : struct compat_readdir_callback {
     404                 :            :         struct dir_context ctx;
     405                 :            :         struct compat_old_linux_dirent __user *dirent;
     406                 :            :         int result;
     407                 :            : };
     408                 :            : 
     409                 :          0 : static int compat_fillonedir(struct dir_context *ctx, const char *name,
     410                 :            :                              int namlen, loff_t offset, u64 ino,
     411                 :            :                              unsigned int d_type)
     412                 :            : {
     413                 :          0 :         struct compat_readdir_callback *buf =
     414                 :          0 :                 container_of(ctx, struct compat_readdir_callback, ctx);
     415                 :          0 :         struct compat_old_linux_dirent __user *dirent;
     416                 :          0 :         compat_ulong_t d_ino;
     417                 :            : 
     418         [ #  # ]:          0 :         if (buf->result)
     419                 :            :                 return -EINVAL;
     420                 :          0 :         d_ino = ino;
     421         [ #  # ]:          0 :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     422                 :          0 :                 buf->result = -EOVERFLOW;
     423                 :          0 :                 return -EOVERFLOW;
     424                 :            :         }
     425                 :          0 :         buf->result++;
     426                 :          0 :         dirent = buf->dirent;
     427   [ #  #  #  # ]:          0 :         if (!access_ok(dirent,
     428                 :            :                         (unsigned long)(dirent->d_name + namlen + 1) -
     429                 :            :                                 (unsigned long)dirent))
     430                 :          0 :                 goto efault;
     431   [ #  #  #  # ]:          0 :         if (    __put_user(d_ino, &dirent->d_ino) ||
     432   [ #  #  #  # ]:          0 :                 __put_user(offset, &dirent->d_offset) ||
     433   [ #  #  #  #  :          0 :                 __put_user(namlen, &dirent->d_namlen) ||
                   #  # ]
     434                 :          0 :                 __copy_to_user(dirent->d_name, name, namlen) ||
     435   [ #  #  #  # ]:          0 :                 __put_user(0, dirent->d_name + namlen))
     436                 :          0 :                 goto efault;
     437                 :            :         return 0;
     438                 :          0 : efault:
     439                 :          0 :         buf->result = -EFAULT;
     440                 :          0 :         return -EFAULT;
     441                 :            : }
     442                 :            : 
     443                 :          0 : COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
     444                 :            :                 struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
     445                 :            : {
     446                 :            :         int error;
     447                 :            :         struct fd f = fdget_pos(fd);
     448                 :            :         struct compat_readdir_callback buf = {
     449                 :            :                 .ctx.actor = compat_fillonedir,
     450                 :            :                 .dirent = dirent
     451                 :            :         };
     452                 :            : 
     453                 :            :         if (!f.file)
     454                 :            :                 return -EBADF;
     455                 :            : 
     456                 :            :         error = iterate_dir(f.file, &buf.ctx);
     457                 :            :         if (buf.result)
     458                 :            :                 error = buf.result;
     459                 :            : 
     460                 :            :         fdput_pos(f);
     461                 :            :         return error;
     462                 :            : }
     463                 :            : 
     464                 :            : struct compat_linux_dirent {
     465                 :            :         compat_ulong_t  d_ino;
     466                 :            :         compat_ulong_t  d_off;
     467                 :            :         unsigned short  d_reclen;
     468                 :            :         char            d_name[1];
     469                 :            : };
     470                 :            : 
     471                 :            : struct compat_getdents_callback {
     472                 :            :         struct dir_context ctx;
     473                 :            :         struct compat_linux_dirent __user *current_dir;
     474                 :            :         struct compat_linux_dirent __user *previous;
     475                 :            :         int count;
     476                 :            :         int error;
     477                 :            : };
     478                 :            : 
     479                 :          0 : static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
     480                 :            :                 loff_t offset, u64 ino, unsigned int d_type)
     481                 :            : {
     482                 :          0 :         struct compat_linux_dirent __user * dirent;
     483                 :          0 :         struct compat_getdents_callback *buf =
     484                 :          0 :                 container_of(ctx, struct compat_getdents_callback, ctx);
     485                 :          0 :         compat_ulong_t d_ino;
     486                 :          0 :         int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
     487                 :            :                 namlen + 2, sizeof(compat_long_t));
     488                 :            : 
     489                 :          0 :         buf->error = -EINVAL;        /* only used if we fail.. */
     490         [ #  # ]:          0 :         if (reclen > buf->count)
     491                 :            :                 return -EINVAL;
     492                 :          0 :         d_ino = ino;
     493         [ #  # ]:          0 :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     494                 :          0 :                 buf->error = -EOVERFLOW;
     495                 :          0 :                 return -EOVERFLOW;
     496                 :            :         }
     497                 :          0 :         dirent = buf->previous;
     498         [ #  # ]:          0 :         if (dirent) {
     499         [ #  # ]:          0 :                 if (signal_pending(current))
     500                 :            :                         return -EINTR;
     501   [ #  #  #  # ]:          0 :                 if (__put_user(offset, &dirent->d_off))
     502                 :          0 :                         goto efault;
     503                 :            :         }
     504                 :          0 :         dirent = buf->current_dir;
     505   [ #  #  #  # ]:          0 :         if (__put_user(d_ino, &dirent->d_ino))
     506                 :          0 :                 goto efault;
     507   [ #  #  #  # ]:          0 :         if (__put_user(reclen, &dirent->d_reclen))
     508                 :          0 :                 goto efault;
     509   [ #  #  #  # ]:          0 :         if (copy_to_user(dirent->d_name, name, namlen))
     510                 :          0 :                 goto efault;
     511   [ #  #  #  # ]:          0 :         if (__put_user(0, dirent->d_name + namlen))
     512                 :          0 :                 goto efault;
     513   [ #  #  #  # ]:          0 :         if (__put_user(d_type, (char  __user *) dirent + reclen - 1))
     514                 :          0 :                 goto efault;
     515                 :          0 :         buf->previous = dirent;
     516                 :          0 :         dirent = (void __user *)dirent + reclen;
     517                 :          0 :         buf->current_dir = dirent;
     518                 :          0 :         buf->count -= reclen;
     519                 :          0 :         return 0;
     520                 :          0 : efault:
     521                 :          0 :         buf->error = -EFAULT;
     522                 :          0 :         return -EFAULT;
     523                 :            : }
     524                 :            : 
     525                 :          0 : COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
     526                 :            :                 struct compat_linux_dirent __user *, dirent, unsigned int, count)
     527                 :            : {
     528                 :          0 :         struct fd f;
     529                 :          0 :         struct compat_linux_dirent __user * lastdirent;
     530                 :          0 :         struct compat_getdents_callback buf = {
     531                 :            :                 .ctx.actor = compat_filldir,
     532                 :            :                 .current_dir = dirent,
     533                 :            :                 .count = count
     534                 :            :         };
     535                 :          0 :         int error;
     536                 :            : 
     537   [ #  #  #  # ]:          0 :         if (!access_ok(dirent, count))
     538                 :            :                 return -EFAULT;
     539                 :            : 
     540                 :          0 :         f = fdget_pos(fd);
     541         [ #  # ]:          0 :         if (!f.file)
     542                 :            :                 return -EBADF;
     543                 :            : 
     544                 :          0 :         error = iterate_dir(f.file, &buf.ctx);
     545         [ #  # ]:          0 :         if (error >= 0)
     546                 :          0 :                 error = buf.error;
     547                 :          0 :         lastdirent = buf.previous;
     548         [ #  # ]:          0 :         if (lastdirent) {
     549         [ #  # ]:          0 :                 if (put_user(buf.ctx.pos, &lastdirent->d_off))
     550                 :            :                         error = -EFAULT;
     551                 :            :                 else
     552                 :          0 :                         error = count - buf.count;
     553                 :            :         }
     554                 :          0 :         fdput_pos(f);
     555                 :          0 :         return error;
     556                 :            : }
     557                 :            : #endif

Generated by: LCOV version 1.14