From: Chuck Lever Add NFSD_CMD_UNLOCK_FILESYSTEM as a dedicated netlink command for revoking NFS state under a filesystem path, providing a netlink equivalent of /proc/fs/nfsd/unlock_fs. The command requires a "path" string attribute containing the filesystem path whose state should be released. The handler resolves the path to its superblock, then cancels async copies, releases NLM locks, and revokes NFSv4 state on that superblock. Signed-off-by: Chuck Lever --- Documentation/netlink/specs/nfsd.yaml | 16 ++++++++++++++ fs/nfsd/netlink.c | 12 +++++++++++ fs/nfsd/netlink.h | 1 + fs/nfsd/nfsctl.c | 40 +++++++++++++++++++++++++++++++++++ include/uapi/linux/nfsd_netlink.h | 8 +++++++ 5 files changed, 77 insertions(+) diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml index 9918b9a84d88..e83209a4a19d 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -141,6 +141,13 @@ attribute-sets: doc: struct sockaddr_in or struct sockaddr_in6. checks: min-len: 16 + - + name: unlock-filesystem + attributes: + - + name: path + type: string + doc: Filesystem path whose state should be released. operations: list: @@ -251,3 +258,12 @@ operations: request: attributes: - address + - + name: unlock-filesystem + doc: revoke NFS state under a filesystem path + attribute-set: unlock-filesystem + flags: [admin-perm] + do: + request: + attributes: + - path diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index 6b7221ce6869..18afebaf4fef 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -53,6 +53,11 @@ static const struct nla_policy nfsd_unlock_ip_nl_policy[NFSD_A_UNLOCK_IP_ADDRESS [NFSD_A_UNLOCK_IP_ADDRESS] = NLA_POLICY_MIN_LEN(16), }; +/* NFSD_CMD_UNLOCK_FILESYSTEM - do */ +static const struct nla_policy nfsd_unlock_filesystem_nl_policy[NFSD_A_UNLOCK_FILESYSTEM_PATH + 1] = { + [NFSD_A_UNLOCK_FILESYSTEM_PATH] = { .type = NLA_NUL_STRING, }, +}; + /* Ops table for nfsd */ static const struct genl_split_ops nfsd_nl_ops[] = { { @@ -115,6 +120,13 @@ static const struct genl_split_ops nfsd_nl_ops[] = { .maxattr = NFSD_A_UNLOCK_IP_ADDRESS, .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, }, + { + .cmd = NFSD_CMD_UNLOCK_FILESYSTEM, + .doit = nfsd_nl_unlock_filesystem_doit, + .policy = nfsd_unlock_filesystem_nl_policy, + .maxattr = NFSD_A_UNLOCK_FILESYSTEM_PATH, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, }; struct genl_family nfsd_nl_family __ro_after_init = { diff --git a/fs/nfsd/netlink.h b/fs/nfsd/netlink.h index 3c2d5996612f..9535782b529a 100644 --- a/fs/nfsd/netlink.h +++ b/fs/nfsd/netlink.h @@ -27,6 +27,7 @@ int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_pool_mode_set_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_pool_mode_get_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_unlock_ip_doit(struct sk_buff *skb, struct genl_info *info); +int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb, struct genl_info *info); extern struct genl_family nfsd_nl_family; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index e1e89d52e6de..ed0d12f77405 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2238,6 +2238,46 @@ int nfsd_nl_unlock_ip_doit(struct sk_buff *skb, struct genl_info *info) return nlmsvc_unlock_all_by_ip(sap); } +/** + * nfsd_nl_unlock_filesystem_doit - revoke NFS state under a filesystem path + * @skb: reply buffer + * @info: netlink metadata and command arguments + * + * Return: 0 on success or a negative errno. + */ +int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb, + struct genl_info *info) +{ + struct net *net = genl_info_net(info); + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct path path; + int error; + + if (GENL_REQ_ATTR_CHECK(info, NFSD_A_UNLOCK_FILESYSTEM_PATH)) + return -EINVAL; + + trace_nfsd_ctl_unlock_fs(net, + nla_data(info->attrs[NFSD_A_UNLOCK_FILESYSTEM_PATH])); + error = kern_path( + nla_data(info->attrs[NFSD_A_UNLOCK_FILESYSTEM_PATH]), + 0, &path); + if (error) + return error; + + nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb); + error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); + + mutex_lock(&nfsd_mutex); + if (nn->nfsd_serv) + nfsd4_revoke_states(nn, path.dentry->d_sb); + else + error = -EINVAL; + mutex_unlock(&nfsd_mutex); + + path_put(&path); + return error; +} + /** * nfsd_net_init - Prepare the nfsd_net portion of a new net namespace * @net: a freshly-created network namespace diff --git a/include/uapi/linux/nfsd_netlink.h b/include/uapi/linux/nfsd_netlink.h index 4153e9c69fbf..51f926c0b15b 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -88,6 +88,13 @@ enum { NFSD_A_UNLOCK_IP_MAX = (__NFSD_A_UNLOCK_IP_MAX - 1) }; +enum { + NFSD_A_UNLOCK_FILESYSTEM_PATH = 1, + + __NFSD_A_UNLOCK_FILESYSTEM_MAX, + NFSD_A_UNLOCK_FILESYSTEM_MAX = (__NFSD_A_UNLOCK_FILESYSTEM_MAX - 1) +}; + enum { NFSD_CMD_RPC_STATUS_GET = 1, NFSD_CMD_THREADS_SET, @@ -99,6 +106,7 @@ enum { NFSD_CMD_POOL_MODE_SET, NFSD_CMD_POOL_MODE_GET, NFSD_CMD_UNLOCK_IP, + NFSD_CMD_UNLOCK_FILESYSTEM, __NFSD_CMD_MAX, NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1) -- 2.53.0