Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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()

+82 -40
+2
Documentation/admin-guide/sysctl/vm.rst
··· 231 231 inode is old enough to be eligible for writeback by the kernel flusher threads. 232 232 And, it is also used as the interval to wakeup dirtytime_writeback thread. 233 233 234 + Setting this to zero disables periodic dirtytime writeback. 235 + 234 236 235 237 dirty_writeback_centisecs 236 238 =========================
+2
fs/9p/vfs_dir.c
··· 242 242 .iterate_shared = v9fs_dir_readdir, 243 243 .open = v9fs_file_open, 244 244 .release = v9fs_dir_release, 245 + .setlease = simple_nosetlease, 245 246 }; 246 247 247 248 const struct file_operations v9fs_dir_operations_dotl = { ··· 252 251 .open = v9fs_file_open, 253 252 .release = v9fs_dir_release, 254 253 .fsync = v9fs_file_fsync_dotl, 254 + .setlease = simple_nosetlease, 255 255 };
+2
fs/ceph/dir.c
··· 2214 2214 .fsync = ceph_fsync, 2215 2215 .lock = ceph_lock, 2216 2216 .flock = ceph_flock, 2217 + .setlease = simple_nosetlease, 2217 2218 }; 2218 2219 2219 2220 const struct file_operations ceph_snapdir_fops = { ··· 2222 2221 .llseek = ceph_dir_llseek, 2223 2222 .open = ceph_open, 2224 2223 .release = ceph_release, 2224 + .setlease = simple_nosetlease, 2225 2225 }; 2226 2226 2227 2227 const struct inode_operations ceph_dir_iops = {
+10
fs/dcache.c
··· 1104 1104 return de; 1105 1105 } 1106 1106 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 + */ 1107 1117 void d_dispose_if_unused(struct dentry *dentry, struct list_head *dispose) 1108 1118 { 1109 1119 spin_lock(&dentry->d_lock);
+12 -4
fs/fs-writeback.c
··· 2492 2492 wb_wakeup(wb); 2493 2493 } 2494 2494 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)); 2496 2498 } 2497 2499 2498 2500 static int dirtytime_interval_handler(const struct ctl_table *table, int write, ··· 2503 2501 int ret; 2504 2502 2505 2503 ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); 2506 - if (ret == 0 && write) 2507 - mod_delayed_work(system_percpu_wq, &dirtytime_work, 0); 2504 + if (ret == 0 && write) { 2505 + if (dirtytime_expire_interval) 2506 + mod_delayed_work(system_percpu_wq, &dirtytime_work, 0); 2507 + else 2508 + cancel_delayed_work_sync(&dirtytime_work); 2509 + } 2508 2510 return ret; 2509 2511 } 2510 2512 ··· 2525 2519 2526 2520 static int __init start_dirtytime_writeback(void) 2527 2521 { 2528 - schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ); 2522 + if (dirtytime_expire_interval) 2523 + schedule_delayed_work(&dirtytime_work, 2524 + round_jiffies_relative(dirtytime_expire_interval * HZ)); 2529 2525 register_sysctl_init("vm", vm_fs_writeback_table); 2530 2526 return 0; 2531 2527 }
+37 -29
fs/fuse/dir.c
··· 32 32 spinlock_t lock; 33 33 }; 34 34 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]; 38 38 struct delayed_work dentry_tree_work; 39 39 40 40 /* Minimum invalidation work queue frequency */ ··· 83 83 84 84 static inline struct dentry_bucket *get_dentry_bucket(struct dentry *dentry) 85 85 { 86 - int i = hash_ptr(dentry, HASH_BITS); 86 + int i = hash_ptr(dentry, FUSE_HASH_BITS); 87 87 88 88 return &dentry_hash[i]; 89 89 } ··· 164 164 struct rb_node *node; 165 165 int i; 166 166 167 - for (i = 0; i < HASH_SIZE; i++) { 167 + for (i = 0; i < FUSE_HASH_SIZE; i++) { 168 168 spin_lock(&dentry_hash[i].lock); 169 169 node = rb_first(&dentry_hash[i].tree); 170 170 while (node) { 171 171 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()) { 175 183 spin_unlock(&dentry_hash[i].lock); 176 - d_dispose_if_unused(fd->dentry, &dispose); 177 184 cond_resched(); 178 185 spin_lock(&dentry_hash[i].lock); 179 - } else 180 - break; 186 + } 181 187 node = rb_first(&dentry_hash[i].tree); 182 188 } 183 189 spin_unlock(&dentry_hash[i].lock); 184 - shrink_dentry_list(&dispose); 185 190 } 191 + shrink_dentry_list(&dispose); 186 192 187 193 if (inval_wq) 188 194 schedule_delayed_work(&dentry_tree_work, ··· 219 213 { 220 214 int i; 221 215 222 - for (i = 0; i < HASH_SIZE; i++) { 216 + for (i = 0; i < FUSE_HASH_SIZE; i++) { 223 217 spin_lock_init(&dentry_hash[i].lock); 224 218 dentry_hash[i].tree = RB_ROOT; 225 219 } ··· 233 227 inval_wq = 0; 234 228 cancel_delayed_work_sync(&dentry_tree_work); 235 229 236 - for (i = 0; i < HASH_SIZE; i++) 230 + for (i = 0; i < FUSE_HASH_SIZE; i++) 237 231 WARN_ON_ONCE(!RB_EMPTY_ROOT(&dentry_hash[i].tree)); 238 232 } 239 233 ··· 485 479 return 0; 486 480 } 487 481 488 - static void fuse_dentry_prune(struct dentry *dentry) 482 + static void fuse_dentry_release(struct dentry *dentry) 489 483 { 490 484 struct fuse_dentry *fd = dentry->d_fsdata; 491 485 492 486 if (!RB_EMPTY_NODE(&fd->node)) 493 487 fuse_dentry_tree_del_node(dentry); 494 - } 495 - 496 - static void fuse_dentry_release(struct dentry *dentry) 497 - { 498 - struct fuse_dentry *fd = dentry->d_fsdata; 499 - 500 488 kfree_rcu(fd, rcu); 501 489 } 502 490 ··· 527 527 .d_revalidate = fuse_dentry_revalidate, 528 528 .d_delete = fuse_dentry_delete, 529 529 .d_init = fuse_dentry_init, 530 - .d_prune = fuse_dentry_prune, 531 530 .d_release = fuse_dentry_release, 532 531 .d_automount = fuse_dentry_automount, 533 532 }; ··· 1583 1584 { 1584 1585 int err = -ENOTDIR; 1585 1586 struct inode *parent; 1586 - struct dentry *dir; 1587 - struct dentry *entry; 1587 + struct dentry *dir = NULL; 1588 + struct dentry *entry = NULL; 1588 1589 1589 1590 parent = fuse_ilookup(fc, parent_nodeid, NULL); 1590 1591 if (!parent) ··· 1597 1598 dir = d_find_alias(parent); 1598 1599 if (!dir) 1599 1600 goto put_parent; 1600 - 1601 - entry = start_removing_noperm(dir, name); 1602 - dput(dir); 1603 - if (IS_ERR(entry)) 1604 - goto put_parent; 1601 + while (!entry) { 1602 + struct dentry *child = try_lookup_noperm(name, dir); 1603 + if (!child || IS_ERR(child)) 1604 + goto put_parent; 1605 + entry = start_removing_dentry(dir, child); 1606 + dput(child); 1607 + if (IS_ERR(entry)) 1608 + goto put_parent; 1609 + if (!d_same_name(entry, dir, name)) { 1610 + end_removing(entry); 1611 + entry = NULL; 1612 + } 1613 + } 1605 1614 1606 1615 fuse_dir_changed(parent); 1607 1616 if (!(flags & FUSE_EXPIRE_ONLY)) ··· 1647 1640 1648 1641 end_removing(entry); 1649 1642 put_parent: 1643 + dput(dir); 1650 1644 iput(parent); 1651 1645 return err; 1652 1646 }
+1
fs/gfs2/file.c
··· 1608 1608 .lock = gfs2_lock, 1609 1609 .flock = gfs2_flock, 1610 1610 .llseek = default_llseek, 1611 + .setlease = simple_nosetlease, 1611 1612 .fop_flags = FOP_ASYNC_LOCK, 1612 1613 }; 1613 1614
+1
fs/iomap/buffered-io.c
··· 851 851 } 852 852 853 853 folio_get(folio); 854 + folio_wait_stable(folio); 854 855 return folio; 855 856 } 856 857
+1
fs/nfs/dir.c
··· 66 66 .open = nfs_opendir, 67 67 .release = nfs_closedir, 68 68 .fsync = nfs_fsync_dir, 69 + .setlease = simple_nosetlease, 69 70 }; 70 71 71 72 const struct address_space_operations nfs_dir_aops = {
-2
fs/nfs/nfs4file.c
··· 431 431 static int nfs4_setlease(struct file *file, int arg, struct file_lease **lease, 432 432 void **priv) 433 433 { 434 - if (!S_ISREG(file_inode(file)->i_mode)) 435 - return -EINVAL; 436 434 return nfs4_proc_setlease(file, arg, lease, priv); 437 435 } 438 436
+3
fs/readdir.c
··· 316 316 struct getdents_callback buf = { 317 317 .ctx.actor = filldir, 318 318 .ctx.count = count, 319 + .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR, 319 320 .current_dir = dirent 320 321 }; 321 322 int error; ··· 401 400 struct getdents_callback64 buf = { 402 401 .ctx.actor = filldir64, 403 402 .ctx.count = count, 403 + .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR, 404 404 .current_dir = dirent 405 405 }; 406 406 int error; ··· 571 569 struct compat_getdents_callback buf = { 572 570 .ctx.actor = compat_filldir, 573 571 .ctx.count = count, 572 + .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR, 574 573 .current_dir = dirent, 575 574 }; 576 575 int error;
+4 -1
fs/romfs/super.c
··· 458 458 459 459 #ifdef CONFIG_BLOCK 460 460 if (!sb->s_mtd) { 461 - sb_set_blocksize(sb, ROMBSIZE); 461 + if (!sb_set_blocksize(sb, ROMBSIZE)) { 462 + errorf(fc, "romfs: unable to set blocksize\n"); 463 + return -EINVAL; 464 + } 462 465 } else { 463 466 sb->s_blocksize = ROMBSIZE; 464 467 sb->s_blocksize_bits = blksize_bits(ROMBSIZE);
+1 -3
fs/smb/client/cifsfs.c
··· 1149 1149 struct inode *inode = file_inode(file); 1150 1150 struct cifsFileInfo *cfile = file->private_data; 1151 1151 1152 - if (!S_ISREG(inode->i_mode)) 1153 - return -EINVAL; 1154 - 1155 1152 /* Check if file is oplocked if this is request for new lease */ 1156 1153 if (arg == F_UNLCK || 1157 1154 ((arg == F_RDLCK) && CIFS_CACHE_READ(CIFS_I(inode))) || ··· 1709 1712 .remap_file_range = cifs_remap_file_range, 1710 1713 .llseek = generic_file_llseek, 1711 1714 .fsync = cifs_dir_fsync, 1715 + .setlease = simple_nosetlease, 1712 1716 }; 1713 1717 1714 1718 static void
+1
fs/vboxsf/dir.c
··· 186 186 .release = vboxsf_dir_release, 187 187 .read = generic_read_dir, 188 188 .llseek = generic_file_llseek, 189 + .setlease = simple_nosetlease, 189 190 }; 190 191 191 192 /*
+5 -1
include/linux/fs.h
··· 1855 1855 * INT_MAX unlimited 1856 1856 */ 1857 1857 int count; 1858 + /* @actor supports these flags in d_type high bits */ 1859 + unsigned int dt_flags_mask; 1858 1860 }; 1859 1861 1860 1862 /* If OR-ed with d_type, pending signals are not checked */ ··· 3526 3524 const char *name, int namelen, 3527 3525 u64 ino, unsigned type) 3528 3526 { 3529 - return ctx->actor(ctx, name, namelen, ctx->pos, ino, type); 3527 + unsigned int dt_mask = S_DT_MASK | ctx->dt_flags_mask; 3528 + 3529 + return ctx->actor(ctx, name, namelen, ctx->pos, ino, type & dt_mask); 3530 3530 } 3531 3531 static inline bool dir_emit_dot(struct file *file, struct dir_context *ctx) 3532 3532 {