Skip to content

Commit 24835a9

Browse files
committed
Merge patch series "filelock: fix conflict detection with userland file delegations"
Jeff Layton <jlayton@kernel.org> says: This patchset fixes the way that conflicts are detected when userland requests file delegations. The problem is due to a hack that was added long ago which worked up until userland could request a file delegation. This fixes the bug and makes things a bit less hacky. Please consider for v6.19. * patches from https://patch.msgid.link/20251204-dir-deleg-ro-v2-0-22d37f92ce2c@kernel.org: filelock: allow lease_managers to dictate what qualifies as a conflict filelock: add lease_dispose_list() helper Link: https://patch.msgid.link/20251204-dir-deleg-ro-v2-0-22d37f92ce2c@kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>
2 parents ed61378 + 12965a1 commit 24835a9

File tree

5 files changed

+103
-60
lines changed

5 files changed

+103
-60
lines changed

Documentation/filesystems/locking.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ lm_change yes no no
416416
lm_breaker_owns_lease: yes no no
417417
lm_lock_expirable yes no no
418418
lm_expire_lock no no yes
419+
lm_open_conflict yes no no
419420
====================== ============= ================= =========
420421

421422
buffer_head

fs/locks.c

Lines changed: 61 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -369,10 +369,19 @@ locks_dispose_list(struct list_head *dispose)
369369
while (!list_empty(dispose)) {
370370
flc = list_first_entry(dispose, struct file_lock_core, flc_list);
371371
list_del_init(&flc->flc_list);
372-
if (flc->flc_flags & (FL_LEASE|FL_DELEG|FL_LAYOUT))
373-
locks_free_lease(file_lease(flc));
374-
else
375-
locks_free_lock(file_lock(flc));
372+
locks_free_lock(file_lock(flc));
373+
}
374+
}
375+
376+
static void
377+
lease_dispose_list(struct list_head *dispose)
378+
{
379+
struct file_lock_core *flc;
380+
381+
while (!list_empty(dispose)) {
382+
flc = list_first_entry(dispose, struct file_lock_core, flc_list);
383+
list_del_init(&flc->flc_list);
384+
locks_free_lease(file_lease(flc));
376385
}
377386
}
378387

@@ -576,10 +585,50 @@ lease_setup(struct file_lease *fl, void **priv)
576585
__f_setown(filp, task_pid(current), PIDTYPE_TGID, 0);
577586
}
578587

588+
/**
589+
* lease_open_conflict - see if the given file points to an inode that has
590+
* an existing open that would conflict with the
591+
* desired lease.
592+
* @filp: file to check
593+
* @arg: type of lease that we're trying to acquire
594+
*
595+
* Check to see if there's an existing open fd on this file that would
596+
* conflict with the lease we're trying to set.
597+
*/
598+
static int
599+
lease_open_conflict(struct file *filp, const int arg)
600+
{
601+
struct inode *inode = file_inode(filp);
602+
int self_wcount = 0, self_rcount = 0;
603+
604+
if (arg == F_RDLCK)
605+
return inode_is_open_for_write(inode) ? -EAGAIN : 0;
606+
else if (arg != F_WRLCK)
607+
return 0;
608+
609+
/*
610+
* Make sure that only read/write count is from lease requestor.
611+
* Note that this will result in denying write leases when i_writecount
612+
* is negative, which is what we want. (We shouldn't grant write leases
613+
* on files open for execution.)
614+
*/
615+
if (filp->f_mode & FMODE_WRITE)
616+
self_wcount = 1;
617+
else if (filp->f_mode & FMODE_READ)
618+
self_rcount = 1;
619+
620+
if (atomic_read(&inode->i_writecount) != self_wcount ||
621+
atomic_read(&inode->i_readcount) != self_rcount)
622+
return -EAGAIN;
623+
624+
return 0;
625+
}
626+
579627
static const struct lease_manager_operations lease_manager_ops = {
580628
.lm_break = lease_break_callback,
581629
.lm_change = lease_modify,
582630
.lm_setup = lease_setup,
631+
.lm_open_conflict = lease_open_conflict,
583632
};
584633

585634
/*
@@ -1620,7 +1669,7 @@ int __break_lease(struct inode *inode, unsigned int flags)
16201669
spin_unlock(&ctx->flc_lock);
16211670
percpu_up_read(&file_rwsem);
16221671

1623-
locks_dispose_list(&dispose);
1672+
lease_dispose_list(&dispose);
16241673
error = wait_event_interruptible_timeout(new_fl->c.flc_wait,
16251674
list_empty(&new_fl->c.flc_blocked_member),
16261675
break_time);
@@ -1643,7 +1692,7 @@ int __break_lease(struct inode *inode, unsigned int flags)
16431692
out:
16441693
spin_unlock(&ctx->flc_lock);
16451694
percpu_up_read(&file_rwsem);
1646-
locks_dispose_list(&dispose);
1695+
lease_dispose_list(&dispose);
16471696
free_lock:
16481697
locks_free_lease(new_fl);
16491698
return error;
@@ -1727,7 +1776,7 @@ static int __fcntl_getlease(struct file *filp, unsigned int flavor)
17271776
spin_unlock(&ctx->flc_lock);
17281777
percpu_up_read(&file_rwsem);
17291778

1730-
locks_dispose_list(&dispose);
1779+
lease_dispose_list(&dispose);
17311780
}
17321781
return type;
17331782
}
@@ -1745,52 +1794,6 @@ int fcntl_getdeleg(struct file *filp, struct delegation *deleg)
17451794
return 0;
17461795
}
17471796

1748-
/**
1749-
* check_conflicting_open - see if the given file points to an inode that has
1750-
* an existing open that would conflict with the
1751-
* desired lease.
1752-
* @filp: file to check
1753-
* @arg: type of lease that we're trying to acquire
1754-
* @flags: current lock flags
1755-
*
1756-
* Check to see if there's an existing open fd on this file that would
1757-
* conflict with the lease we're trying to set.
1758-
*/
1759-
static int
1760-
check_conflicting_open(struct file *filp, const int arg, int flags)
1761-
{
1762-
struct inode *inode = file_inode(filp);
1763-
int self_wcount = 0, self_rcount = 0;
1764-
1765-
if (flags & FL_LAYOUT)
1766-
return 0;
1767-
if (flags & FL_DELEG)
1768-
/* We leave these checks to the caller */
1769-
return 0;
1770-
1771-
if (arg == F_RDLCK)
1772-
return inode_is_open_for_write(inode) ? -EAGAIN : 0;
1773-
else if (arg != F_WRLCK)
1774-
return 0;
1775-
1776-
/*
1777-
* Make sure that only read/write count is from lease requestor.
1778-
* Note that this will result in denying write leases when i_writecount
1779-
* is negative, which is what we want. (We shouldn't grant write leases
1780-
* on files open for execution.)
1781-
*/
1782-
if (filp->f_mode & FMODE_WRITE)
1783-
self_wcount = 1;
1784-
else if (filp->f_mode & FMODE_READ)
1785-
self_rcount = 1;
1786-
1787-
if (atomic_read(&inode->i_writecount) != self_wcount ||
1788-
atomic_read(&inode->i_readcount) != self_rcount)
1789-
return -EAGAIN;
1790-
1791-
return 0;
1792-
}
1793-
17941797
static int
17951798
generic_add_lease(struct file *filp, int arg, struct file_lease **flp, void **priv)
17961799
{
@@ -1827,7 +1830,7 @@ generic_add_lease(struct file *filp, int arg, struct file_lease **flp, void **pr
18271830
percpu_down_read(&file_rwsem);
18281831
spin_lock(&ctx->flc_lock);
18291832
time_out_leases(inode, &dispose);
1830-
error = check_conflicting_open(filp, arg, lease->c.flc_flags);
1833+
error = lease->fl_lmops->lm_open_conflict(filp, arg);
18311834
if (error)
18321835
goto out;
18331836

@@ -1884,7 +1887,7 @@ generic_add_lease(struct file *filp, int arg, struct file_lease **flp, void **pr
18841887
* precedes these checks.
18851888
*/
18861889
smp_mb();
1887-
error = check_conflicting_open(filp, arg, lease->c.flc_flags);
1890+
error = lease->fl_lmops->lm_open_conflict(filp, arg);
18881891
if (error) {
18891892
locks_unlink_lock_ctx(&lease->c);
18901893
goto out;
@@ -1896,7 +1899,7 @@ generic_add_lease(struct file *filp, int arg, struct file_lease **flp, void **pr
18961899
out:
18971900
spin_unlock(&ctx->flc_lock);
18981901
percpu_up_read(&file_rwsem);
1899-
locks_dispose_list(&dispose);
1902+
lease_dispose_list(&dispose);
19001903
if (is_deleg)
19011904
inode_unlock(inode);
19021905
if (!error && !my_fl)
@@ -1932,7 +1935,7 @@ static int generic_delete_lease(struct file *filp, void *owner)
19321935
error = fl->fl_lmops->lm_change(victim, F_UNLCK, &dispose);
19331936
spin_unlock(&ctx->flc_lock);
19341937
percpu_up_read(&file_rwsem);
1935-
locks_dispose_list(&dispose);
1938+
lease_dispose_list(&dispose);
19361939
return error;
19371940
}
19381941

@@ -2727,7 +2730,7 @@ locks_remove_lease(struct file *filp, struct file_lock_context *ctx)
27272730
spin_unlock(&ctx->flc_lock);
27282731
percpu_up_read(&file_rwsem);
27292732

2730-
locks_dispose_list(&dispose);
2733+
lease_dispose_list(&dispose);
27312734
}
27322735

27332736
/*

fs/nfsd/nfs4layouts.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -764,9 +764,28 @@ nfsd4_layout_lm_change(struct file_lease *onlist, int arg,
764764
return lease_modify(onlist, arg, dispose);
765765
}
766766

767+
/**
768+
* nfsd4_layout_lm_open_conflict - see if the given file points to an inode that has
769+
* an existing open that would conflict with the
770+
* desired lease.
771+
* @filp: file to check
772+
* @arg: type of lease that we're trying to acquire
773+
*
774+
* The kernel will call into this operation to determine whether there
775+
* are conflicting opens that may prevent the layout from being granted.
776+
* For nfsd, that check is done at a higher level, so this trivially
777+
* returns 0.
778+
*/
779+
static int
780+
nfsd4_layout_lm_open_conflict(struct file *filp, int arg)
781+
{
782+
return 0;
783+
}
784+
767785
static const struct lease_manager_operations nfsd4_layouts_lm_ops = {
768-
.lm_break = nfsd4_layout_lm_break,
769-
.lm_change = nfsd4_layout_lm_change,
786+
.lm_break = nfsd4_layout_lm_break,
787+
.lm_change = nfsd4_layout_lm_change,
788+
.lm_open_conflict = nfsd4_layout_lm_open_conflict,
770789
};
771790

772791
int

fs/nfsd/nfs4state.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5552,10 +5552,29 @@ nfsd_change_deleg_cb(struct file_lease *onlist, int arg,
55525552
return -EAGAIN;
55535553
}
55545554

5555+
/**
5556+
* nfsd4_deleg_lm_open_conflict - see if the given file points to an inode that has
5557+
* an existing open that would conflict with the
5558+
* desired lease.
5559+
* @filp: file to check
5560+
* @arg: type of lease that we're trying to acquire
5561+
*
5562+
* The kernel will call into this operation to determine whether there
5563+
* are conflicting opens that may prevent the deleg from being granted.
5564+
* For nfsd, that check is done at a higher level, so this trivially
5565+
* returns 0.
5566+
*/
5567+
static int
5568+
nfsd4_deleg_lm_open_conflict(struct file *filp, int arg)
5569+
{
5570+
return 0;
5571+
}
5572+
55555573
static const struct lease_manager_operations nfsd_lease_mng_ops = {
55565574
.lm_breaker_owns_lease = nfsd_breaker_owns_lease,
55575575
.lm_break = nfsd_break_deleg_cb,
55585576
.lm_change = nfsd_change_deleg_cb,
5577+
.lm_open_conflict = nfsd4_deleg_lm_open_conflict,
55595578
};
55605579

55615580
static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4_stateowner *so, u32 seqid)

include/linux/filelock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ struct lease_manager_operations {
4949
int (*lm_change)(struct file_lease *, int, struct list_head *);
5050
void (*lm_setup)(struct file_lease *, void **);
5151
bool (*lm_breaker_owns_lease)(struct file_lease *);
52+
int (*lm_open_conflict)(struct file *, int);
5253
};
5354

5455
struct lock_manager {

0 commit comments

Comments
 (0)