Skip to content

Commit 82af599

Browse files
author
Al Viro
committed
readdir.c: get compat_filldir() more or less in sync with filldir()
Signed-off-by: Al Viro <[email protected]>
1 parent 391b746 commit 82af599

File tree

1 file changed

+27
-24
lines changed

1 file changed

+27
-24
lines changed

fs/readdir.c

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -473,21 +473,25 @@ struct compat_linux_dirent {
473473
struct compat_getdents_callback {
474474
struct dir_context ctx;
475475
struct compat_linux_dirent __user *current_dir;
476-
struct compat_linux_dirent __user *previous;
476+
int prev_reclen;
477477
int count;
478478
int error;
479479
};
480480

481481
static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
482482
loff_t offset, u64 ino, unsigned int d_type)
483483
{
484-
struct compat_linux_dirent __user * dirent;
484+
struct compat_linux_dirent __user *dirent, *prev;
485485
struct compat_getdents_callback *buf =
486486
container_of(ctx, struct compat_getdents_callback, ctx);
487487
compat_ulong_t d_ino;
488488
int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
489489
namlen + 2, sizeof(compat_long_t));
490+
int prev_reclen;
490491

492+
buf->error = verify_dirent_name(name, namlen);
493+
if (unlikely(buf->error))
494+
return buf->error;
491495
buf->error = -EINVAL; /* only used if we fail.. */
492496
if (reclen > buf->count)
493497
return -EINVAL;
@@ -496,29 +500,27 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
496500
buf->error = -EOVERFLOW;
497501
return -EOVERFLOW;
498502
}
499-
dirent = buf->previous;
500-
if (dirent) {
501-
if (signal_pending(current))
502-
return -EINTR;
503-
if (__put_user(offset, &dirent->d_off))
504-
goto efault;
505-
}
503+
prev_reclen = buf->prev_reclen;
504+
if (prev_reclen && signal_pending(current))
505+
return -EINTR;
506506
dirent = buf->current_dir;
507-
if (__put_user(d_ino, &dirent->d_ino))
508-
goto efault;
509-
if (__put_user(reclen, &dirent->d_reclen))
510-
goto efault;
511-
if (copy_to_user(dirent->d_name, name, namlen))
512-
goto efault;
513-
if (__put_user(0, dirent->d_name + namlen))
514-
goto efault;
515-
if (__put_user(d_type, (char __user *) dirent + reclen - 1))
507+
prev = (void __user *) dirent - prev_reclen;
508+
if (!user_write_access_begin(prev, reclen + prev_reclen))
516509
goto efault;
517-
buf->previous = dirent;
518-
dirent = (void __user *)dirent + reclen;
519-
buf->current_dir = dirent;
510+
511+
unsafe_put_user(offset, &prev->d_off, efault_end);
512+
unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
513+
unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
514+
unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
515+
unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
516+
user_write_access_end();
517+
518+
buf->prev_reclen = reclen;
519+
buf->current_dir = (void __user *)dirent + reclen;
520520
buf->count -= reclen;
521521
return 0;
522+
efault_end:
523+
user_write_access_end();
522524
efault:
523525
buf->error = -EFAULT;
524526
return -EFAULT;
@@ -528,7 +530,6 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
528530
struct compat_linux_dirent __user *, dirent, unsigned int, count)
529531
{
530532
struct fd f;
531-
struct compat_linux_dirent __user * lastdirent;
532533
struct compat_getdents_callback buf = {
533534
.ctx.actor = compat_filldir,
534535
.current_dir = dirent,
@@ -546,8 +547,10 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
546547
error = iterate_dir(f.file, &buf.ctx);
547548
if (error >= 0)
548549
error = buf.error;
549-
lastdirent = buf.previous;
550-
if (lastdirent) {
550+
if (buf.prev_reclen) {
551+
struct compat_linux_dirent __user * lastdirent;
552+
lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
553+
551554
if (put_user(buf.ctx.pos, &lastdirent->d_off))
552555
error = -EFAULT;
553556
else

0 commit comments

Comments
 (0)