Commit 82af599b authored by Al Viro's avatar Al Viro

readdir.c: get compat_filldir() more or less in sync with filldir()

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 391b7461
...@@ -473,7 +473,7 @@ struct compat_linux_dirent { ...@@ -473,7 +473,7 @@ struct compat_linux_dirent {
struct compat_getdents_callback { struct compat_getdents_callback {
struct dir_context ctx; struct dir_context ctx;
struct compat_linux_dirent __user *current_dir; struct compat_linux_dirent __user *current_dir;
struct compat_linux_dirent __user *previous; int prev_reclen;
int count; int count;
int error; int error;
}; };
...@@ -481,13 +481,17 @@ struct compat_getdents_callback { ...@@ -481,13 +481,17 @@ struct compat_getdents_callback {
static int compat_filldir(struct dir_context *ctx, const char *name, int namlen, static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type) loff_t offset, u64 ino, unsigned int d_type)
{ {
struct compat_linux_dirent __user * dirent; struct compat_linux_dirent __user *dirent, *prev;
struct compat_getdents_callback *buf = struct compat_getdents_callback *buf =
container_of(ctx, struct compat_getdents_callback, ctx); container_of(ctx, struct compat_getdents_callback, ctx);
compat_ulong_t d_ino; compat_ulong_t d_ino;
int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) + int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
namlen + 2, sizeof(compat_long_t)); namlen + 2, sizeof(compat_long_t));
int prev_reclen;
buf->error = verify_dirent_name(name, namlen);
if (unlikely(buf->error))
return buf->error;
buf->error = -EINVAL; /* only used if we fail.. */ buf->error = -EINVAL; /* only used if we fail.. */
if (reclen > buf->count) if (reclen > buf->count)
return -EINVAL; return -EINVAL;
...@@ -496,29 +500,27 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen, ...@@ -496,29 +500,27 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
buf->error = -EOVERFLOW; buf->error = -EOVERFLOW;
return -EOVERFLOW; return -EOVERFLOW;
} }
dirent = buf->previous; prev_reclen = buf->prev_reclen;
if (dirent) { if (prev_reclen && signal_pending(current))
if (signal_pending(current)) return -EINTR;
return -EINTR;
if (__put_user(offset, &dirent->d_off))
goto efault;
}
dirent = buf->current_dir; dirent = buf->current_dir;
if (__put_user(d_ino, &dirent->d_ino)) prev = (void __user *) dirent - prev_reclen;
goto efault; if (!user_write_access_begin(prev, reclen + prev_reclen))
if (__put_user(reclen, &dirent->d_reclen))
goto efault;
if (copy_to_user(dirent->d_name, name, namlen))
goto efault;
if (__put_user(0, dirent->d_name + namlen))
goto efault;
if (__put_user(d_type, (char __user *) dirent + reclen - 1))
goto efault; goto efault;
buf->previous = dirent;
dirent = (void __user *)dirent + reclen; unsafe_put_user(offset, &prev->d_off, efault_end);
buf->current_dir = dirent; unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
user_write_access_end();
buf->prev_reclen = reclen;
buf->current_dir = (void __user *)dirent + reclen;
buf->count -= reclen; buf->count -= reclen;
return 0; return 0;
efault_end:
user_write_access_end();
efault: efault:
buf->error = -EFAULT; buf->error = -EFAULT;
return -EFAULT; return -EFAULT;
...@@ -528,7 +530,6 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd, ...@@ -528,7 +530,6 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
struct compat_linux_dirent __user *, dirent, unsigned int, count) struct compat_linux_dirent __user *, dirent, unsigned int, count)
{ {
struct fd f; struct fd f;
struct compat_linux_dirent __user * lastdirent;
struct compat_getdents_callback buf = { struct compat_getdents_callback buf = {
.ctx.actor = compat_filldir, .ctx.actor = compat_filldir,
.current_dir = dirent, .current_dir = dirent,
...@@ -546,8 +547,10 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd, ...@@ -546,8 +547,10 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
error = iterate_dir(f.file, &buf.ctx); error = iterate_dir(f.file, &buf.ctx);
if (error >= 0) if (error >= 0)
error = buf.error; error = buf.error;
lastdirent = buf.previous; if (buf.prev_reclen) {
if (lastdirent) { struct compat_linux_dirent __user * lastdirent;
lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
if (put_user(buf.ctx.pos, &lastdirent->d_off)) if (put_user(buf.ctx.pos, &lastdirent->d_off))
error = -EFAULT; error = -EFAULT;
else else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment