LCOV - code coverage report
Current view: top level - fs - readdir.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-djwx @ Mon Jul 31 20:08:22 PDT 2023 Lines: 72 254 28.3 %
Date: 2023-07-31 20:08:22 Functions: 5 20 25.0 %

          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   185123509 : int iterate_dir(struct file *file, struct dir_context *ctx)
      41             : {
      42   185123509 :         struct inode *inode = file_inode(file);
      43   185123509 :         bool shared = false;
      44   185123509 :         int res = -ENOTDIR;
      45   185123509 :         if (file->f_op->iterate_shared)
      46             :                 shared = true;
      47      576756 :         else if (!file->f_op->iterate)
      48           0 :                 goto out;
      49             : 
      50   185123509 :         res = security_file_permission(file, MAY_READ);
      51   185123509 :         if (res)
      52             :                 goto out;
      53             : 
      54   185123509 :         if (shared)
      55   184546753 :                 res = down_read_killable(&inode->i_rwsem);
      56             :         else
      57      576756 :                 res = down_write_killable(&inode->i_rwsem);
      58   185425752 :         if (res)
      59          28 :                 goto out;
      60             : 
      61   185425724 :         res = -ENOENT;
      62   185425724 :         if (!IS_DEADDIR(inode)) {
      63   185318212 :                 ctx->pos = file->f_pos;
      64   185318212 :                 if (shared)
      65   184741453 :                         res = file->f_op->iterate_shared(file, ctx);
      66             :                 else
      67      576759 :                         res = file->f_op->iterate(file, ctx);
      68   184935060 :                 file->f_pos = ctx->pos;
      69   184935060 :                 fsnotify_access(file);
      70   185031940 :                 file_accessed(file);
      71             :         }
      72   184979941 :         if (shared)
      73   184403182 :                 inode_unlock_shared(inode);
      74             :         else
      75      576759 :                 inode_unlock(inode);
      76   185408946 : out:
      77   185408946 :         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 16367196815 : static int verify_dirent_name(const char *name, int len)
     111             : {
     112 16367196815 :         if (len <= 0 || len >= PATH_MAX)
     113             :                 return -EIO;
     114 32734393630 :         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[];
     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 bool 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 false;
     153           0 :         buf->result = verify_dirent_name(name, namlen);
     154           0 :         if (buf->result)
     155             :                 return false;
     156           0 :         d_ino = ino;
     157           0 :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     158             :                 buf->result = -EOVERFLOW;
     159             :                 return false;
     160             :         }
     161           0 :         buf->result++;
     162           0 :         dirent = buf->dirent;
     163           0 :         if (!user_write_access_begin(dirent,
     164             :                         (unsigned long)(dirent->d_name + namlen + 1) -
     165             :                                 (unsigned long)dirent))
     166           0 :                 goto efault;
     167           0 :         unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
     168           0 :         unsafe_put_user(offset, &dirent->d_offset, efault_end);
     169           0 :         unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
     170           0 :         unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
     171           0 :         user_write_access_end();
     172           0 :         return true;
     173           0 : efault_end:
     174           0 :         user_write_access_end();
     175           0 : efault:
     176           0 :         buf->result = -EFAULT;
     177           0 :         return false;
     178             : }
     179             : 
     180           0 : SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
     181             :                 struct old_linux_dirent __user *, dirent, unsigned int, count)
     182             : {
     183           0 :         int error;
     184           0 :         struct fd f = fdget_pos(fd);
     185           0 :         struct readdir_callback buf = {
     186             :                 .ctx.actor = fillonedir,
     187             :                 .dirent = dirent
     188             :         };
     189             : 
     190           0 :         if (!f.file)
     191             :                 return -EBADF;
     192             : 
     193           0 :         error = iterate_dir(f.file, &buf.ctx);
     194           0 :         if (buf.result)
     195           0 :                 error = buf.result;
     196             : 
     197           0 :         fdput_pos(f);
     198           0 :         return error;
     199             : }
     200             : 
     201             : #endif /* __ARCH_WANT_OLD_READDIR */
     202             : 
     203             : /*
     204             :  * New, all-improved, singing, dancing, iBCS2-compliant getdents()
     205             :  * interface. 
     206             :  */
     207             : struct linux_dirent {
     208             :         unsigned long   d_ino;
     209             :         unsigned long   d_off;
     210             :         unsigned short  d_reclen;
     211             :         char            d_name[];
     212             : };
     213             : 
     214             : struct getdents_callback {
     215             :         struct dir_context ctx;
     216             :         struct linux_dirent __user * current_dir;
     217             :         int prev_reclen;
     218             :         int count;
     219             :         int error;
     220             : };
     221             : 
     222           0 : static bool filldir(struct dir_context *ctx, const char *name, int namlen,
     223             :                    loff_t offset, u64 ino, unsigned int d_type)
     224             : {
     225           0 :         struct linux_dirent __user *dirent, *prev;
     226           0 :         struct getdents_callback *buf =
     227           0 :                 container_of(ctx, struct getdents_callback, ctx);
     228           0 :         unsigned long d_ino;
     229           0 :         int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
     230             :                 sizeof(long));
     231           0 :         int prev_reclen;
     232             : 
     233           0 :         buf->error = verify_dirent_name(name, namlen);
     234           0 :         if (unlikely(buf->error))
     235             :                 return false;
     236           0 :         buf->error = -EINVAL;        /* only used if we fail.. */
     237           0 :         if (reclen > buf->count)
     238             :                 return false;
     239           0 :         d_ino = ino;
     240           0 :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     241             :                 buf->error = -EOVERFLOW;
     242             :                 return false;
     243             :         }
     244           0 :         prev_reclen = buf->prev_reclen;
     245           0 :         if (prev_reclen && signal_pending(current))
     246             :                 return false;
     247           0 :         dirent = buf->current_dir;
     248           0 :         prev = (void __user *) dirent - prev_reclen;
     249           0 :         if (!user_write_access_begin(prev, reclen + prev_reclen))
     250           0 :                 goto efault;
     251             : 
     252             :         /* This might be 'dirent->d_off', but if so it will get overwritten */
     253           0 :         unsafe_put_user(offset, &prev->d_off, efault_end);
     254           0 :         unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
     255           0 :         unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
     256           0 :         unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
     257           0 :         unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
     258           0 :         user_write_access_end();
     259             : 
     260           0 :         buf->current_dir = (void __user *)dirent + reclen;
     261           0 :         buf->prev_reclen = reclen;
     262           0 :         buf->count -= reclen;
     263           0 :         return true;
     264           0 : efault_end:
     265           0 :         user_write_access_end();
     266           0 : efault:
     267           0 :         buf->error = -EFAULT;
     268           0 :         return false;
     269             : }
     270             : 
     271           0 : SYSCALL_DEFINE3(getdents, unsigned int, fd,
     272             :                 struct linux_dirent __user *, dirent, unsigned int, count)
     273             : {
     274           0 :         struct fd f;
     275           0 :         struct getdents_callback buf = {
     276             :                 .ctx.actor = filldir,
     277             :                 .count = count,
     278             :                 .current_dir = dirent
     279             :         };
     280           0 :         int error;
     281             : 
     282           0 :         f = fdget_pos(fd);
     283           0 :         if (!f.file)
     284             :                 return -EBADF;
     285             : 
     286           0 :         error = iterate_dir(f.file, &buf.ctx);
     287           0 :         if (error >= 0)
     288           0 :                 error = buf.error;
     289           0 :         if (buf.prev_reclen) {
     290           0 :                 struct linux_dirent __user * lastdirent;
     291           0 :                 lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
     292             : 
     293           0 :                 if (put_user(buf.ctx.pos, &lastdirent->d_off))
     294             :                         error = -EFAULT;
     295             :                 else
     296           0 :                         error = count - buf.count;
     297             :         }
     298           0 :         fdput_pos(f);
     299           0 :         return error;
     300             : }
     301             : 
     302             : struct getdents_callback64 {
     303             :         struct dir_context ctx;
     304             :         struct linux_dirent64 __user * current_dir;
     305             :         int prev_reclen;
     306             :         int count;
     307             :         int error;
     308             : };
     309             : 
     310 16686716108 : static bool filldir64(struct dir_context *ctx, const char *name, int namlen,
     311             :                      loff_t offset, u64 ino, unsigned int d_type)
     312             : {
     313 16686716108 :         struct linux_dirent64 __user *dirent, *prev;
     314 16686716108 :         struct getdents_callback64 *buf =
     315 16686716108 :                 container_of(ctx, struct getdents_callback64, ctx);
     316 16686716108 :         int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
     317             :                 sizeof(u64));
     318 16686716108 :         int prev_reclen;
     319             : 
     320 16686716108 :         buf->error = verify_dirent_name(name, namlen);
     321 16232359367 :         if (unlikely(buf->error))
     322             :                 return false;
     323 16232359367 :         buf->error = -EINVAL;        /* only used if we fail.. */
     324 16232359367 :         if (reclen > buf->count)
     325             :                 return false;
     326 16222138969 :         prev_reclen = buf->prev_reclen;
     327 16222138969 :         if (prev_reclen && signal_pending(current))
     328             :                 return false;
     329 16227376461 :         dirent = buf->current_dir;
     330 16227376461 :         prev = (void __user *)dirent - prev_reclen;
     331 16227376461 :         if (!user_write_access_begin(prev, reclen + prev_reclen))
     332           0 :                 goto efault;
     333             : 
     334             :         /* This might be 'dirent->d_off', but if so it will get overwritten */
     335 16319750791 :         unsafe_put_user(offset, &prev->d_off, efault_end);
     336 16632927601 :         unsafe_put_user(ino, &dirent->d_ino, efault_end);
     337 16547892233 :         unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
     338 16253535781 :         unsafe_put_user(d_type, &dirent->d_type, efault_end);
     339 39630663738 :         unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
     340 16070267437 :         user_write_access_end();
     341             : 
     342 16232644453 :         buf->prev_reclen = reclen;
     343 16232644453 :         buf->current_dir = (void __user *)dirent + reclen;
     344 16232644453 :         buf->count -= reclen;
     345 16232644453 :         return true;
     346             : 
     347           0 : efault_end:
     348           0 :         user_write_access_end();
     349           0 : efault:
     350           0 :         buf->error = -EFAULT;
     351           0 :         return false;
     352             : }
     353             : 
     354   357006661 : SYSCALL_DEFINE3(getdents64, unsigned int, fd,
     355             :                 struct linux_dirent64 __user *, dirent, unsigned int, count)
     356             : {
     357   177453035 :         struct fd f;
     358   177453035 :         struct getdents_callback64 buf = {
     359             :                 .ctx.actor = filldir64,
     360             :                 .count = count,
     361             :                 .current_dir = dirent
     362             :         };
     363   177453035 :         int error;
     364             : 
     365   177453035 :         f = fdget_pos(fd);
     366   179544428 :         if (!f.file)
     367             :                 return -EBADF;
     368             : 
     369   179544428 :         error = iterate_dir(f.file, &buf.ctx);
     370   179536365 :         if (error >= 0)
     371   179535839 :                 error = buf.error;
     372   179536365 :         if (buf.prev_reclen) {
     373    94827610 :                 struct linux_dirent64 __user * lastdirent;
     374    94827610 :                 typeof(lastdirent->d_off) d_off = buf.ctx.pos;
     375             : 
     376    94827610 :                 lastdirent = (void __user *) buf.current_dir - buf.prev_reclen;
     377    94827610 :                 if (put_user(d_off, &lastdirent->d_off))
     378             :                         error = -EFAULT;
     379             :                 else
     380    94675935 :                         error = count - buf.count;
     381             :         }
     382   179384690 :         fdput_pos(f);
     383   179548982 :         return error;
     384             : }
     385             : 
     386             : #ifdef CONFIG_COMPAT
     387             : struct compat_old_linux_dirent {
     388             :         compat_ulong_t  d_ino;
     389             :         compat_ulong_t  d_offset;
     390             :         unsigned short  d_namlen;
     391             :         char            d_name[];
     392             : };
     393             : 
     394             : struct compat_readdir_callback {
     395             :         struct dir_context ctx;
     396             :         struct compat_old_linux_dirent __user *dirent;
     397             :         int result;
     398             : };
     399             : 
     400           0 : static bool compat_fillonedir(struct dir_context *ctx, const char *name,
     401             :                              int namlen, loff_t offset, u64 ino,
     402             :                              unsigned int d_type)
     403             : {
     404           0 :         struct compat_readdir_callback *buf =
     405           0 :                 container_of(ctx, struct compat_readdir_callback, ctx);
     406           0 :         struct compat_old_linux_dirent __user *dirent;
     407           0 :         compat_ulong_t d_ino;
     408             : 
     409           0 :         if (buf->result)
     410             :                 return false;
     411           0 :         buf->result = verify_dirent_name(name, namlen);
     412           0 :         if (buf->result)
     413             :                 return false;
     414           0 :         d_ino = ino;
     415           0 :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     416           0 :                 buf->result = -EOVERFLOW;
     417           0 :                 return false;
     418             :         }
     419           0 :         buf->result++;
     420           0 :         dirent = buf->dirent;
     421           0 :         if (!user_write_access_begin(dirent,
     422             :                         (unsigned long)(dirent->d_name + namlen + 1) -
     423             :                                 (unsigned long)dirent))
     424           0 :                 goto efault;
     425           0 :         unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
     426           0 :         unsafe_put_user(offset, &dirent->d_offset, efault_end);
     427           0 :         unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
     428           0 :         unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
     429           0 :         user_write_access_end();
     430           0 :         return true;
     431           0 : efault_end:
     432           0 :         user_write_access_end();
     433           0 : efault:
     434           0 :         buf->result = -EFAULT;
     435           0 :         return false;
     436             : }
     437             : 
     438           0 : COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
     439             :                 struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
     440             : {
     441           0 :         int error;
     442           0 :         struct fd f = fdget_pos(fd);
     443           0 :         struct compat_readdir_callback buf = {
     444             :                 .ctx.actor = compat_fillonedir,
     445             :                 .dirent = dirent
     446             :         };
     447             : 
     448           0 :         if (!f.file)
     449             :                 return -EBADF;
     450             : 
     451           0 :         error = iterate_dir(f.file, &buf.ctx);
     452           0 :         if (buf.result)
     453           0 :                 error = buf.result;
     454             : 
     455           0 :         fdput_pos(f);
     456           0 :         return error;
     457             : }
     458             : 
     459             : struct compat_linux_dirent {
     460             :         compat_ulong_t  d_ino;
     461             :         compat_ulong_t  d_off;
     462             :         unsigned short  d_reclen;
     463             :         char            d_name[];
     464             : };
     465             : 
     466             : struct compat_getdents_callback {
     467             :         struct dir_context ctx;
     468             :         struct compat_linux_dirent __user *current_dir;
     469             :         int prev_reclen;
     470             :         int count;
     471             :         int error;
     472             : };
     473             : 
     474           0 : static bool compat_filldir(struct dir_context *ctx, const char *name, int namlen,
     475             :                 loff_t offset, u64 ino, unsigned int d_type)
     476             : {
     477           0 :         struct compat_linux_dirent __user *dirent, *prev;
     478           0 :         struct compat_getdents_callback *buf =
     479           0 :                 container_of(ctx, struct compat_getdents_callback, ctx);
     480           0 :         compat_ulong_t d_ino;
     481           0 :         int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
     482             :                 namlen + 2, sizeof(compat_long_t));
     483           0 :         int prev_reclen;
     484             : 
     485           0 :         buf->error = verify_dirent_name(name, namlen);
     486           0 :         if (unlikely(buf->error))
     487             :                 return false;
     488           0 :         buf->error = -EINVAL;        /* only used if we fail.. */
     489           0 :         if (reclen > buf->count)
     490             :                 return false;
     491           0 :         d_ino = ino;
     492           0 :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     493           0 :                 buf->error = -EOVERFLOW;
     494           0 :                 return false;
     495             :         }
     496           0 :         prev_reclen = buf->prev_reclen;
     497           0 :         if (prev_reclen && signal_pending(current))
     498             :                 return false;
     499           0 :         dirent = buf->current_dir;
     500           0 :         prev = (void __user *) dirent - prev_reclen;
     501           0 :         if (!user_write_access_begin(prev, reclen + prev_reclen))
     502           0 :                 goto efault;
     503             : 
     504           0 :         unsafe_put_user(offset, &prev->d_off, efault_end);
     505           0 :         unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
     506           0 :         unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
     507           0 :         unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
     508           0 :         unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
     509           0 :         user_write_access_end();
     510             : 
     511           0 :         buf->prev_reclen = reclen;
     512           0 :         buf->current_dir = (void __user *)dirent + reclen;
     513           0 :         buf->count -= reclen;
     514           0 :         return true;
     515           0 : efault_end:
     516           0 :         user_write_access_end();
     517           0 : efault:
     518           0 :         buf->error = -EFAULT;
     519           0 :         return false;
     520             : }
     521             : 
     522           0 : COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
     523             :                 struct compat_linux_dirent __user *, dirent, unsigned int, count)
     524             : {
     525           0 :         struct fd f;
     526           0 :         struct compat_getdents_callback buf = {
     527             :                 .ctx.actor = compat_filldir,
     528             :                 .current_dir = dirent,
     529             :                 .count = count
     530             :         };
     531           0 :         int error;
     532             : 
     533           0 :         f = fdget_pos(fd);
     534           0 :         if (!f.file)
     535             :                 return -EBADF;
     536             : 
     537           0 :         error = iterate_dir(f.file, &buf.ctx);
     538           0 :         if (error >= 0)
     539           0 :                 error = buf.error;
     540           0 :         if (buf.prev_reclen) {
     541           0 :                 struct compat_linux_dirent __user * lastdirent;
     542           0 :                 lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
     543             : 
     544           0 :                 if (put_user(buf.ctx.pos, &lastdirent->d_off))
     545             :                         error = -EFAULT;
     546             :                 else
     547           0 :                         error = count - buf.count;
     548             :         }
     549           0 :         fdput_pos(f);
     550           0 :         return error;
     551             : }
     552             : #endif

Generated by: LCOV version 1.14