Skip to content

Commit 043930b

Browse files
committed
Merge tag 'fuse-update-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
Pull fuse update from Miklos Szeredi: - Allow some write requests to proceed in parallel - Fix a performance problem with allow_sys_admin_access - Add a special kind of invalidation that doesn't immediately purge submounts - On revalidation treat the target of rename(RENAME_NOREPLACE) the same as open(O_EXCL) - Use type safe helpers for some mnt_userns transformations * tag 'fuse-update-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: Rearrange fuse_allow_current_process checks fuse: allow non-extending parallel direct writes on the same file fuse: remove the unneeded result variable fuse: port to vfs{g,u}id_t and associated helpers fuse: Remove user_ns check for FUSE_DEV_IOC_CLONE fuse: always revalidate rename target dentry fuse: add "expire only" mode to FUSE_NOTIFY_INVAL_ENTRY fs/fuse: Replace kmap() with kmap_local_page()
2 parents 6df7cc2 + b138777 commit 043930b

File tree

7 files changed

+87
-35
lines changed

7 files changed

+87
-35
lines changed

fs/fuse/cuse.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,6 @@ static int cuse_channel_release(struct inode *inode, struct file *file)
545545
{
546546
struct fuse_dev *fud = file->private_data;
547547
struct cuse_conn *cc = fc_to_cc(fud->fc);
548-
int rc;
549548

550549
/* remove from the conntbl, no more access from this point on */
551550
mutex_lock(&cuse_lock);
@@ -560,9 +559,7 @@ static int cuse_channel_release(struct inode *inode, struct file *file)
560559
cdev_del(cc->cdev);
561560
}
562561

563-
rc = fuse_dev_release(inode, file); /* puts the base reference */
564-
565-
return rc;
562+
return fuse_dev_release(inode, file);
566563
}
567564

568565
static struct file_operations cuse_channel_fops; /* initialized during init */

fs/fuse/dev.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,7 +1498,7 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
14981498
buf[outarg.namelen] = 0;
14991499

15001500
down_read(&fc->killsb);
1501-
err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name);
1501+
err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name, outarg.flags);
15021502
up_read(&fc->killsb);
15031503
kfree(buf);
15041504
return err;
@@ -1546,7 +1546,7 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
15461546
buf[outarg.namelen] = 0;
15471547

15481548
down_read(&fc->killsb);
1549-
err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name);
1549+
err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name, 0);
15501550
up_read(&fc->killsb);
15511551
kfree(buf);
15521552
return err;
@@ -2267,8 +2267,7 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
22672267
* Check against file->f_op because CUSE
22682268
* uses the same ioctl handler.
22692269
*/
2270-
if (old->f_op == file->f_op &&
2271-
old->f_cred->user_ns == file->f_cred->user_ns)
2270+
if (old->f_op == file->f_op)
22722271
fud = fuse_get_dev(old);
22732272

22742273
if (fud) {

fs/fuse/dir.c

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
214214
if (inode && fuse_is_bad(inode))
215215
goto invalid;
216216
else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) ||
217-
(flags & (LOOKUP_EXCL | LOOKUP_REVAL))) {
217+
(flags & (LOOKUP_EXCL | LOOKUP_REVAL | LOOKUP_RENAME_TARGET))) {
218218
struct fuse_entry_out outarg;
219219
FUSE_ARGS(args);
220220
struct fuse_forget_link *forget;
@@ -1170,7 +1170,7 @@ int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask)
11701170
}
11711171

11721172
int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
1173-
u64 child_nodeid, struct qstr *name)
1173+
u64 child_nodeid, struct qstr *name, u32 flags)
11741174
{
11751175
int err = -ENOTDIR;
11761176
struct inode *parent;
@@ -1197,7 +1197,9 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
11971197
goto unlock;
11981198

11991199
fuse_dir_changed(parent);
1200-
fuse_invalidate_entry(entry);
1200+
if (!(flags & FUSE_EXPIRE_ONLY))
1201+
d_invalidate(entry);
1202+
fuse_invalidate_entry_cache(entry);
12011203

12021204
if (child_nodeid != 0 && d_really_is_positive(entry)) {
12031205
inode_lock(d_inode(entry));
@@ -1235,6 +1237,18 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
12351237
return err;
12361238
}
12371239

1240+
static inline bool fuse_permissible_uidgid(struct fuse_conn *fc)
1241+
{
1242+
const struct cred *cred = current_cred();
1243+
1244+
return (uid_eq(cred->euid, fc->user_id) &&
1245+
uid_eq(cred->suid, fc->user_id) &&
1246+
uid_eq(cred->uid, fc->user_id) &&
1247+
gid_eq(cred->egid, fc->group_id) &&
1248+
gid_eq(cred->sgid, fc->group_id) &&
1249+
gid_eq(cred->gid, fc->group_id));
1250+
}
1251+
12381252
/*
12391253
* Calling into a user-controlled filesystem gives the filesystem
12401254
* daemon ptrace-like capabilities over the current process. This
@@ -1248,26 +1262,19 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
12481262
* for which the owner of the mount has ptrace privilege. This
12491263
* excludes processes started by other users, suid or sgid processes.
12501264
*/
1251-
int fuse_allow_current_process(struct fuse_conn *fc)
1265+
bool fuse_allow_current_process(struct fuse_conn *fc)
12521266
{
1253-
const struct cred *cred;
1254-
1255-
if (allow_sys_admin_access && capable(CAP_SYS_ADMIN))
1256-
return 1;
1267+
bool allow;
12571268

12581269
if (fc->allow_other)
1259-
return current_in_userns(fc->user_ns);
1270+
allow = current_in_userns(fc->user_ns);
1271+
else
1272+
allow = fuse_permissible_uidgid(fc);
12601273

1261-
cred = current_cred();
1262-
if (uid_eq(cred->euid, fc->user_id) &&
1263-
uid_eq(cred->suid, fc->user_id) &&
1264-
uid_eq(cred->uid, fc->user_id) &&
1265-
gid_eq(cred->egid, fc->group_id) &&
1266-
gid_eq(cred->sgid, fc->group_id) &&
1267-
gid_eq(cred->gid, fc->group_id))
1268-
return 1;
1274+
if (!allow && allow_sys_admin_access && capable(CAP_SYS_ADMIN))
1275+
allow = true;
12691276

1270-
return 0;
1277+
return allow;
12711278
}
12721279

12731280
static int fuse_access(struct inode *inode, int mask)

fs/fuse/file.c

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,14 +1563,47 @@ static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to)
15631563
return res;
15641564
}
15651565

1566+
static bool fuse_direct_write_extending_i_size(struct kiocb *iocb,
1567+
struct iov_iter *iter)
1568+
{
1569+
struct inode *inode = file_inode(iocb->ki_filp);
1570+
1571+
return iocb->ki_pos + iov_iter_count(iter) > i_size_read(inode);
1572+
}
1573+
15661574
static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
15671575
{
15681576
struct inode *inode = file_inode(iocb->ki_filp);
1577+
struct file *file = iocb->ki_filp;
1578+
struct fuse_file *ff = file->private_data;
15691579
struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb);
15701580
ssize_t res;
1581+
bool exclusive_lock =
1582+
!(ff->open_flags & FOPEN_PARALLEL_DIRECT_WRITES) ||
1583+
iocb->ki_flags & IOCB_APPEND ||
1584+
fuse_direct_write_extending_i_size(iocb, from);
1585+
1586+
/*
1587+
* Take exclusive lock if
1588+
* - Parallel direct writes are disabled - a user space decision
1589+
* - Parallel direct writes are enabled and i_size is being extended.
1590+
* This might not be needed at all, but needs further investigation.
1591+
*/
1592+
if (exclusive_lock)
1593+
inode_lock(inode);
1594+
else {
1595+
inode_lock_shared(inode);
1596+
1597+
/* A race with truncate might have come up as the decision for
1598+
* the lock type was done without holding the lock, check again.
1599+
*/
1600+
if (fuse_direct_write_extending_i_size(iocb, from)) {
1601+
inode_unlock_shared(inode);
1602+
inode_lock(inode);
1603+
exclusive_lock = true;
1604+
}
1605+
}
15711606

1572-
/* Don't allow parallel writes to the same file */
1573-
inode_lock(inode);
15741607
res = generic_write_checks(iocb, from);
15751608
if (res > 0) {
15761609
if (!is_sync_kiocb(iocb) && iocb->ki_flags & IOCB_DIRECT) {
@@ -1581,7 +1614,10 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
15811614
fuse_write_update_attr(inode, iocb->ki_pos, res);
15821615
}
15831616
}
1584-
inode_unlock(inode);
1617+
if (exclusive_lock)
1618+
inode_unlock(inode);
1619+
else
1620+
inode_unlock_shared(inode);
15851621

15861622
return res;
15871623
}
@@ -2931,6 +2967,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
29312967

29322968
if (iov_iter_rw(iter) == WRITE) {
29332969
fuse_write_update_attr(inode, pos, ret);
2970+
/* For extending writes we already hold exclusive lock */
29342971
if (ret < 0 && offset + count > i_size)
29352972
fuse_do_truncate(file);
29362973
}

fs/fuse/fuse_i.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,7 +1179,7 @@ bool fuse_invalid_attr(struct fuse_attr *attr);
11791179
/**
11801180
* Is current process allowed to perform filesystem operation?
11811181
*/
1182-
int fuse_allow_current_process(struct fuse_conn *fc);
1182+
bool fuse_allow_current_process(struct fuse_conn *fc);
11831183

11841184
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
11851185

@@ -1220,7 +1220,7 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid,
12201220
* then the dentry is unhashed (d_delete()).
12211221
*/
12221222
int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
1223-
u64 child_nodeid, struct qstr *name);
1223+
u64 child_nodeid, struct qstr *name, u32 flags);
12241224

12251225
int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file,
12261226
bool isdir);

fs/fuse/readdir.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,9 +547,9 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
547547
* Contents of the page are now protected against changing by holding
548548
* the page lock.
549549
*/
550-
addr = kmap(page);
550+
addr = kmap_local_page(page);
551551
res = fuse_parse_cache(ff, addr, size, ctx);
552-
kunmap(page);
552+
kunmap_local(addr);
553553
unlock_page(page);
554554
put_page(page);
555555

include/uapi/linux/fuse.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@
197197
*
198198
* 7.37
199199
* - add FUSE_TMPFILE
200+
*
201+
* 7.38
202+
* - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
203+
* - add FOPEN_PARALLEL_DIRECT_WRITES
200204
*/
201205

202206
#ifndef _LINUX_FUSE_H
@@ -232,7 +236,7 @@
232236
#define FUSE_KERNEL_VERSION 7
233237

234238
/** Minor version number of this interface */
235-
#define FUSE_KERNEL_MINOR_VERSION 37
239+
#define FUSE_KERNEL_MINOR_VERSION 38
236240

237241
/** The node ID of the root inode */
238242
#define FUSE_ROOT_ID 1
@@ -304,13 +308,15 @@ struct fuse_file_lock {
304308
* FOPEN_CACHE_DIR: allow caching this directory
305309
* FOPEN_STREAM: the file is stream-like (no file position at all)
306310
* FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
311+
* FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode
307312
*/
308313
#define FOPEN_DIRECT_IO (1 << 0)
309314
#define FOPEN_KEEP_CACHE (1 << 1)
310315
#define FOPEN_NONSEEKABLE (1 << 2)
311316
#define FOPEN_CACHE_DIR (1 << 3)
312317
#define FOPEN_STREAM (1 << 4)
313318
#define FOPEN_NOFLUSH (1 << 5)
319+
#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
314320

315321
/**
316322
* INIT request/reply flags
@@ -491,6 +497,12 @@ struct fuse_file_lock {
491497
*/
492498
#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0)
493499

500+
/**
501+
* notify_inval_entry flags
502+
* FUSE_EXPIRE_ONLY
503+
*/
504+
#define FUSE_EXPIRE_ONLY (1 << 0)
505+
494506
enum fuse_opcode {
495507
FUSE_LOOKUP = 1,
496508
FUSE_FORGET = 2, /* no reply */
@@ -919,7 +931,7 @@ struct fuse_notify_inval_inode_out {
919931
struct fuse_notify_inval_entry_out {
920932
uint64_t parent;
921933
uint32_t namelen;
922-
uint32_t padding;
934+
uint32_t flags;
923935
};
924936

925937
struct fuse_notify_delete_out {

0 commit comments

Comments
 (0)