diff --git a/Documentation/netlink/specs/nfsd.yaml b/Documentation/netlink/specs/nfsd.yaml index bf41682464c3..e121c54033a0 100644 --- a/Documentation/netlink/specs/nfsd.yaml +++ b/Documentation/netlink/specs/nfsd.yaml @@ -310,6 +310,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: @@ -473,6 +480,15 @@ 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 mcast-groups: list: diff --git a/fs/nfsd/netlink.c b/fs/nfsd/netlink.c index 5830627c0288..63df3c4cf63a 100644 --- a/fs/nfsd/netlink.c +++ b/fs/nfsd/netlink.c @@ -108,6 +108,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[] = { { @@ -201,6 +206,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, + }, }; static const struct genl_multicast_group nfsd_nl_mcgrps[] = { diff --git a/fs/nfsd/netlink.h b/fs/nfsd/netlink.h index 88edbbc68453..29bd5468d401 100644 --- a/fs/nfsd/netlink.h +++ b/fs/nfsd/netlink.h @@ -40,6 +40,7 @@ int nfsd_nl_expkey_get_reqs_dumpit(struct sk_buff *skb, int nfsd_nl_expkey_set_reqs_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_cache_flush_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); enum { NFSD_NLGRP_NONE, diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index cf246652e478..f7f104cc457d 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2314,6 +2314,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 90ef1e686769..d01096c06d72 100644 --- a/include/uapi/linux/nfsd_netlink.h +++ b/include/uapi/linux/nfsd_netlink.h @@ -211,6 +211,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, @@ -228,6 +235,7 @@ enum { NFSD_CMD_EXPKEY_SET_REQS, NFSD_CMD_CACHE_FLUSH, NFSD_CMD_UNLOCK_IP, + NFSD_CMD_UNLOCK_FILESYSTEM, __NFSD_CMD_MAX, NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)