mirror of https://github.com/torvalds/linux.git
readdir.c: get compat_filldir() more or less in sync with filldir()
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
391b7461d4
commit
82af599b70
51
fs/readdir.c
51
fs/readdir.c
|
|
@ -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 {
|
||||||
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,
|
||||||
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;
|
||||||
|
if (!user_write_access_begin(prev, reclen + prev_reclen))
|
||||||
goto efault;
|
goto efault;
|
||||||
if (__put_user(reclen, &dirent->d_reclen))
|
|
||||||
goto efault;
|
unsafe_put_user(offset, &prev->d_off, efault_end);
|
||||||
if (copy_to_user(dirent->d_name, name, namlen))
|
unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
|
||||||
goto efault;
|
unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
|
||||||
if (__put_user(0, dirent->d_name + namlen))
|
unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
|
||||||
goto efault;
|
unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
|
||||||
if (__put_user(d_type, (char __user *) dirent + reclen - 1))
|
user_write_access_end();
|
||||||
goto efault;
|
|
||||||
buf->previous = dirent;
|
buf->prev_reclen = reclen;
|
||||||
dirent = (void __user *)dirent + reclen;
|
buf->current_dir = (void __user *)dirent + reclen;
|
||||||
buf->current_dir = dirent;
|
|
||||||
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,
|
||||||
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,
|
||||||
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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue