Skip to content

Commit fcb70a5

Browse files
committed
Merge tag 'vfs-6.19-rc8.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull vfs fixes from Christian Brauner: - Fix the the buggy conversion of fuse_reverse_inval_entry() introduced during the creation rework - Disallow nfs delegation requests for directories by setting simple_nosetlease() - Require an opt-in for getting readdir flag bits outside of S_DT_MASK set in d_type - Fix scheduling delayed writeback work by only scheduling when the dirty time expiry interval is non-zero and cancel the delayed work if the interval is set to zero - Use rounded_jiffies_interval for dirty time work - Check the return value of sb_set_blocksize() for romfs - Wait for batched folios to be stable in __iomap_get_folio() - Use private naming for fuse hash size - Fix the stale dentry cleanup to prevent a race that causes a UAF * tag 'vfs-6.19-rc8.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: vfs: document d_dispose_if_unused() fuse: shrink once after all buckets have been scanned fuse: clean up fuse_dentry_tree_work() fuse: add need_resched() before unlocking bucket fuse: make sure dentry is evicted if stale fuse: fix race when disposing stale dentries fuse: use private naming for fuse hash size writeback: use round_jiffies_relative for dirtytime_work iomap: wait for batched folios to be stable in __iomap_get_folio romfs: check sb_set_blocksize() return value docs: clarify that dirtytime_expire_seconds=0 disables writeback writeback: fix 100% CPU usage when dirtytime_expire_interval is 0 readdir: require opt-in for d_type flags vboxsf: don't allow delegations to be set on directories ceph: don't allow delegations to be set on directories gfs2: don't allow delegations to be set on directories 9p: don't allow delegations to be set on directories smb/client: properly disallow delegations on directories nfs: properly disallow delegation requests on directories fuse: fix conversion of fuse_reverse_inval_entry() to start_removing()
2 parents 63804fe + 6358461 commit fcb70a5

15 files changed

Lines changed: 82 additions & 40 deletions

File tree

Documentation/admin-guide/sysctl/vm.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ eventually gets pushed out to disk. This tunable is used to define when dirty
231231
inode is old enough to be eligible for writeback by the kernel flusher threads.
232232
And, it is also used as the interval to wakeup dirtytime_writeback thread.
233233

234+
Setting this to zero disables periodic dirtytime writeback.
235+
234236

235237
dirty_writeback_centisecs
236238
=========================

fs/9p/vfs_dir.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ const struct file_operations v9fs_dir_operations = {
242242
.iterate_shared = v9fs_dir_readdir,
243243
.open = v9fs_file_open,
244244
.release = v9fs_dir_release,
245+
.setlease = simple_nosetlease,
245246
};
246247

247248
const struct file_operations v9fs_dir_operations_dotl = {
@@ -251,4 +252,5 @@ const struct file_operations v9fs_dir_operations_dotl = {
251252
.open = v9fs_file_open,
252253
.release = v9fs_dir_release,
253254
.fsync = v9fs_file_fsync_dotl,
255+
.setlease = simple_nosetlease,
254256
};

fs/ceph/dir.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,13 +2214,15 @@ const struct file_operations ceph_dir_fops = {
22142214
.fsync = ceph_fsync,
22152215
.lock = ceph_lock,
22162216
.flock = ceph_flock,
2217+
.setlease = simple_nosetlease,
22172218
};
22182219

22192220
const struct file_operations ceph_snapdir_fops = {
22202221
.iterate_shared = shared_ceph_readdir,
22212222
.llseek = ceph_dir_llseek,
22222223
.open = ceph_open,
22232224
.release = ceph_release,
2225+
.setlease = simple_nosetlease,
22242226
};
22252227

22262228
const struct inode_operations ceph_dir_iops = {

fs/dcache.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,16 @@ struct dentry *d_find_alias_rcu(struct inode *inode)
11041104
return de;
11051105
}
11061106

1107+
/**
1108+
* d_dispose_if_unused - move unreferenced dentries to shrink list
1109+
* @dentry: dentry in question
1110+
* @dispose: head of shrink list
1111+
*
1112+
* If dentry has no external references, move it to shrink list.
1113+
*
1114+
* NOTE!!! The caller is responsible for preventing eviction of the dentry by
1115+
* holding dentry->d_inode->i_lock or equivalent.
1116+
*/
11071117
void d_dispose_if_unused(struct dentry *dentry, struct list_head *dispose)
11081118
{
11091119
spin_lock(&dentry->d_lock);

fs/fs-writeback.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2492,7 +2492,9 @@ static void wakeup_dirtytime_writeback(struct work_struct *w)
24922492
wb_wakeup(wb);
24932493
}
24942494
rcu_read_unlock();
2495-
schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
2495+
if (dirtytime_expire_interval)
2496+
schedule_delayed_work(&dirtytime_work,
2497+
round_jiffies_relative(dirtytime_expire_interval * HZ));
24962498
}
24972499

24982500
static int dirtytime_interval_handler(const struct ctl_table *table, int write,
@@ -2501,8 +2503,12 @@ static int dirtytime_interval_handler(const struct ctl_table *table, int write,
25012503
int ret;
25022504

25032505
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
2504-
if (ret == 0 && write)
2505-
mod_delayed_work(system_percpu_wq, &dirtytime_work, 0);
2506+
if (ret == 0 && write) {
2507+
if (dirtytime_expire_interval)
2508+
mod_delayed_work(system_percpu_wq, &dirtytime_work, 0);
2509+
else
2510+
cancel_delayed_work_sync(&dirtytime_work);
2511+
}
25062512
return ret;
25072513
}
25082514

@@ -2519,7 +2525,9 @@ static const struct ctl_table vm_fs_writeback_table[] = {
25192525

25202526
static int __init start_dirtytime_writeback(void)
25212527
{
2522-
schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
2528+
if (dirtytime_expire_interval)
2529+
schedule_delayed_work(&dirtytime_work,
2530+
round_jiffies_relative(dirtytime_expire_interval * HZ));
25232531
register_sysctl_init("vm", vm_fs_writeback_table);
25242532
return 0;
25252533
}

fs/fuse/dir.c

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ struct dentry_bucket {
3232
spinlock_t lock;
3333
};
3434

35-
#define HASH_BITS 5
36-
#define HASH_SIZE (1 << HASH_BITS)
37-
static struct dentry_bucket dentry_hash[HASH_SIZE];
35+
#define FUSE_HASH_BITS 5
36+
#define FUSE_HASH_SIZE (1 << FUSE_HASH_BITS)
37+
static struct dentry_bucket dentry_hash[FUSE_HASH_SIZE];
3838
struct delayed_work dentry_tree_work;
3939

4040
/* Minimum invalidation work queue frequency */
@@ -83,7 +83,7 @@ MODULE_PARM_DESC(inval_wq,
8383

8484
static inline struct dentry_bucket *get_dentry_bucket(struct dentry *dentry)
8585
{
86-
int i = hash_ptr(dentry, HASH_BITS);
86+
int i = hash_ptr(dentry, FUSE_HASH_BITS);
8787

8888
return &dentry_hash[i];
8989
}
@@ -164,25 +164,31 @@ static void fuse_dentry_tree_work(struct work_struct *work)
164164
struct rb_node *node;
165165
int i;
166166

167-
for (i = 0; i < HASH_SIZE; i++) {
167+
for (i = 0; i < FUSE_HASH_SIZE; i++) {
168168
spin_lock(&dentry_hash[i].lock);
169169
node = rb_first(&dentry_hash[i].tree);
170170
while (node) {
171171
fd = rb_entry(node, struct fuse_dentry, node);
172-
if (time_after64(get_jiffies_64(), fd->time)) {
173-
rb_erase(&fd->node, &dentry_hash[i].tree);
174-
RB_CLEAR_NODE(&fd->node);
172+
if (!time_before64(fd->time, get_jiffies_64()))
173+
break;
174+
175+
rb_erase(&fd->node, &dentry_hash[i].tree);
176+
RB_CLEAR_NODE(&fd->node);
177+
spin_lock(&fd->dentry->d_lock);
178+
/* If dentry is still referenced, let next dput release it */
179+
fd->dentry->d_flags |= DCACHE_OP_DELETE;
180+
spin_unlock(&fd->dentry->d_lock);
181+
d_dispose_if_unused(fd->dentry, &dispose);
182+
if (need_resched()) {
175183
spin_unlock(&dentry_hash[i].lock);
176-
d_dispose_if_unused(fd->dentry, &dispose);
177184
cond_resched();
178185
spin_lock(&dentry_hash[i].lock);
179-
} else
180-
break;
186+
}
181187
node = rb_first(&dentry_hash[i].tree);
182188
}
183189
spin_unlock(&dentry_hash[i].lock);
184-
shrink_dentry_list(&dispose);
185190
}
191+
shrink_dentry_list(&dispose);
186192

187193
if (inval_wq)
188194
schedule_delayed_work(&dentry_tree_work,
@@ -213,7 +219,7 @@ void fuse_dentry_tree_init(void)
213219
{
214220
int i;
215221

216-
for (i = 0; i < HASH_SIZE; i++) {
222+
for (i = 0; i < FUSE_HASH_SIZE; i++) {
217223
spin_lock_init(&dentry_hash[i].lock);
218224
dentry_hash[i].tree = RB_ROOT;
219225
}
@@ -227,7 +233,7 @@ void fuse_dentry_tree_cleanup(void)
227233
inval_wq = 0;
228234
cancel_delayed_work_sync(&dentry_tree_work);
229235

230-
for (i = 0; i < HASH_SIZE; i++)
236+
for (i = 0; i < FUSE_HASH_SIZE; i++)
231237
WARN_ON_ONCE(!RB_EMPTY_ROOT(&dentry_hash[i].tree));
232238
}
233239

@@ -479,18 +485,12 @@ static int fuse_dentry_init(struct dentry *dentry)
479485
return 0;
480486
}
481487

482-
static void fuse_dentry_prune(struct dentry *dentry)
488+
static void fuse_dentry_release(struct dentry *dentry)
483489
{
484490
struct fuse_dentry *fd = dentry->d_fsdata;
485491

486492
if (!RB_EMPTY_NODE(&fd->node))
487493
fuse_dentry_tree_del_node(dentry);
488-
}
489-
490-
static void fuse_dentry_release(struct dentry *dentry)
491-
{
492-
struct fuse_dentry *fd = dentry->d_fsdata;
493-
494494
kfree_rcu(fd, rcu);
495495
}
496496

@@ -527,7 +527,6 @@ const struct dentry_operations fuse_dentry_operations = {
527527
.d_revalidate = fuse_dentry_revalidate,
528528
.d_delete = fuse_dentry_delete,
529529
.d_init = fuse_dentry_init,
530-
.d_prune = fuse_dentry_prune,
531530
.d_release = fuse_dentry_release,
532531
.d_automount = fuse_dentry_automount,
533532
};
@@ -1584,8 +1583,8 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
15841583
{
15851584
int err = -ENOTDIR;
15861585
struct inode *parent;
1587-
struct dentry *dir;
1588-
struct dentry *entry;
1586+
struct dentry *dir = NULL;
1587+
struct dentry *entry = NULL;
15891588

15901589
parent = fuse_ilookup(fc, parent_nodeid, NULL);
15911590
if (!parent)
@@ -1598,11 +1597,19 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
15981597
dir = d_find_alias(parent);
15991598
if (!dir)
16001599
goto put_parent;
1601-
1602-
entry = start_removing_noperm(dir, name);
1603-
dput(dir);
1604-
if (IS_ERR(entry))
1605-
goto put_parent;
1600+
while (!entry) {
1601+
struct dentry *child = try_lookup_noperm(name, dir);
1602+
if (!child || IS_ERR(child))
1603+
goto put_parent;
1604+
entry = start_removing_dentry(dir, child);
1605+
dput(child);
1606+
if (IS_ERR(entry))
1607+
goto put_parent;
1608+
if (!d_same_name(entry, dir, name)) {
1609+
end_removing(entry);
1610+
entry = NULL;
1611+
}
1612+
}
16061613

16071614
fuse_dir_changed(parent);
16081615
if (!(flags & FUSE_EXPIRE_ONLY))
@@ -1640,6 +1647,7 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
16401647

16411648
end_removing(entry);
16421649
put_parent:
1650+
dput(dir);
16431651
iput(parent);
16441652
return err;
16451653
}

fs/gfs2/file.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,7 @@ const struct file_operations gfs2_dir_fops = {
16081608
.lock = gfs2_lock,
16091609
.flock = gfs2_flock,
16101610
.llseek = default_llseek,
1611+
.setlease = simple_nosetlease,
16111612
.fop_flags = FOP_ASYNC_LOCK,
16121613
};
16131614

fs/iomap/buffered-io.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,7 @@ static struct folio *__iomap_get_folio(struct iomap_iter *iter,
851851
}
852852

853853
folio_get(folio);
854+
folio_wait_stable(folio);
854855
return folio;
855856
}
856857

fs/nfs/dir.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const struct file_operations nfs_dir_operations = {
6666
.open = nfs_opendir,
6767
.release = nfs_closedir,
6868
.fsync = nfs_fsync_dir,
69+
.setlease = simple_nosetlease,
6970
};
7071

7172
const struct address_space_operations nfs_dir_aops = {

fs/nfs/nfs4file.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,6 @@ void nfs42_ssc_unregister_ops(void)
431431
static int nfs4_setlease(struct file *file, int arg, struct file_lease **lease,
432432
void **priv)
433433
{
434-
if (!S_ISREG(file_inode(file)->i_mode))
435-
return -EINVAL;
436434
return nfs4_proc_setlease(file, arg, lease, priv);
437435
}
438436

0 commit comments

Comments
 (0)