Message ID | 20250416090728.923460-1-mszeredi@redhat.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | [RFC] fuse: don't allow signals to interrupt getdents copying | expand |
On 4/16/25 11:07, Miklos Szeredi wrote: > When getting the directory contents, the entries are fist fetched to a > kernel buffer, then they are copied to userspace with dir_emit(). This > second phase is non-blocking as long as the userspace buffer is not paged > out, making it interruptible makes zero sense. > > Overload d_type as flags, since it only uses 4 bits from 32. > > Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> > --- > fs/fuse/readdir.c | 4 ++-- > fs/readdir.c | 15 ++++++++++++--- > include/linux/fs.h | 3 +++ > 3 files changed, 17 insertions(+), 5 deletions(-) > > diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c > index 17ce9636a2b1..edcd6f18a8a8 100644 > --- a/fs/fuse/readdir.c > +++ b/fs/fuse/readdir.c > @@ -120,7 +120,7 @@ static bool fuse_emit(struct file *file, struct dir_context *ctx, > fuse_add_dirent_to_cache(file, dirent, ctx->pos); > > return dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino, > - dirent->type); > + dirent->type | FILLDIR_FLAG_NOINTR); > } > > static int parse_dirfile(char *buf, size_t nbytes, struct file *file, > @@ -419,7 +419,7 @@ static enum fuse_parse_result fuse_parse_cache(struct fuse_file *ff, > if (ff->readdir.pos == ctx->pos) { > res = FOUND_SOME; > if (!dir_emit(ctx, dirent->name, dirent->namelen, > - dirent->ino, dirent->type)) > + dirent->ino, dirent->type | FILLDIR_FLAG_NOINTR)) > return FOUND_ALL; > ctx->pos = dirent->off; > } > diff --git a/fs/readdir.c b/fs/readdir.c > index 0038efda417b..00ceae3fc2e3 100644 > --- a/fs/readdir.c > +++ b/fs/readdir.c > @@ -266,6 +266,9 @@ static bool filldir(struct dir_context *ctx, const char *name, int namlen, > int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, > sizeof(long)); > int prev_reclen; > + unsigned int flags = d_type; > + > + d_type &= S_DT_MASK; > > buf->error = verify_dirent_name(name, namlen); > if (unlikely(buf->error)) > @@ -279,7 +282,7 @@ static bool filldir(struct dir_context *ctx, const char *name, int namlen, > return false; > } > prev_reclen = buf->prev_reclen; > - if (prev_reclen && signal_pending(current)) > + if (!(flags & FILLDIR_FLAG_NOINTR) && prev_reclen && signal_pending(current)) > return false; > dirent = buf->current_dir; > prev = (void __user *) dirent - prev_reclen; > @@ -351,6 +354,9 @@ static bool filldir64(struct dir_context *ctx, const char *name, int namlen, > int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, > sizeof(u64)); > int prev_reclen; > + unsigned int flags = d_type; > + > + d_type &= S_DT_MASK; > > buf->error = verify_dirent_name(name, namlen); > if (unlikely(buf->error)) > @@ -359,7 +365,7 @@ static bool filldir64(struct dir_context *ctx, const char *name, int namlen, > if (reclen > buf->count) > return false; > prev_reclen = buf->prev_reclen; > - if (prev_reclen && signal_pending(current)) > + if (!(flags & FILLDIR_FLAG_NOINTR) && prev_reclen && signal_pending(current)) > return false; > dirent = buf->current_dir; > prev = (void __user *)dirent - prev_reclen; > @@ -513,6 +519,9 @@ static bool compat_filldir(struct dir_context *ctx, const char *name, int namlen > int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) + > namlen + 2, sizeof(compat_long_t)); > int prev_reclen; > + unsigned int flags = d_type; > + > + d_type &= S_DT_MASK; > > buf->error = verify_dirent_name(name, namlen); > if (unlikely(buf->error)) > @@ -526,7 +535,7 @@ static bool compat_filldir(struct dir_context *ctx, const char *name, int namlen > return false; > } > prev_reclen = buf->prev_reclen; > - if (prev_reclen && signal_pending(current)) > + if (!(flags & FILLDIR_FLAG_NOINTR) && prev_reclen && signal_pending(current)) > return false; > dirent = buf->current_dir; > prev = (void __user *) dirent - prev_reclen; > diff --git a/include/linux/fs.h b/include/linux/fs.h > index 016b0fe1536e..0f2a1a572e3a 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -2073,6 +2073,9 @@ struct dir_context { > loff_t pos; > }; > > +/* If OR-ed with d_type, pending signals are not checked */ > +#define FILLDIR_FLAG_NOINTR 0x1000 This is the part that might be confusing - it is in another file than DT_MAX / S_DT_MASK - it might be hard to know about FILLDIR_FLAG_NOINTR and possible other future flags? But then it is certainly not a file type, having it in fs.h makes sense from that point of view. Reviewed-by: Bernd Schubert <bschubert@ddn.com>
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index 17ce9636a2b1..edcd6f18a8a8 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -120,7 +120,7 @@ static bool fuse_emit(struct file *file, struct dir_context *ctx, fuse_add_dirent_to_cache(file, dirent, ctx->pos); return dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino, - dirent->type); + dirent->type | FILLDIR_FLAG_NOINTR); } static int parse_dirfile(char *buf, size_t nbytes, struct file *file, @@ -419,7 +419,7 @@ static enum fuse_parse_result fuse_parse_cache(struct fuse_file *ff, if (ff->readdir.pos == ctx->pos) { res = FOUND_SOME; if (!dir_emit(ctx, dirent->name, dirent->namelen, - dirent->ino, dirent->type)) + dirent->ino, dirent->type | FILLDIR_FLAG_NOINTR)) return FOUND_ALL; ctx->pos = dirent->off; } diff --git a/fs/readdir.c b/fs/readdir.c index 0038efda417b..00ceae3fc2e3 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -266,6 +266,9 @@ static bool filldir(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, sizeof(long)); int prev_reclen; + unsigned int flags = d_type; + + d_type &= S_DT_MASK; buf->error = verify_dirent_name(name, namlen); if (unlikely(buf->error)) @@ -279,7 +282,7 @@ static bool filldir(struct dir_context *ctx, const char *name, int namlen, return false; } prev_reclen = buf->prev_reclen; - if (prev_reclen && signal_pending(current)) + if (!(flags & FILLDIR_FLAG_NOINTR) && prev_reclen && signal_pending(current)) return false; dirent = buf->current_dir; prev = (void __user *) dirent - prev_reclen; @@ -351,6 +354,9 @@ static bool filldir64(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, sizeof(u64)); int prev_reclen; + unsigned int flags = d_type; + + d_type &= S_DT_MASK; buf->error = verify_dirent_name(name, namlen); if (unlikely(buf->error)) @@ -359,7 +365,7 @@ static bool filldir64(struct dir_context *ctx, const char *name, int namlen, if (reclen > buf->count) return false; prev_reclen = buf->prev_reclen; - if (prev_reclen && signal_pending(current)) + if (!(flags & FILLDIR_FLAG_NOINTR) && prev_reclen && signal_pending(current)) return false; dirent = buf->current_dir; prev = (void __user *)dirent - prev_reclen; @@ -513,6 +519,9 @@ static bool compat_filldir(struct dir_context *ctx, const char *name, int namlen int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) + namlen + 2, sizeof(compat_long_t)); int prev_reclen; + unsigned int flags = d_type; + + d_type &= S_DT_MASK; buf->error = verify_dirent_name(name, namlen); if (unlikely(buf->error)) @@ -526,7 +535,7 @@ static bool compat_filldir(struct dir_context *ctx, const char *name, int namlen return false; } prev_reclen = buf->prev_reclen; - if (prev_reclen && signal_pending(current)) + if (!(flags & FILLDIR_FLAG_NOINTR) && prev_reclen && signal_pending(current)) return false; dirent = buf->current_dir; prev = (void __user *) dirent - prev_reclen; diff --git a/include/linux/fs.h b/include/linux/fs.h index 016b0fe1536e..0f2a1a572e3a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2073,6 +2073,9 @@ struct dir_context { loff_t pos; }; +/* If OR-ed with d_type, pending signals are not checked */ +#define FILLDIR_FLAG_NOINTR 0x1000 + /* * These flags let !MMU mmap() govern direct device mapping vs immediate * copying more easily for MAP_PRIVATE, especially for ROM filesystems.
When getting the directory contents, the entries are fist fetched to a kernel buffer, then they are copied to userspace with dir_emit(). This second phase is non-blocking as long as the userspace buffer is not paged out, making it interruptible makes zero sense. Overload d_type as flags, since it only uses 4 bits from 32. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> --- fs/fuse/readdir.c | 4 ++-- fs/readdir.c | 15 ++++++++++++--- include/linux/fs.h | 3 +++ 3 files changed, 17 insertions(+), 5 deletions(-)