mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
readdir: supply dir_context.count as readdir buffer size hint
This is a preparation for large readdir buffers in fuse. Simply setting the fuse buffer size to the userspace buffer size should work, the record sizes are similar (fuse's is slightly larger than libc's, so no overflow should ever happen). Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Signed-off-by: Jaco Kroon <jaco@uls.co.za>
This commit is contained in:
parent
c31f91c6af
commit
467e245d47
@ -284,6 +284,7 @@ static int get_name(const struct path *path, char *name, struct dentry *child)
|
|||||||
};
|
};
|
||||||
struct getdents_callback buffer = {
|
struct getdents_callback buffer = {
|
||||||
.ctx.actor = filldir_one,
|
.ctx.actor = filldir_one,
|
||||||
|
.ctx.count = INT_MAX,
|
||||||
.name = name,
|
.name = name,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -351,6 +351,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list,
|
|||||||
struct path realpath;
|
struct path realpath;
|
||||||
struct ovl_readdir_data rdd = {
|
struct ovl_readdir_data rdd = {
|
||||||
.ctx.actor = ovl_fill_merge,
|
.ctx.actor = ovl_fill_merge,
|
||||||
|
.ctx.count = INT_MAX,
|
||||||
.dentry = dentry,
|
.dentry = dentry,
|
||||||
.list = list,
|
.list = list,
|
||||||
.root = root,
|
.root = root,
|
||||||
@ -571,6 +572,7 @@ static int ovl_dir_read_impure(const struct path *path, struct list_head *list,
|
|||||||
struct ovl_cache_entry *p, *n;
|
struct ovl_cache_entry *p, *n;
|
||||||
struct ovl_readdir_data rdd = {
|
struct ovl_readdir_data rdd = {
|
||||||
.ctx.actor = ovl_fill_plain,
|
.ctx.actor = ovl_fill_plain,
|
||||||
|
.ctx.count = INT_MAX,
|
||||||
.list = list,
|
.list = list,
|
||||||
.root = root,
|
.root = root,
|
||||||
};
|
};
|
||||||
@ -672,6 +674,7 @@ static bool ovl_fill_real(struct dir_context *ctx, const char *name,
|
|||||||
struct ovl_readdir_translate *rdt =
|
struct ovl_readdir_translate *rdt =
|
||||||
container_of(ctx, struct ovl_readdir_translate, ctx);
|
container_of(ctx, struct ovl_readdir_translate, ctx);
|
||||||
struct dir_context *orig_ctx = rdt->orig_ctx;
|
struct dir_context *orig_ctx = rdt->orig_ctx;
|
||||||
|
bool res;
|
||||||
|
|
||||||
if (rdt->parent_ino && strcmp(name, "..") == 0) {
|
if (rdt->parent_ino && strcmp(name, "..") == 0) {
|
||||||
ino = rdt->parent_ino;
|
ino = rdt->parent_ino;
|
||||||
@ -686,7 +689,10 @@ static bool ovl_fill_real(struct dir_context *ctx, const char *name,
|
|||||||
name, namelen, rdt->xinowarn);
|
name, namelen, rdt->xinowarn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return orig_ctx->actor(orig_ctx, name, namelen, offset, ino, d_type);
|
res = orig_ctx->actor(orig_ctx, name, namelen, offset, ino, d_type);
|
||||||
|
ctx->count = orig_ctx->count;
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ovl_is_impure_dir(struct file *file)
|
static bool ovl_is_impure_dir(struct file *file)
|
||||||
@ -713,6 +719,7 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx)
|
|||||||
const struct ovl_layer *lower_layer = ovl_layer_lower(dir);
|
const struct ovl_layer *lower_layer = ovl_layer_lower(dir);
|
||||||
struct ovl_readdir_translate rdt = {
|
struct ovl_readdir_translate rdt = {
|
||||||
.ctx.actor = ovl_fill_real,
|
.ctx.actor = ovl_fill_real,
|
||||||
|
.ctx.count = ctx->count,
|
||||||
.orig_ctx = ctx,
|
.orig_ctx = ctx,
|
||||||
.xinobits = ovl_xino_bits(ofs),
|
.xinobits = ovl_xino_bits(ofs),
|
||||||
.xinowarn = ovl_xino_warn(ofs),
|
.xinowarn = ovl_xino_warn(ofs),
|
||||||
@ -1073,6 +1080,7 @@ int ovl_check_d_type_supported(const struct path *realpath)
|
|||||||
int err;
|
int err;
|
||||||
struct ovl_readdir_data rdd = {
|
struct ovl_readdir_data rdd = {
|
||||||
.ctx.actor = ovl_check_d_type,
|
.ctx.actor = ovl_check_d_type,
|
||||||
|
.ctx.count = INT_MAX,
|
||||||
.d_type_supported = false,
|
.d_type_supported = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1094,6 +1102,7 @@ static int ovl_workdir_cleanup_recurse(struct ovl_fs *ofs, const struct path *pa
|
|||||||
struct ovl_cache_entry *p;
|
struct ovl_cache_entry *p;
|
||||||
struct ovl_readdir_data rdd = {
|
struct ovl_readdir_data rdd = {
|
||||||
.ctx.actor = ovl_fill_plain,
|
.ctx.actor = ovl_fill_plain,
|
||||||
|
.ctx.count = INT_MAX,
|
||||||
.list = &list,
|
.list = &list,
|
||||||
};
|
};
|
||||||
bool incompat = false;
|
bool incompat = false;
|
||||||
@ -1178,6 +1187,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
|
|||||||
struct ovl_cache_entry *p;
|
struct ovl_cache_entry *p;
|
||||||
struct ovl_readdir_data rdd = {
|
struct ovl_readdir_data rdd = {
|
||||||
.ctx.actor = ovl_fill_plain,
|
.ctx.actor = ovl_fill_plain,
|
||||||
|
.ctx.count = INT_MAX,
|
||||||
.list = &list,
|
.list = &list,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
29
fs/readdir.c
29
fs/readdir.c
@ -222,6 +222,7 @@ SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
|
|||||||
CLASS(fd_pos, f)(fd);
|
CLASS(fd_pos, f)(fd);
|
||||||
struct readdir_callback buf = {
|
struct readdir_callback buf = {
|
||||||
.ctx.actor = fillonedir,
|
.ctx.actor = fillonedir,
|
||||||
|
.ctx.count = 1, /* Hint to fs: just one entry. */
|
||||||
.dirent = dirent
|
.dirent = dirent
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -252,7 +253,6 @@ struct getdents_callback {
|
|||||||
struct dir_context ctx;
|
struct dir_context ctx;
|
||||||
struct linux_dirent __user * current_dir;
|
struct linux_dirent __user * current_dir;
|
||||||
int prev_reclen;
|
int prev_reclen;
|
||||||
int count;
|
|
||||||
int error;
|
int error;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -275,7 +275,7 @@ static bool filldir(struct dir_context *ctx, const char *name, int namlen,
|
|||||||
if (unlikely(buf->error))
|
if (unlikely(buf->error))
|
||||||
return false;
|
return false;
|
||||||
buf->error = -EINVAL; /* only used if we fail.. */
|
buf->error = -EINVAL; /* only used if we fail.. */
|
||||||
if (reclen > buf->count)
|
if (reclen > ctx->count)
|
||||||
return false;
|
return false;
|
||||||
d_ino = ino;
|
d_ino = ino;
|
||||||
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
|
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
|
||||||
@ -300,7 +300,7 @@ static bool filldir(struct dir_context *ctx, const char *name, int namlen,
|
|||||||
|
|
||||||
buf->current_dir = (void __user *)dirent + reclen;
|
buf->current_dir = (void __user *)dirent + reclen;
|
||||||
buf->prev_reclen = reclen;
|
buf->prev_reclen = reclen;
|
||||||
buf->count -= reclen;
|
ctx->count -= reclen;
|
||||||
return true;
|
return true;
|
||||||
efault_end:
|
efault_end:
|
||||||
user_write_access_end();
|
user_write_access_end();
|
||||||
@ -315,7 +315,7 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
|
|||||||
CLASS(fd_pos, f)(fd);
|
CLASS(fd_pos, f)(fd);
|
||||||
struct getdents_callback buf = {
|
struct getdents_callback buf = {
|
||||||
.ctx.actor = filldir,
|
.ctx.actor = filldir,
|
||||||
.count = count,
|
.ctx.count = count,
|
||||||
.current_dir = dirent
|
.current_dir = dirent
|
||||||
};
|
};
|
||||||
int error;
|
int error;
|
||||||
@ -333,7 +333,7 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
|
|||||||
if (put_user(buf.ctx.pos, &lastdirent->d_off))
|
if (put_user(buf.ctx.pos, &lastdirent->d_off))
|
||||||
error = -EFAULT;
|
error = -EFAULT;
|
||||||
else
|
else
|
||||||
error = count - buf.count;
|
error = count - buf.ctx.count;
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@ -342,7 +342,6 @@ struct getdents_callback64 {
|
|||||||
struct dir_context ctx;
|
struct dir_context ctx;
|
||||||
struct linux_dirent64 __user * current_dir;
|
struct linux_dirent64 __user * current_dir;
|
||||||
int prev_reclen;
|
int prev_reclen;
|
||||||
int count;
|
|
||||||
int error;
|
int error;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -364,7 +363,7 @@ static bool filldir64(struct dir_context *ctx, const char *name, int namlen,
|
|||||||
if (unlikely(buf->error))
|
if (unlikely(buf->error))
|
||||||
return false;
|
return false;
|
||||||
buf->error = -EINVAL; /* only used if we fail.. */
|
buf->error = -EINVAL; /* only used if we fail.. */
|
||||||
if (reclen > buf->count)
|
if (reclen > ctx->count)
|
||||||
return false;
|
return false;
|
||||||
prev_reclen = buf->prev_reclen;
|
prev_reclen = buf->prev_reclen;
|
||||||
if (!(flags & FILLDIR_FLAG_NOINTR) && prev_reclen && signal_pending(current))
|
if (!(flags & FILLDIR_FLAG_NOINTR) && prev_reclen && signal_pending(current))
|
||||||
@ -384,7 +383,7 @@ static bool filldir64(struct dir_context *ctx, const char *name, int namlen,
|
|||||||
|
|
||||||
buf->prev_reclen = reclen;
|
buf->prev_reclen = reclen;
|
||||||
buf->current_dir = (void __user *)dirent + reclen;
|
buf->current_dir = (void __user *)dirent + reclen;
|
||||||
buf->count -= reclen;
|
ctx->count -= reclen;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
efault_end:
|
efault_end:
|
||||||
@ -400,7 +399,7 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
|
|||||||
CLASS(fd_pos, f)(fd);
|
CLASS(fd_pos, f)(fd);
|
||||||
struct getdents_callback64 buf = {
|
struct getdents_callback64 buf = {
|
||||||
.ctx.actor = filldir64,
|
.ctx.actor = filldir64,
|
||||||
.count = count,
|
.ctx.count = count,
|
||||||
.current_dir = dirent
|
.current_dir = dirent
|
||||||
};
|
};
|
||||||
int error;
|
int error;
|
||||||
@ -419,7 +418,7 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
|
|||||||
if (put_user(d_off, &lastdirent->d_off))
|
if (put_user(d_off, &lastdirent->d_off))
|
||||||
error = -EFAULT;
|
error = -EFAULT;
|
||||||
else
|
else
|
||||||
error = count - buf.count;
|
error = count - buf.ctx.count;
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@ -483,6 +482,7 @@ COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
|
|||||||
CLASS(fd_pos, f)(fd);
|
CLASS(fd_pos, f)(fd);
|
||||||
struct compat_readdir_callback buf = {
|
struct compat_readdir_callback buf = {
|
||||||
.ctx.actor = compat_fillonedir,
|
.ctx.actor = compat_fillonedir,
|
||||||
|
.ctx.count = 1, /* Hint to fs: just one entry. */
|
||||||
.dirent = dirent
|
.dirent = dirent
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -507,7 +507,6 @@ 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;
|
||||||
int prev_reclen;
|
int prev_reclen;
|
||||||
int count;
|
|
||||||
int error;
|
int error;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -530,7 +529,7 @@ static bool compat_filldir(struct dir_context *ctx, const char *name, int namlen
|
|||||||
if (unlikely(buf->error))
|
if (unlikely(buf->error))
|
||||||
return false;
|
return false;
|
||||||
buf->error = -EINVAL; /* only used if we fail.. */
|
buf->error = -EINVAL; /* only used if we fail.. */
|
||||||
if (reclen > buf->count)
|
if (reclen > ctx->count)
|
||||||
return false;
|
return false;
|
||||||
d_ino = ino;
|
d_ino = ino;
|
||||||
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
|
if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
|
||||||
@ -554,7 +553,7 @@ static bool compat_filldir(struct dir_context *ctx, const char *name, int namlen
|
|||||||
|
|
||||||
buf->prev_reclen = reclen;
|
buf->prev_reclen = reclen;
|
||||||
buf->current_dir = (void __user *)dirent + reclen;
|
buf->current_dir = (void __user *)dirent + reclen;
|
||||||
buf->count -= reclen;
|
ctx->count -= reclen;
|
||||||
return true;
|
return true;
|
||||||
efault_end:
|
efault_end:
|
||||||
user_write_access_end();
|
user_write_access_end();
|
||||||
@ -569,8 +568,8 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
|
|||||||
CLASS(fd_pos, f)(fd);
|
CLASS(fd_pos, f)(fd);
|
||||||
struct compat_getdents_callback buf = {
|
struct compat_getdents_callback buf = {
|
||||||
.ctx.actor = compat_filldir,
|
.ctx.actor = compat_filldir,
|
||||||
|
.ctx.count = count,
|
||||||
.current_dir = dirent,
|
.current_dir = dirent,
|
||||||
.count = count
|
|
||||||
};
|
};
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
@ -587,7 +586,7 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
|
|||||||
if (put_user(buf.ctx.pos, &lastdirent->d_off))
|
if (put_user(buf.ctx.pos, &lastdirent->d_off))
|
||||||
error = -EFAULT;
|
error = -EFAULT;
|
||||||
else
|
else
|
||||||
error = count - buf.count;
|
error = count - buf.ctx.count;
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -2071,6 +2071,13 @@ typedef bool (*filldir_t)(struct dir_context *, const char *, int, loff_t, u64,
|
|||||||
struct dir_context {
|
struct dir_context {
|
||||||
filldir_t actor;
|
filldir_t actor;
|
||||||
loff_t pos;
|
loff_t pos;
|
||||||
|
/*
|
||||||
|
* Filesystems MUST NOT MODIFY count, but may use as a hint:
|
||||||
|
* 0 unknown
|
||||||
|
* > 0 space in buffer (assume at least one entry)
|
||||||
|
* INT_MAX unlimited
|
||||||
|
*/
|
||||||
|
int count;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* If OR-ed with d_type, pending signals are not checked */
|
/* If OR-ed with d_type, pending signals are not checked */
|
||||||
|
Loading…
Reference in New Issue
Block a user