linux/fs/readdir.c
<<
>>
Prefs
   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
  24#include <linux/uaccess.h>
  25
  26int iterate_dir(struct file *file, struct dir_context *ctx)
  27{
  28        struct inode *inode = file_inode(file);
  29        bool shared = false;
  30        int res = -ENOTDIR;
  31        if (file->f_op->iterate_shared)
  32                shared = true;
  33        else if (!file->f_op->iterate)
  34                goto out;
  35
  36        res = security_file_permission(file, MAY_READ);
  37        if (res)
  38                goto out;
  39
  40        if (shared)
  41                res = down_read_killable(&inode->i_rwsem);
  42        else
  43                res = down_write_killable(&inode->i_rwsem);
  44        if (res)
  45                goto out;
  46
  47        res = -ENOENT;
  48        if (!IS_DEADDIR(inode)) {
  49                ctx->pos = file->f_pos;
  50                if (shared)
  51                        res = file->f_op->iterate_shared(file, ctx);
  52                else
  53                        res = file->f_op->iterate(file, ctx);
  54                file->f_pos = ctx->pos;
  55                fsnotify_access(file);
  56                file_accessed(file);
  57        }
  58        if (shared)
  59                inode_unlock_shared(inode);
  60        else
  61                inode_unlock(inode);
  62out:
  63        return res;
  64}
  65EXPORT_SYMBOL(iterate_dir);
  66
  67/*
  68 * Traditional linux readdir() handling..
  69 *
  70 * "count=1" is a special case, meaning that the buffer is one
  71 * dirent-structure in size and that the code can't handle more
  72 * anyway. Thus the special "fillonedir()" function for that
  73 * case (the low-level handlers don't need to care about this).
  74 */
  75
  76#ifdef __ARCH_WANT_OLD_READDIR
  77
  78struct old_linux_dirent {
  79        unsigned long   d_ino;
  80        unsigned long   d_offset;
  81        unsigned short  d_namlen;
  82        char            d_name[1];
  83};
  84
  85struct readdir_callback {
  86        struct dir_context ctx;
  87        struct old_linux_dirent __user * dirent;
  88        int result;
  89};
  90
  91static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
  92                      loff_t offset, u64 ino, unsigned int d_type)
  93{
  94        struct readdir_callback *buf =
  95                container_of(ctx, struct readdir_callback, ctx);
  96        struct old_linux_dirent __user * dirent;
  97        unsigned long d_ino;
  98
  99        if (buf->result)
 100                return -EINVAL;
 101        d_ino = ino;
 102        if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
 103                buf->result = -EOVERFLOW;
 104                return -EOVERFLOW;
 105        }
 106        buf->result++;
 107        dirent = buf->dirent;
 108        if (!access_ok(VERIFY_WRITE, dirent,
 109                        (unsigned long)(dirent->d_name + namlen + 1) -
 110                                (unsigned long)dirent))
 111                goto efault;
 112        if (    __put_user(d_ino, &dirent->d_ino) ||
 113                __put_user(offset, &dirent->d_offset) ||
 114                __put_user(namlen, &dirent->d_namlen) ||
 115                __copy_to_user(dirent->d_name, name, namlen) ||
 116                __put_user(0, dirent->d_name + namlen))
 117                goto efault;
 118        return 0;
 119efault:
 120        buf->result = -EFAULT;
 121        return -EFAULT;
 122}
 123
 124SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
 125                struct old_linux_dirent __user *, dirent, unsigned int, count)
 126{
 127        int error;
 128        struct fd f = fdget_pos(fd);
 129        struct readdir_callback buf = {
 130                .ctx.actor = fillonedir,
 131                .dirent = dirent
 132        };
 133
 134        if (!f.file)
 135                return -EBADF;
 136
 137        error = iterate_dir(f.file, &buf.ctx);
 138        if (buf.result)
 139                error = buf.result;
 140
 141        fdput_pos(f);
 142        return error;
 143}
 144
 145#endif /* __ARCH_WANT_OLD_READDIR */
 146
 147/*
 148 * New, all-improved, singing, dancing, iBCS2-compliant getdents()
 149 * interface. 
 150 */
 151struct linux_dirent {
 152        unsigned long   d_ino;
 153        unsigned long   d_off;
 154        unsigned short  d_reclen;
 155        char            d_name[1];
 156};
 157
 158struct getdents_callback {
 159        struct dir_context ctx;
 160        struct linux_dirent __user * current_dir;
 161        struct linux_dirent __user * previous;
 162        int count;
 163        int error;
 164};
 165
 166static int filldir(struct dir_context *ctx, const char *name, int namlen,
 167                   loff_t offset, u64 ino, unsigned int d_type)
 168{
 169        struct linux_dirent __user * dirent;
 170        struct getdents_callback *buf =
 171                container_of(ctx, struct getdents_callback, ctx);
 172        unsigned long d_ino;
 173        int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
 174                sizeof(long));
 175
 176        buf->error = -EINVAL;   /* only used if we fail.. */
 177        if (reclen > buf->count)
 178                return -EINVAL;
 179        d_ino = ino;
 180        if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
 181                buf->error = -EOVERFLOW;
 182                return -EOVERFLOW;
 183        }
 184        dirent = buf->previous;
 185        if (dirent) {
 186                if (signal_pending(current))
 187                        return -EINTR;
 188                if (__put_user(offset, &dirent->d_off))
 189                        goto efault;
 190        }
 191        dirent = buf->current_dir;
 192        if (__put_user(d_ino, &dirent->d_ino))
 193                goto efault;
 194        if (__put_user(reclen, &dirent->d_reclen))
 195                goto efault;
 196        if (copy_to_user(dirent->d_name, name, namlen))
 197                goto efault;
 198        if (__put_user(0, dirent->d_name + namlen))
 199                goto efault;
 200        if (__put_user(d_type, (char __user *) dirent + reclen - 1))
 201                goto efault;
 202        buf->previous = dirent;
 203        dirent = (void __user *)dirent + reclen;
 204        buf->current_dir = dirent;
 205        buf->count -= reclen;
 206        return 0;
 207efault:
 208        buf->error = -EFAULT;
 209        return -EFAULT;
 210}
 211
 212SYSCALL_DEFINE3(getdents, unsigned int, fd,
 213                struct linux_dirent __user *, dirent, unsigned int, count)
 214{
 215        struct fd f;
 216        struct linux_dirent __user * lastdirent;
 217        struct getdents_callback buf = {
 218                .ctx.actor = filldir,
 219                .count = count,
 220                .current_dir = dirent
 221        };
 222        int error;
 223
 224        if (!access_ok(VERIFY_WRITE, dirent, count))
 225                return -EFAULT;
 226
 227        f = fdget_pos(fd);
 228        if (!f.file)
 229                return -EBADF;
 230
 231        error = iterate_dir(f.file, &buf.ctx);
 232        if (error >= 0)
 233                error = buf.error;
 234        lastdirent = buf.previous;
 235        if (lastdirent) {
 236                if (put_user(buf.ctx.pos, &lastdirent->d_off))
 237                        error = -EFAULT;
 238                else
 239                        error = count - buf.count;
 240        }
 241        fdput_pos(f);
 242        return error;
 243}
 244
 245struct getdents_callback64 {
 246        struct dir_context ctx;
 247        struct linux_dirent64 __user * current_dir;
 248        struct linux_dirent64 __user * previous;
 249        int count;
 250        int error;
 251};
 252
 253static int filldir64(struct dir_context *ctx, const char *name, int namlen,
 254                     loff_t offset, u64 ino, unsigned int d_type)
 255{
 256        struct linux_dirent64 __user *dirent;
 257        struct getdents_callback64 *buf =
 258                container_of(ctx, struct getdents_callback64, ctx);
 259        int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
 260                sizeof(u64));
 261
 262        buf->error = -EINVAL;   /* only used if we fail.. */
 263        if (reclen > buf->count)
 264                return -EINVAL;
 265        dirent = buf->previous;
 266        if (dirent) {
 267                if (signal_pending(current))
 268                        return -EINTR;
 269                if (__put_user(offset, &dirent->d_off))
 270                        goto efault;
 271        }
 272        dirent = buf->current_dir;
 273        if (__put_user(ino, &dirent->d_ino))
 274                goto efault;
 275        if (__put_user(0, &dirent->d_off))
 276                goto efault;
 277        if (__put_user(reclen, &dirent->d_reclen))
 278                goto efault;
 279        if (__put_user(d_type, &dirent->d_type))
 280                goto efault;
 281        if (copy_to_user(dirent->d_name, name, namlen))
 282                goto efault;
 283        if (__put_user(0, dirent->d_name + namlen))
 284                goto efault;
 285        buf->previous = dirent;
 286        dirent = (void __user *)dirent + reclen;
 287        buf->current_dir = dirent;
 288        buf->count -= reclen;
 289        return 0;
 290efault:
 291        buf->error = -EFAULT;
 292        return -EFAULT;
 293}
 294
 295int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent,
 296                    unsigned int count)
 297{
 298        struct fd f;
 299        struct linux_dirent64 __user * lastdirent;
 300        struct getdents_callback64 buf = {
 301                .ctx.actor = filldir64,
 302                .count = count,
 303                .current_dir = dirent
 304        };
 305        int error;
 306
 307        if (!access_ok(VERIFY_WRITE, dirent, count))
 308                return -EFAULT;
 309
 310        f = fdget_pos(fd);
 311        if (!f.file)
 312                return -EBADF;
 313
 314        error = iterate_dir(f.file, &buf.ctx);
 315        if (error >= 0)
 316                error = buf.error;
 317        lastdirent = buf.previous;
 318        if (lastdirent) {
 319                typeof(lastdirent->d_off) d_off = buf.ctx.pos;
 320                if (__put_user(d_off, &lastdirent->d_off))
 321                        error = -EFAULT;
 322                else
 323                        error = count - buf.count;
 324        }
 325        fdput_pos(f);
 326        return error;
 327}
 328
 329
 330SYSCALL_DEFINE3(getdents64, unsigned int, fd,
 331                struct linux_dirent64 __user *, dirent, unsigned int, count)
 332{
 333        return ksys_getdents64(fd, dirent, count);
 334}
 335
 336#ifdef CONFIG_COMPAT
 337struct compat_old_linux_dirent {
 338        compat_ulong_t  d_ino;
 339        compat_ulong_t  d_offset;
 340        unsigned short  d_namlen;
 341        char            d_name[1];
 342};
 343
 344struct compat_readdir_callback {
 345        struct dir_context ctx;
 346        struct compat_old_linux_dirent __user *dirent;
 347        int result;
 348};
 349
 350static int compat_fillonedir(struct dir_context *ctx, const char *name,
 351                             int namlen, loff_t offset, u64 ino,
 352                             unsigned int d_type)
 353{
 354        struct compat_readdir_callback *buf =
 355                container_of(ctx, struct compat_readdir_callback, ctx);
 356        struct compat_old_linux_dirent __user *dirent;
 357        compat_ulong_t d_ino;
 358
 359        if (buf->result)
 360                return -EINVAL;
 361        d_ino = ino;
 362        if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
 363                buf->result = -EOVERFLOW;
 364                return -EOVERFLOW;
 365        }
 366        buf->result++;
 367        dirent = buf->dirent;
 368        if (!access_ok(VERIFY_WRITE, dirent,
 369                        (unsigned long)(dirent->d_name + namlen + 1) -
 370                                (unsigned long)dirent))
 371                goto efault;
 372        if (    __put_user(d_ino, &dirent->d_ino) ||
 373                __put_user(offset, &dirent->d_offset) ||
 374                __put_user(namlen, &dirent->d_namlen) ||
 375                __copy_to_user(dirent->d_name, name, namlen) ||
 376                __put_user(0, dirent->d_name + namlen))
 377                goto efault;
 378        return 0;
 379efault:
 380        buf->result = -EFAULT;
 381        return -EFAULT;
 382}
 383
 384COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
 385                struct compat_old_linux_dirent __user *, dirent, unsigned int, count)
 386{
 387        int error;
 388        struct fd f = fdget_pos(fd);
 389        struct compat_readdir_callback buf = {
 390                .ctx.actor = compat_fillonedir,
 391                .dirent = dirent
 392        };
 393
 394        if (!f.file)
 395                return -EBADF;
 396
 397        error = iterate_dir(f.file, &buf.ctx);
 398        if (buf.result)
 399                error = buf.result;
 400
 401        fdput_pos(f);
 402        return error;
 403}
 404
 405struct compat_linux_dirent {
 406        compat_ulong_t  d_ino;
 407        compat_ulong_t  d_off;
 408        unsigned short  d_reclen;
 409        char            d_name[1];
 410};
 411
 412struct compat_getdents_callback {
 413        struct dir_context ctx;
 414        struct compat_linux_dirent __user *current_dir;
 415        struct compat_linux_dirent __user *previous;
 416        int count;
 417        int error;
 418};
 419
 420static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
 421                loff_t offset, u64 ino, unsigned int d_type)
 422{
 423        struct compat_linux_dirent __user * dirent;
 424        struct compat_getdents_callback *buf =
 425                container_of(ctx, struct compat_getdents_callback, ctx);
 426        compat_ulong_t d_ino;
 427        int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
 428                namlen + 2, sizeof(compat_long_t));
 429
 430        buf->error = -EINVAL;   /* only used if we fail.. */
 431        if (reclen > buf->count)
 432                return -EINVAL;
 433        d_ino = ino;
 434        if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
 435                buf->error = -EOVERFLOW;
 436                return -EOVERFLOW;
 437        }
 438        dirent = buf->previous;
 439        if (dirent) {
 440                if (signal_pending(current))
 441                        return -EINTR;
 442                if (__put_user(offset, &dirent->d_off))
 443                        goto efault;
 444        }
 445        dirent = buf->current_dir;
 446        if (__put_user(d_ino, &dirent->d_ino))
 447                goto efault;
 448        if (__put_user(reclen, &dirent->d_reclen))
 449                goto efault;
 450        if (copy_to_user(dirent->d_name, name, namlen))
 451                goto efault;
 452        if (__put_user(0, dirent->d_name + namlen))
 453                goto efault;
 454        if (__put_user(d_type, (char  __user *) dirent + reclen - 1))
 455                goto efault;
 456        buf->previous = dirent;
 457        dirent = (void __user *)dirent + reclen;
 458        buf->current_dir = dirent;
 459        buf->count -= reclen;
 460        return 0;
 461efault:
 462        buf->error = -EFAULT;
 463        return -EFAULT;
 464}
 465
 466COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
 467                struct compat_linux_dirent __user *, dirent, unsigned int, count)
 468{
 469        struct fd f;
 470        struct compat_linux_dirent __user * lastdirent;
 471        struct compat_getdents_callback buf = {
 472                .ctx.actor = compat_filldir,
 473                .current_dir = dirent,
 474                .count = count
 475        };
 476        int error;
 477
 478        if (!access_ok(VERIFY_WRITE, dirent, count))
 479                return -EFAULT;
 480
 481        f = fdget_pos(fd);
 482        if (!f.file)
 483                return -EBADF;
 484
 485        error = iterate_dir(f.file, &buf.ctx);
 486        if (error >= 0)
 487                error = buf.error;
 488        lastdirent = buf.previous;
 489        if (lastdirent) {
 490                if (put_user(buf.ctx.pos, &lastdirent->d_off))
 491                        error = -EFAULT;
 492                else
 493                        error = count - buf.count;
 494        }
 495        fdput_pos(f);
 496        return error;
 497}
 498#endif
 499