LCOV - code coverage report
Current view: top level - fs - readdir.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 126 0.0 %
Date: 2023-08-24 13:40:31 Functions: 0 11 0.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           0 : int iterate_dir(struct file *file, struct dir_context *ctx)
      41             : {
      42           0 :         struct inode *inode = file_inode(file);
      43           0 :         bool shared = false;
      44           0 :         int res = -ENOTDIR;
      45           0 :         if (file->f_op->iterate_shared)
      46             :                 shared = true;
      47           0 :         else if (!file->f_op->iterate)
      48             :                 goto out;
      49             : 
      50           0 :         res = security_file_permission(file, MAY_READ);
      51             :         if (res)
      52             :                 goto out;
      53             : 
      54           0 :         if (shared)
      55           0 :                 res = down_read_killable(&inode->i_rwsem);
      56             :         else
      57           0 :                 res = down_write_killable(&inode->i_rwsem);
      58           0 :         if (res)
      59             :                 goto out;
      60             : 
      61           0 :         res = -ENOENT;
      62           0 :         if (!IS_DEADDIR(inode)) {
      63           0 :                 ctx->pos = file->f_pos;
      64           0 :                 if (shared)
      65           0 :                         res = file->f_op->iterate_shared(file, ctx);
      66             :                 else
      67           0 :                         res = file->f_op->iterate(file, ctx);
      68           0 :                 file->f_pos = ctx->pos;
      69           0 :                 fsnotify_access(file);
      70             :                 file_accessed(file);
      71             :         }
      72           0 :         if (shared)
      73             :                 inode_unlock_shared(inode);
      74             :         else
      75             :                 inode_unlock(inode);
      76             : out:
      77           0 :         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           0 : static int verify_dirent_name(const char *name, int len)
     111             : {
     112           0 :         if (len <= 0 || len >= PATH_MAX)
     113             :                 return -EIO;
     114           0 :         if (memchr(name, '/', len))
     115             :                 return -EIO;
     116           0 :         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             :         struct old_linux_dirent __user * dirent;
     149             :         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             :         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             :                 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             :         user_write_access_end();
     172             :         return true;
     173             : efault_end:
     174             :         user_write_access_end();
     175             : 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             :         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             :         struct linux_dirent __user *dirent, *prev;
     226           0 :         struct getdents_callback *buf =
     227           0 :                 container_of(ctx, struct getdents_callback, ctx);
     228             :         unsigned long d_ino;
     229           0 :         int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
     230             :                 sizeof(long));
     231             :         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             :         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             :                 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             :         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             : efault_end:
     265             :         user_write_access_end();
     266             : 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             :         struct fd f;
     275           0 :         struct getdents_callback buf = {
     276             :                 .ctx.actor = filldir,
     277             :                 .count = count,
     278             :                 .current_dir = dirent
     279             :         };
     280             :         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             :                 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           0 : static bool filldir64(struct dir_context *ctx, const char *name, int namlen,
     311             :                      loff_t offset, u64 ino, unsigned int d_type)
     312             : {
     313             :         struct linux_dirent64 __user *dirent, *prev;
     314           0 :         struct getdents_callback64 *buf =
     315           0 :                 container_of(ctx, struct getdents_callback64, ctx);
     316           0 :         int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
     317             :                 sizeof(u64));
     318             :         int prev_reclen;
     319             : 
     320           0 :         buf->error = verify_dirent_name(name, namlen);
     321           0 :         if (unlikely(buf->error))
     322             :                 return false;
     323           0 :         buf->error = -EINVAL;        /* only used if we fail.. */
     324           0 :         if (reclen > buf->count)
     325             :                 return false;
     326           0 :         prev_reclen = buf->prev_reclen;
     327           0 :         if (prev_reclen && signal_pending(current))
     328             :                 return false;
     329           0 :         dirent = buf->current_dir;
     330           0 :         prev = (void __user *)dirent - prev_reclen;
     331           0 :         if (!user_write_access_begin(prev, reclen + prev_reclen))
     332             :                 goto efault;
     333             : 
     334             :         /* This might be 'dirent->d_off', but if so it will get overwritten */
     335           0 :         unsafe_put_user(offset, &prev->d_off, efault_end);
     336           0 :         unsafe_put_user(ino, &dirent->d_ino, efault_end);
     337           0 :         unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
     338           0 :         unsafe_put_user(d_type, &dirent->d_type, efault_end);
     339           0 :         unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
     340             :         user_write_access_end();
     341             : 
     342           0 :         buf->prev_reclen = reclen;
     343           0 :         buf->current_dir = (void __user *)dirent + reclen;
     344           0 :         buf->count -= reclen;
     345           0 :         return true;
     346             : 
     347             : efault_end:
     348             :         user_write_access_end();
     349             : efault:
     350           0 :         buf->error = -EFAULT;
     351           0 :         return false;
     352             : }
     353             : 
     354           0 : SYSCALL_DEFINE3(getdents64, unsigned int, fd,
     355             :                 struct linux_dirent64 __user *, dirent, unsigned int, count)
     356             : {
     357             :         struct fd f;
     358           0 :         struct getdents_callback64 buf = {
     359             :                 .ctx.actor = filldir64,
     360             :                 .count = count,
     361             :                 .current_dir = dirent
     362             :         };
     363             :         int error;
     364             : 
     365           0 :         f = fdget_pos(fd);
     366           0 :         if (!f.file)
     367             :                 return -EBADF;
     368             : 
     369           0 :         error = iterate_dir(f.file, &buf.ctx);
     370           0 :         if (error >= 0)
     371           0 :                 error = buf.error;
     372           0 :         if (buf.prev_reclen) {
     373             :                 struct linux_dirent64 __user * lastdirent;
     374           0 :                 typeof(lastdirent->d_off) d_off = buf.ctx.pos;
     375             : 
     376           0 :                 lastdirent = (void __user *) buf.current_dir - buf.prev_reclen;
     377           0 :                 if (put_user(d_off, &lastdirent->d_off))
     378             :                         error = -EFAULT;
     379             :                 else
     380           0 :                         error = count - buf.count;
     381             :         }
     382           0 :         fdput_pos(f);
     383           0 :         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             : 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             :         struct compat_readdir_callback *buf =
     405             :                 container_of(ctx, struct compat_readdir_callback, ctx);
     406             :         struct compat_old_linux_dirent __user *dirent;
     407             :         compat_ulong_t d_ino;
     408             : 
     409             :         if (buf->result)
     410             :                 return false;
     411             :         buf->result = verify_dirent_name(name, namlen);
     412             :         if (buf->result)
     413             :                 return false;
     414             :         d_ino = ino;
     415             :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     416             :                 buf->result = -EOVERFLOW;
     417             :                 return false;
     418             :         }
     419             :         buf->result++;
     420             :         dirent = buf->dirent;
     421             :         if (!user_write_access_begin(dirent,
     422             :                         (unsigned long)(dirent->d_name + namlen + 1) -
     423             :                                 (unsigned long)dirent))
     424             :                 goto efault;
     425             :         unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
     426             :         unsafe_put_user(offset, &dirent->d_offset, efault_end);
     427             :         unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
     428             :         unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
     429             :         user_write_access_end();
     430             :         return true;
     431             : efault_end:
     432             :         user_write_access_end();
     433             : efault:
     434             :         buf->result = -EFAULT;
     435             :         return false;
     436             : }
     437             : 
     438             : COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
     439             :                 struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
     440             : {
     441             :         int error;
     442             :         struct fd f = fdget_pos(fd);
     443             :         struct compat_readdir_callback buf = {
     444             :                 .ctx.actor = compat_fillonedir,
     445             :                 .dirent = dirent
     446             :         };
     447             : 
     448             :         if (!f.file)
     449             :                 return -EBADF;
     450             : 
     451             :         error = iterate_dir(f.file, &buf.ctx);
     452             :         if (buf.result)
     453             :                 error = buf.result;
     454             : 
     455             :         fdput_pos(f);
     456             :         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             : 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             :         struct compat_linux_dirent __user *dirent, *prev;
     478             :         struct compat_getdents_callback *buf =
     479             :                 container_of(ctx, struct compat_getdents_callback, ctx);
     480             :         compat_ulong_t d_ino;
     481             :         int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
     482             :                 namlen + 2, sizeof(compat_long_t));
     483             :         int prev_reclen;
     484             : 
     485             :         buf->error = verify_dirent_name(name, namlen);
     486             :         if (unlikely(buf->error))
     487             :                 return false;
     488             :         buf->error = -EINVAL;        /* only used if we fail.. */
     489             :         if (reclen > buf->count)
     490             :                 return false;
     491             :         d_ino = ino;
     492             :         if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
     493             :                 buf->error = -EOVERFLOW;
     494             :                 return false;
     495             :         }
     496             :         prev_reclen = buf->prev_reclen;
     497             :         if (prev_reclen && signal_pending(current))
     498             :                 return false;
     499             :         dirent = buf->current_dir;
     500             :         prev = (void __user *) dirent - prev_reclen;
     501             :         if (!user_write_access_begin(prev, reclen + prev_reclen))
     502             :                 goto efault;
     503             : 
     504             :         unsafe_put_user(offset, &prev->d_off, efault_end);
     505             :         unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
     506             :         unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
     507             :         unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
     508             :         unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
     509             :         user_write_access_end();
     510             : 
     511             :         buf->prev_reclen = reclen;
     512             :         buf->current_dir = (void __user *)dirent + reclen;
     513             :         buf->count -= reclen;
     514             :         return true;
     515             : efault_end:
     516             :         user_write_access_end();
     517             : efault:
     518             :         buf->error = -EFAULT;
     519             :         return false;
     520             : }
     521             : 
     522             : COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
     523             :                 struct compat_linux_dirent __user *, dirent, unsigned int, count)
     524             : {
     525             :         struct fd f;
     526             :         struct compat_getdents_callback buf = {
     527             :                 .ctx.actor = compat_filldir,
     528             :                 .current_dir = dirent,
     529             :                 .count = count
     530             :         };
     531             :         int error;
     532             : 
     533             :         f = fdget_pos(fd);
     534             :         if (!f.file)
     535             :                 return -EBADF;
     536             : 
     537             :         error = iterate_dir(f.file, &buf.ctx);
     538             :         if (error >= 0)
     539             :                 error = buf.error;
     540             :         if (buf.prev_reclen) {
     541             :                 struct compat_linux_dirent __user * lastdirent;
     542             :                 lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
     543             : 
     544             :                 if (put_user(buf.ctx.pos, &lastdirent->d_off))
     545             :                         error = -EFAULT;
     546             :                 else
     547             :                         error = count - buf.count;
     548             :         }
     549             :         fdput_pos(f);
     550             :         return error;
     551             : }
     552             : #endif

Generated by: LCOV version 1.14