1a55370a3SNeilBrown /*
2a55370a3SNeilBrown * Copyright (c) 2004 The Regents of the University of Michigan.
3f3f80148SJeff Layton * Copyright (c) 2012 Jeff Layton <jlayton@redhat.com>
4a55370a3SNeilBrown * All rights reserved.
5a55370a3SNeilBrown *
6a55370a3SNeilBrown * Andy Adamson <andros@citi.umich.edu>
7a55370a3SNeilBrown *
8a55370a3SNeilBrown * Redistribution and use in source and binary forms, with or without
9a55370a3SNeilBrown * modification, are permitted provided that the following conditions
10a55370a3SNeilBrown * are met:
11a55370a3SNeilBrown *
12a55370a3SNeilBrown * 1. Redistributions of source code must retain the above copyright
13a55370a3SNeilBrown * notice, this list of conditions and the following disclaimer.
14a55370a3SNeilBrown * 2. Redistributions in binary form must reproduce the above copyright
15a55370a3SNeilBrown * notice, this list of conditions and the following disclaimer in the
16a55370a3SNeilBrown * documentation and/or other materials provided with the distribution.
17a55370a3SNeilBrown * 3. Neither the name of the University nor the names of its
18a55370a3SNeilBrown * contributors may be used to endorse or promote products derived
19a55370a3SNeilBrown * from this software without specific prior written permission.
20a55370a3SNeilBrown *
21a55370a3SNeilBrown * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
22a55370a3SNeilBrown * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23a55370a3SNeilBrown * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24a55370a3SNeilBrown * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25a55370a3SNeilBrown * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26a55370a3SNeilBrown * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27a55370a3SNeilBrown * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28a55370a3SNeilBrown * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29a55370a3SNeilBrown * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30a55370a3SNeilBrown * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31a55370a3SNeilBrown * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32a55370a3SNeilBrown *
33a55370a3SNeilBrown */
34a55370a3SNeilBrown
351edb82d2SHerbert Xu #include <crypto/hash.h>
36190e4fbfSNeilBrown #include <linux/file.h>
375a0e3ad6STejun Heo #include <linux/slab.h>
38190e4fbfSNeilBrown #include <linux/namei.h>
39e8edc6e0SAlexey Dobriyan #include <linux/sched.h>
40f3f80148SJeff Layton #include <linux/fs.h>
41813fd320SJeff Layton #include <linux/module.h>
42f3f80148SJeff Layton #include <net/net_namespace.h>
43f3f80148SJeff Layton #include <linux/sunrpc/rpc_pipe_fs.h>
44f3f80148SJeff Layton #include <linux/sunrpc/clnt.h>
45f3f80148SJeff Layton #include <linux/nfsd/cld.h>
469a74af21SBoaz Harrosh
479a74af21SBoaz Harrosh #include "nfsd.h"
489a74af21SBoaz Harrosh #include "state.h"
490a3adadeSJ. Bruce Fields #include "vfs.h"
50f3f80148SJeff Layton #include "netns.h"
51a55370a3SNeilBrown
52a55370a3SNeilBrown #define NFSDDBG_FACILITY NFSDDBG_PROC
53a55370a3SNeilBrown
542a4317c5SJeff Layton /* Declarations */
552a4317c5SJeff Layton struct nfsd4_client_tracking_ops {
562a4317c5SJeff Layton int (*init)(struct net *);
572a4317c5SJeff Layton void (*exit)(struct net *);
582a4317c5SJeff Layton void (*create)(struct nfs4_client *);
592a4317c5SJeff Layton void (*remove)(struct nfs4_client *);
602a4317c5SJeff Layton int (*check)(struct nfs4_client *);
61919b8049SJeff Layton void (*grace_done)(struct nfsd_net *);
6211a60d15SScott Mayhew uint8_t version;
6311a60d15SScott Mayhew size_t msglen;
642a4317c5SJeff Layton };
652a4317c5SJeff Layton
6611a60d15SScott Mayhew static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops;
676ee95d1cSScott Mayhew static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2;
6811a60d15SScott Mayhew
69190e4fbfSNeilBrown /* Globals */
7048483bf2SJ. Bruce Fields static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
71190e4fbfSNeilBrown
72d84f4f99SDavid Howells static int
nfs4_save_creds(const struct cred ** original_creds)73d84f4f99SDavid Howells nfs4_save_creds(const struct cred **original_creds)
74190e4fbfSNeilBrown {
75d84f4f99SDavid Howells struct cred *new;
76d84f4f99SDavid Howells
77d84f4f99SDavid Howells new = prepare_creds();
78d84f4f99SDavid Howells if (!new)
79d84f4f99SDavid Howells return -ENOMEM;
80d84f4f99SDavid Howells
816fab8779SEric W. Biederman new->fsuid = GLOBAL_ROOT_UID;
826fab8779SEric W. Biederman new->fsgid = GLOBAL_ROOT_GID;
83d84f4f99SDavid Howells *original_creds = override_creds(new);
84d84f4f99SDavid Howells put_cred(new);
85d84f4f99SDavid Howells return 0;
86190e4fbfSNeilBrown }
87190e4fbfSNeilBrown
88190e4fbfSNeilBrown static void
nfs4_reset_creds(const struct cred * original)89d84f4f99SDavid Howells nfs4_reset_creds(const struct cred *original)
90190e4fbfSNeilBrown {
91d84f4f99SDavid Howells revert_creds(original);
92190e4fbfSNeilBrown }
93190e4fbfSNeilBrown
94a55370a3SNeilBrown static void
md5_to_hex(char * out,char * md5)95a55370a3SNeilBrown md5_to_hex(char *out, char *md5)
96a55370a3SNeilBrown {
97a55370a3SNeilBrown int i;
98a55370a3SNeilBrown
99a55370a3SNeilBrown for (i=0; i<16; i++) {
100a55370a3SNeilBrown unsigned char c = md5[i];
101a55370a3SNeilBrown
102a55370a3SNeilBrown *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
103a55370a3SNeilBrown *out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
104a55370a3SNeilBrown }
105a55370a3SNeilBrown *out = '\0';
106a55370a3SNeilBrown }
107a55370a3SNeilBrown
1082216d449SJeff Layton static int
nfs4_make_rec_clidname(char * dname,const struct xdr_netobj * clname)1092216d449SJeff Layton nfs4_make_rec_clidname(char *dname, const struct xdr_netobj *clname)
110a55370a3SNeilBrown {
111a55370a3SNeilBrown struct xdr_netobj cksum;
1121edb82d2SHerbert Xu struct crypto_shash *tfm;
1132216d449SJeff Layton int status;
114a55370a3SNeilBrown
115a55370a3SNeilBrown dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
116a55370a3SNeilBrown clname->len, clname->data);
1171edb82d2SHerbert Xu tfm = crypto_alloc_shash("md5", 0, 0);
1181edb82d2SHerbert Xu if (IS_ERR(tfm)) {
1191edb82d2SHerbert Xu status = PTR_ERR(tfm);
12035058687SHerbert Xu goto out_no_tfm;
1212216d449SJeff Layton }
1222216d449SJeff Layton
1231edb82d2SHerbert Xu cksum.len = crypto_shash_digestsize(tfm);
124a55370a3SNeilBrown cksum.data = kmalloc(cksum.len, GFP_KERNEL);
1252216d449SJeff Layton if (cksum.data == NULL) {
1262216d449SJeff Layton status = -ENOMEM;
127a55370a3SNeilBrown goto out;
1282216d449SJeff Layton }
129a55370a3SNeilBrown
130ea794db2SEric Biggers status = crypto_shash_tfm_digest(tfm, clname->data, clname->len,
1311edb82d2SHerbert Xu cksum.data);
1322216d449SJeff Layton if (status)
13335058687SHerbert Xu goto out;
134a55370a3SNeilBrown
135a55370a3SNeilBrown md5_to_hex(dname, cksum.data);
136a55370a3SNeilBrown
1372216d449SJeff Layton status = 0;
138a55370a3SNeilBrown out:
1392bd9e7b6SKrishna Kumar kfree(cksum.data);
1401edb82d2SHerbert Xu crypto_free_shash(tfm);
14135058687SHerbert Xu out_no_tfm:
142a55370a3SNeilBrown return status;
143a55370a3SNeilBrown }
144190e4fbfSNeilBrown
1452216d449SJeff Layton /*
1462216d449SJeff Layton * If we had an error generating the recdir name for the legacy tracker
1472216d449SJeff Layton * then warn the admin. If the error doesn't appear to be transient,
1482216d449SJeff Layton * then disable recovery tracking.
1492216d449SJeff Layton */
1502216d449SJeff Layton static void
legacy_recdir_name_error(struct nfs4_client * clp,int error)1517255e716SJeff Layton legacy_recdir_name_error(struct nfs4_client *clp, int error)
1522216d449SJeff Layton {
1532216d449SJeff Layton printk(KERN_ERR "NFSD: unable to generate recoverydir "
1542216d449SJeff Layton "name (%d).\n", error);
1552216d449SJeff Layton
1562216d449SJeff Layton /*
1572216d449SJeff Layton * if the algorithm just doesn't exist, then disable the recovery
1582216d449SJeff Layton * tracker altogether. The crypto libs will generally return this if
1592216d449SJeff Layton * FIPS is enabled as well.
1602216d449SJeff Layton */
1612216d449SJeff Layton if (error == -ENOENT) {
1622216d449SJeff Layton printk(KERN_ERR "NFSD: disabling legacy clientid tracking. "
1632216d449SJeff Layton "Reboot recovery will not function correctly!\n");
1647255e716SJeff Layton nfsd4_client_tracking_exit(clp->net);
1652216d449SJeff Layton }
1662216d449SJeff Layton }
1672216d449SJeff Layton
1682a4317c5SJeff Layton static void
__nfsd4_create_reclaim_record_grace(struct nfs4_client * clp,const char * dname,int len,struct nfsd_net * nn)1696b189105SScott Mayhew __nfsd4_create_reclaim_record_grace(struct nfs4_client *clp,
1706b189105SScott Mayhew const char *dname, int len, struct nfsd_net *nn)
1716b189105SScott Mayhew {
1726b189105SScott Mayhew struct xdr_netobj name;
1736ee95d1cSScott Mayhew struct xdr_netobj princhash = { .len = 0, .data = NULL };
1746b189105SScott Mayhew struct nfs4_client_reclaim *crp;
1756b189105SScott Mayhew
1766b189105SScott Mayhew name.data = kmemdup(dname, len, GFP_KERNEL);
1776b189105SScott Mayhew if (!name.data) {
1786b189105SScott Mayhew dprintk("%s: failed to allocate memory for name.data!\n",
1796b189105SScott Mayhew __func__);
1806b189105SScott Mayhew return;
1816b189105SScott Mayhew }
1826b189105SScott Mayhew name.len = len;
1836ee95d1cSScott Mayhew crp = nfs4_client_to_reclaim(name, princhash, nn);
1846b189105SScott Mayhew if (!crp) {
1856b189105SScott Mayhew kfree(name.data);
1866b189105SScott Mayhew return;
1876b189105SScott Mayhew }
1886b189105SScott Mayhew crp->cr_clp = clp;
1896b189105SScott Mayhew }
1906b189105SScott Mayhew
1916b189105SScott Mayhew static void
nfsd4_create_clid_dir(struct nfs4_client * clp)1922a4317c5SJeff Layton nfsd4_create_clid_dir(struct nfs4_client *clp)
193c7b9a459SNeilBrown {
194d84f4f99SDavid Howells const struct cred *original_cred;
1952216d449SJeff Layton char dname[HEXDIR_LEN];
196e970a573SChristoph Hellwig struct dentry *dir, *dentry;
197c7b9a459SNeilBrown int status;
19852e19c09SStanislav Kinsbursky struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
199c7b9a459SNeilBrown
200a52d726bSJeff Layton if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
2017a6ef8c7SJ. Bruce Fields return;
2023a073369SStanislav Kinsbursky if (!nn->rec_file)
2037a6ef8c7SJ. Bruce Fields return;
2042216d449SJeff Layton
2052216d449SJeff Layton status = nfs4_make_rec_clidname(dname, &clp->cl_name);
2062216d449SJeff Layton if (status)
2077255e716SJeff Layton return legacy_recdir_name_error(clp, status);
2082216d449SJeff Layton
209d84f4f99SDavid Howells status = nfs4_save_creds(&original_cred);
210d84f4f99SDavid Howells if (status < 0)
2117a6ef8c7SJ. Bruce Fields return;
212c7b9a459SNeilBrown
2133a073369SStanislav Kinsbursky status = mnt_want_write_file(nn->rec_file);
2144a55c101SJan Kara if (status)
215c2236f14SKinglong Mee goto out_creds;
2164a55c101SJan Kara
2173a073369SStanislav Kinsbursky dir = nn->rec_file->f_path.dentry;
218c7b9a459SNeilBrown /* lock the parent */
2195955102cSAl Viro inode_lock(d_inode(dir));
220c7b9a459SNeilBrown
221e970a573SChristoph Hellwig dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1);
222c7b9a459SNeilBrown if (IS_ERR(dentry)) {
223c7b9a459SNeilBrown status = PTR_ERR(dentry);
224c7b9a459SNeilBrown goto out_unlock;
225c7b9a459SNeilBrown }
2262b0143b5SDavid Howells if (d_really_is_positive(dentry))
227aec39680SJ. Bruce Fields /*
228aec39680SJ. Bruce Fields * In the 4.1 case, where we're called from
229aec39680SJ. Bruce Fields * reclaim_complete(), records from the previous reboot
230aec39680SJ. Bruce Fields * may still be left, so this is OK.
231aec39680SJ. Bruce Fields *
232aec39680SJ. Bruce Fields * In the 4.0 case, we should never get here; but we may
233aec39680SJ. Bruce Fields * as well be forgiving and just succeed silently.
234aec39680SJ. Bruce Fields */
235c7b9a459SNeilBrown goto out_put;
236abf08576SChristian Brauner status = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU);
237c7b9a459SNeilBrown out_put:
238c7b9a459SNeilBrown dput(dentry);
239c7b9a459SNeilBrown out_unlock:
2405955102cSAl Viro inode_unlock(d_inode(dir));
2410ce0c2b5SJeff Layton if (status == 0) {
2426b189105SScott Mayhew if (nn->in_grace)
2436b189105SScott Mayhew __nfsd4_create_reclaim_record_grace(clp, dname,
2446b189105SScott Mayhew HEXDIR_LEN, nn);
2453a073369SStanislav Kinsbursky vfs_fsync(nn->rec_file, 0);
2460ce0c2b5SJeff Layton } else {
2476577aac0SBoaz Harrosh printk(KERN_ERR "NFSD: failed to write recovery record"
2486577aac0SBoaz Harrosh " (err %d); please check that %s exists"
2496577aac0SBoaz Harrosh " and is writeable", status,
2506577aac0SBoaz Harrosh user_recovery_dirname);
2510ce0c2b5SJeff Layton }
2523a073369SStanislav Kinsbursky mnt_drop_write_file(nn->rec_file);
253c2236f14SKinglong Mee out_creds:
254d84f4f99SDavid Howells nfs4_reset_creds(original_cred);
255c7b9a459SNeilBrown }
256c7b9a459SNeilBrown
25752e19c09SStanislav Kinsbursky typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *);
258190e4fbfSNeilBrown
25905f4f678SJ. Bruce Fields struct name_list {
26005f4f678SJ. Bruce Fields char name[HEXDIR_LEN];
261190e4fbfSNeilBrown struct list_head list;
262190e4fbfSNeilBrown };
263190e4fbfSNeilBrown
264bb6f619bSAl Viro struct nfs4_dir_ctx {
265bb6f619bSAl Viro struct dir_context ctx;
266bb6f619bSAl Viro struct list_head names;
267bb6f619bSAl Viro };
268bb6f619bSAl Viro
26925885a35SAl Viro static bool
nfsd4_build_namelist(struct dir_context * __ctx,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)270ac7576f4SMiklos Szeredi nfsd4_build_namelist(struct dir_context *__ctx, const char *name, int namlen,
271afefdbb2SDavid Howells loff_t offset, u64 ino, unsigned int d_type)
272190e4fbfSNeilBrown {
273ac7576f4SMiklos Szeredi struct nfs4_dir_ctx *ctx =
274ac7576f4SMiklos Szeredi container_of(__ctx, struct nfs4_dir_ctx, ctx);
27505f4f678SJ. Bruce Fields struct name_list *entry;
276190e4fbfSNeilBrown
27705f4f678SJ. Bruce Fields if (namlen != HEXDIR_LEN - 1)
27825885a35SAl Viro return true;
27905f4f678SJ. Bruce Fields entry = kmalloc(sizeof(struct name_list), GFP_KERNEL);
28005f4f678SJ. Bruce Fields if (entry == NULL)
28125885a35SAl Viro return false;
28205f4f678SJ. Bruce Fields memcpy(entry->name, name, HEXDIR_LEN - 1);
28305f4f678SJ. Bruce Fields entry->name[HEXDIR_LEN - 1] = '\0';
284bb6f619bSAl Viro list_add(&entry->list, &ctx->names);
28525885a35SAl Viro return true;
286190e4fbfSNeilBrown }
287190e4fbfSNeilBrown
288190e4fbfSNeilBrown static int
nfsd4_list_rec_dir(recdir_func * f,struct nfsd_net * nn)28952e19c09SStanislav Kinsbursky nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn)
290190e4fbfSNeilBrown {
291d84f4f99SDavid Howells const struct cred *original_cred;
2923a073369SStanislav Kinsbursky struct dentry *dir = nn->rec_file->f_path.dentry;
293ac6614b7SAl Viro struct nfs4_dir_ctx ctx = {
294ac6614b7SAl Viro .ctx.actor = nfsd4_build_namelist,
295ac6614b7SAl Viro .names = LIST_HEAD_INIT(ctx.names)
296ac6614b7SAl Viro };
2974691b271SKinglong Mee struct name_list *entry, *tmp;
298190e4fbfSNeilBrown int status;
299190e4fbfSNeilBrown
300d84f4f99SDavid Howells status = nfs4_save_creds(&original_cred);
301d84f4f99SDavid Howells if (status < 0)
302d84f4f99SDavid Howells return status;
303190e4fbfSNeilBrown
3043a073369SStanislav Kinsbursky status = vfs_llseek(nn->rec_file, 0, SEEK_SET);
3055b4b299cSAl Viro if (status < 0) {
3065b4b299cSAl Viro nfs4_reset_creds(original_cred);
3075b4b299cSAl Viro return status;
3085b4b299cSAl Viro }
3095b4b299cSAl Viro
3105c0ba4e0SAl Viro status = iterate_dir(nn->rec_file, &ctx.ctx);
3115955102cSAl Viro inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
3124691b271SKinglong Mee
3134691b271SKinglong Mee list_for_each_entry_safe(entry, tmp, &ctx.names, list) {
3145b4b299cSAl Viro if (!status) {
3155b4b299cSAl Viro struct dentry *dentry;
31605f4f678SJ. Bruce Fields dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
31705f4f678SJ. Bruce Fields if (IS_ERR(dentry)) {
31805f4f678SJ. Bruce Fields status = PTR_ERR(dentry);
3192f9092e1SDavid Woodhouse break;
32005f4f678SJ. Bruce Fields }
32152e19c09SStanislav Kinsbursky status = f(dir, dentry, nn);
32205f4f678SJ. Bruce Fields dput(dentry);
3235b4b299cSAl Viro }
32405f4f678SJ. Bruce Fields list_del(&entry->list);
32505f4f678SJ. Bruce Fields kfree(entry);
326190e4fbfSNeilBrown }
3275955102cSAl Viro inode_unlock(d_inode(dir));
328d84f4f99SDavid Howells nfs4_reset_creds(original_cred);
3294691b271SKinglong Mee
3304691b271SKinglong Mee list_for_each_entry_safe(entry, tmp, &ctx.names, list) {
3314691b271SKinglong Mee dprintk("NFSD: %s. Left entry %s\n", __func__, entry->name);
3324691b271SKinglong Mee list_del(&entry->list);
3334691b271SKinglong Mee kfree(entry);
3344691b271SKinglong Mee }
335190e4fbfSNeilBrown return status;
336190e4fbfSNeilBrown }
337190e4fbfSNeilBrown
338190e4fbfSNeilBrown static int
nfsd4_unlink_clid_dir(char * name,int namlen,struct nfsd_net * nn)3393a073369SStanislav Kinsbursky nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn)
340c7b9a459SNeilBrown {
341e970a573SChristoph Hellwig struct dentry *dir, *dentry;
342c7b9a459SNeilBrown int status;
343c7b9a459SNeilBrown
344c7b9a459SNeilBrown dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name);
345c7b9a459SNeilBrown
3463a073369SStanislav Kinsbursky dir = nn->rec_file->f_path.dentry;
3475955102cSAl Viro inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
348e970a573SChristoph Hellwig dentry = lookup_one_len(name, dir, namlen);
349c7b9a459SNeilBrown if (IS_ERR(dentry)) {
350c7b9a459SNeilBrown status = PTR_ERR(dentry);
3512f9092e1SDavid Woodhouse goto out_unlock;
352c7b9a459SNeilBrown }
353c7b9a459SNeilBrown status = -ENOENT;
3542b0143b5SDavid Howells if (d_really_is_negative(dentry))
355c7b9a459SNeilBrown goto out;
356abf08576SChristian Brauner status = vfs_rmdir(&nop_mnt_idmap, d_inode(dir), dentry);
357c7b9a459SNeilBrown out:
358c7b9a459SNeilBrown dput(dentry);
3592f9092e1SDavid Woodhouse out_unlock:
3605955102cSAl Viro inode_unlock(d_inode(dir));
361c7b9a459SNeilBrown return status;
362c7b9a459SNeilBrown }
363c7b9a459SNeilBrown
3642a4317c5SJeff Layton static void
__nfsd4_remove_reclaim_record_grace(const char * dname,int len,struct nfsd_net * nn)3656b189105SScott Mayhew __nfsd4_remove_reclaim_record_grace(const char *dname, int len,
3666b189105SScott Mayhew struct nfsd_net *nn)
3676b189105SScott Mayhew {
3686b189105SScott Mayhew struct xdr_netobj name;
3696b189105SScott Mayhew struct nfs4_client_reclaim *crp;
3706b189105SScott Mayhew
3716b189105SScott Mayhew name.data = kmemdup(dname, len, GFP_KERNEL);
3726b189105SScott Mayhew if (!name.data) {
3736b189105SScott Mayhew dprintk("%s: failed to allocate memory for name.data!\n",
3746b189105SScott Mayhew __func__);
3756b189105SScott Mayhew return;
3766b189105SScott Mayhew }
3776b189105SScott Mayhew name.len = len;
3786b189105SScott Mayhew crp = nfsd4_find_reclaim_client(name, nn);
3796b189105SScott Mayhew kfree(name.data);
3806b189105SScott Mayhew if (crp)
3816b189105SScott Mayhew nfs4_remove_reclaim_record(crp, nn);
3826b189105SScott Mayhew }
3836b189105SScott Mayhew
3846b189105SScott Mayhew static void
nfsd4_remove_clid_dir(struct nfs4_client * clp)385c7b9a459SNeilBrown nfsd4_remove_clid_dir(struct nfs4_client *clp)
386c7b9a459SNeilBrown {
387d84f4f99SDavid Howells const struct cred *original_cred;
3882216d449SJeff Layton char dname[HEXDIR_LEN];
389c7b9a459SNeilBrown int status;
39052e19c09SStanislav Kinsbursky struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
391c7b9a459SNeilBrown
3923a073369SStanislav Kinsbursky if (!nn->rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
393c7b9a459SNeilBrown return;
394c7b9a459SNeilBrown
3952216d449SJeff Layton status = nfs4_make_rec_clidname(dname, &clp->cl_name);
3962216d449SJeff Layton if (status)
3977255e716SJeff Layton return legacy_recdir_name_error(clp, status);
3982216d449SJeff Layton
3993a073369SStanislav Kinsbursky status = mnt_want_write_file(nn->rec_file);
4000622753bSDave Hansen if (status)
4010622753bSDave Hansen goto out;
402a52d726bSJeff Layton clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
403d84f4f99SDavid Howells
404d84f4f99SDavid Howells status = nfs4_save_creds(&original_cred);
405d84f4f99SDavid Howells if (status < 0)
406698d8d87SJeff Layton goto out_drop_write;
407d84f4f99SDavid Howells
4083a073369SStanislav Kinsbursky status = nfsd4_unlink_clid_dir(dname, HEXDIR_LEN-1, nn);
409d84f4f99SDavid Howells nfs4_reset_creds(original_cred);
4100ce0c2b5SJeff Layton if (status == 0) {
4113a073369SStanislav Kinsbursky vfs_fsync(nn->rec_file, 0);
4126b189105SScott Mayhew if (nn->in_grace)
4136b189105SScott Mayhew __nfsd4_remove_reclaim_record_grace(dname,
4146b189105SScott Mayhew HEXDIR_LEN, nn);
4150ce0c2b5SJeff Layton }
416698d8d87SJeff Layton out_drop_write:
4173a073369SStanislav Kinsbursky mnt_drop_write_file(nn->rec_file);
4180622753bSDave Hansen out:
419c7b9a459SNeilBrown if (status)
420c7b9a459SNeilBrown printk("NFSD: Failed to remove expired client state directory"
4212216d449SJeff Layton " %.*s\n", HEXDIR_LEN, dname);
422c7b9a459SNeilBrown }
423c7b9a459SNeilBrown
424c7b9a459SNeilBrown static int
purge_old(struct dentry * parent,struct dentry * child,struct nfsd_net * nn)42552e19c09SStanislav Kinsbursky purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
426c7b9a459SNeilBrown {
427c7b9a459SNeilBrown int status;
4286b189105SScott Mayhew struct xdr_netobj name;
429c7b9a459SNeilBrown
4306b189105SScott Mayhew if (child->d_name.len != HEXDIR_LEN - 1) {
4316b189105SScott Mayhew printk("%s: illegal name %pd in recovery directory\n",
4326b189105SScott Mayhew __func__, child);
4336b189105SScott Mayhew /* Keep trying; maybe the others are OK: */
434b37ad28bSAl Viro return 0;
4356b189105SScott Mayhew }
4366b189105SScott Mayhew name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
4376b189105SScott Mayhew if (!name.data) {
4386b189105SScott Mayhew dprintk("%s: failed to allocate memory for name.data!\n",
4396b189105SScott Mayhew __func__);
4406b189105SScott Mayhew goto out;
4416b189105SScott Mayhew }
4426b189105SScott Mayhew name.len = HEXDIR_LEN;
4436b189105SScott Mayhew if (nfs4_has_reclaimed_state(name, nn))
4446b189105SScott Mayhew goto out_free;
445c7b9a459SNeilBrown
446abf08576SChristian Brauner status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child);
447c7b9a459SNeilBrown if (status)
448a6a9f18fSAl Viro printk("failed to remove client recovery directory %pd\n",
449a6a9f18fSAl Viro child);
4506b189105SScott Mayhew out_free:
4516b189105SScott Mayhew kfree(name.data);
4526b189105SScott Mayhew out:
453c7b9a459SNeilBrown /* Keep trying, success or failure: */
454b37ad28bSAl Viro return 0;
455c7b9a459SNeilBrown }
456c7b9a459SNeilBrown
4572a4317c5SJeff Layton static void
nfsd4_recdir_purge_old(struct nfsd_net * nn)458919b8049SJeff Layton nfsd4_recdir_purge_old(struct nfsd_net *nn)
4592a4317c5SJeff Layton {
460c7b9a459SNeilBrown int status;
461c7b9a459SNeilBrown
462f141f79dSStanislav Kinsbursky nn->in_grace = false;
4633a073369SStanislav Kinsbursky if (!nn->rec_file)
464c7b9a459SNeilBrown return;
4653a073369SStanislav Kinsbursky status = mnt_want_write_file(nn->rec_file);
4660622753bSDave Hansen if (status)
4670622753bSDave Hansen goto out;
46852e19c09SStanislav Kinsbursky status = nfsd4_list_rec_dir(purge_old, nn);
469c7b9a459SNeilBrown if (status == 0)
4703a073369SStanislav Kinsbursky vfs_fsync(nn->rec_file, 0);
4713a073369SStanislav Kinsbursky mnt_drop_write_file(nn->rec_file);
4720622753bSDave Hansen out:
47352e19c09SStanislav Kinsbursky nfs4_release_reclaim(nn);
474c7b9a459SNeilBrown if (status)
475c7b9a459SNeilBrown printk("nfsd4: failed to purge old clients from recovery"
476a6a9f18fSAl Viro " directory %pD\n", nn->rec_file);
477c7b9a459SNeilBrown }
478c7b9a459SNeilBrown
479c7b9a459SNeilBrown static int
load_recdir(struct dentry * parent,struct dentry * child,struct nfsd_net * nn)48052e19c09SStanislav Kinsbursky load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
481190e4fbfSNeilBrown {
4826b189105SScott Mayhew struct xdr_netobj name;
4836ee95d1cSScott Mayhew struct xdr_netobj princhash = { .len = 0, .data = NULL };
4846b189105SScott Mayhew
485190e4fbfSNeilBrown if (child->d_name.len != HEXDIR_LEN - 1) {
4866b189105SScott Mayhew printk("%s: illegal name %pd in recovery directory\n",
4876b189105SScott Mayhew __func__, child);
488190e4fbfSNeilBrown /* Keep trying; maybe the others are OK: */
489b37ad28bSAl Viro return 0;
490190e4fbfSNeilBrown }
4916b189105SScott Mayhew name.data = kmemdup_nul(child->d_name.name, child->d_name.len, GFP_KERNEL);
4926b189105SScott Mayhew if (!name.data) {
4936b189105SScott Mayhew dprintk("%s: failed to allocate memory for name.data!\n",
4946b189105SScott Mayhew __func__);
4956b189105SScott Mayhew goto out;
4966b189105SScott Mayhew }
4976b189105SScott Mayhew name.len = HEXDIR_LEN;
4986ee95d1cSScott Mayhew if (!nfs4_client_to_reclaim(name, princhash, nn))
4996b189105SScott Mayhew kfree(name.data);
5006b189105SScott Mayhew out:
501b37ad28bSAl Viro return 0;
502190e4fbfSNeilBrown }
503190e4fbfSNeilBrown
5042a4317c5SJeff Layton static int
nfsd4_recdir_load(struct net * net)50552e19c09SStanislav Kinsbursky nfsd4_recdir_load(struct net *net) {
506190e4fbfSNeilBrown int status;
50752e19c09SStanislav Kinsbursky struct nfsd_net *nn = net_generic(net, nfsd_net_id);
508190e4fbfSNeilBrown
5093a073369SStanislav Kinsbursky if (!nn->rec_file)
510e970a573SChristoph Hellwig return 0;
511e970a573SChristoph Hellwig
51252e19c09SStanislav Kinsbursky status = nfsd4_list_rec_dir(load_recdir, nn);
513190e4fbfSNeilBrown if (status)
514190e4fbfSNeilBrown printk("nfsd4: failed loading clients from recovery"
515a6a9f18fSAl Viro " directory %pD\n", nn->rec_file);
516190e4fbfSNeilBrown return status;
517190e4fbfSNeilBrown }
518190e4fbfSNeilBrown
519190e4fbfSNeilBrown /*
520190e4fbfSNeilBrown * Hold reference to the recovery directory.
521190e4fbfSNeilBrown */
522190e4fbfSNeilBrown
5232a4317c5SJeff Layton static int
nfsd4_init_recdir(struct net * net)5243a073369SStanislav Kinsbursky nfsd4_init_recdir(struct net *net)
525190e4fbfSNeilBrown {
5263a073369SStanislav Kinsbursky struct nfsd_net *nn = net_generic(net, nfsd_net_id);
527d84f4f99SDavid Howells const struct cred *original_cred;
528190e4fbfSNeilBrown int status;
529190e4fbfSNeilBrown
530190e4fbfSNeilBrown printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
53148483bf2SJ. Bruce Fields user_recovery_dirname);
532190e4fbfSNeilBrown
5333a073369SStanislav Kinsbursky BUG_ON(nn->rec_file);
534190e4fbfSNeilBrown
535d84f4f99SDavid Howells status = nfs4_save_creds(&original_cred);
536d84f4f99SDavid Howells if (status < 0) {
537d84f4f99SDavid Howells printk("NFSD: Unable to change credentials to find recovery"
538d84f4f99SDavid Howells " directory: error %d\n",
539d84f4f99SDavid Howells status);
5402a4317c5SJeff Layton return status;
541d84f4f99SDavid Howells }
542190e4fbfSNeilBrown
5433a073369SStanislav Kinsbursky nn->rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0);
5443a073369SStanislav Kinsbursky if (IS_ERR(nn->rec_file)) {
545c2642ab0SJ. Bruce Fields printk("NFSD: unable to find recovery directory %s\n",
54648483bf2SJ. Bruce Fields user_recovery_dirname);
5473a073369SStanislav Kinsbursky status = PTR_ERR(nn->rec_file);
5483a073369SStanislav Kinsbursky nn->rec_file = NULL;
549e970a573SChristoph Hellwig }
550190e4fbfSNeilBrown
551d84f4f99SDavid Howells nfs4_reset_creds(original_cred);
5520ce0c2b5SJeff Layton if (!status)
553f141f79dSStanislav Kinsbursky nn->in_grace = true;
5542a4317c5SJeff Layton return status;
555190e4fbfSNeilBrown }
556190e4fbfSNeilBrown
55715d176c1SKinglong Mee static void
nfsd4_shutdown_recdir(struct net * net)55815d176c1SKinglong Mee nfsd4_shutdown_recdir(struct net *net)
55915d176c1SKinglong Mee {
56015d176c1SKinglong Mee struct nfsd_net *nn = net_generic(net, nfsd_net_id);
56115d176c1SKinglong Mee
56215d176c1SKinglong Mee if (!nn->rec_file)
56315d176c1SKinglong Mee return;
56415d176c1SKinglong Mee fput(nn->rec_file);
56515d176c1SKinglong Mee nn->rec_file = NULL;
56615d176c1SKinglong Mee }
56752e19c09SStanislav Kinsbursky
56852e19c09SStanislav Kinsbursky static int
nfs4_legacy_state_init(struct net * net)56952e19c09SStanislav Kinsbursky nfs4_legacy_state_init(struct net *net)
57052e19c09SStanislav Kinsbursky {
57152e19c09SStanislav Kinsbursky struct nfsd_net *nn = net_generic(net, nfsd_net_id);
57252e19c09SStanislav Kinsbursky int i;
57352e19c09SStanislav Kinsbursky
5746da2ec56SKees Cook nn->reclaim_str_hashtbl = kmalloc_array(CLIENT_HASH_SIZE,
5756da2ec56SKees Cook sizeof(struct list_head),
5766da2ec56SKees Cook GFP_KERNEL);
57752e19c09SStanislav Kinsbursky if (!nn->reclaim_str_hashtbl)
57852e19c09SStanislav Kinsbursky return -ENOMEM;
57952e19c09SStanislav Kinsbursky
58052e19c09SStanislav Kinsbursky for (i = 0; i < CLIENT_HASH_SIZE; i++)
58152e19c09SStanislav Kinsbursky INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
58252e19c09SStanislav Kinsbursky nn->reclaim_str_hashtbl_size = 0;
58352e19c09SStanislav Kinsbursky
58452e19c09SStanislav Kinsbursky return 0;
58552e19c09SStanislav Kinsbursky }
58652e19c09SStanislav Kinsbursky
58752e19c09SStanislav Kinsbursky static void
nfs4_legacy_state_shutdown(struct net * net)58852e19c09SStanislav Kinsbursky nfs4_legacy_state_shutdown(struct net *net)
58952e19c09SStanislav Kinsbursky {
59052e19c09SStanislav Kinsbursky struct nfsd_net *nn = net_generic(net, nfsd_net_id);
59152e19c09SStanislav Kinsbursky
59252e19c09SStanislav Kinsbursky kfree(nn->reclaim_str_hashtbl);
59352e19c09SStanislav Kinsbursky }
59452e19c09SStanislav Kinsbursky
5952a4317c5SJeff Layton static int
nfsd4_load_reboot_recovery_data(struct net * net)5962a4317c5SJeff Layton nfsd4_load_reboot_recovery_data(struct net *net)
5972a4317c5SJeff Layton {
5982a4317c5SJeff Layton int status;
5992a4317c5SJeff Layton
6003a073369SStanislav Kinsbursky status = nfsd4_init_recdir(net);
60115d176c1SKinglong Mee if (status)
60215d176c1SKinglong Mee return status;
60315d176c1SKinglong Mee
60452e19c09SStanislav Kinsbursky status = nfsd4_recdir_load(net);
60552e19c09SStanislav Kinsbursky if (status)
60615d176c1SKinglong Mee nfsd4_shutdown_recdir(net);
60715d176c1SKinglong Mee
60852e19c09SStanislav Kinsbursky return status;
60952e19c09SStanislav Kinsbursky }
61052e19c09SStanislav Kinsbursky
61152e19c09SStanislav Kinsbursky static int
nfsd4_legacy_tracking_init(struct net * net)61252e19c09SStanislav Kinsbursky nfsd4_legacy_tracking_init(struct net *net)
61352e19c09SStanislav Kinsbursky {
61452e19c09SStanislav Kinsbursky int status;
61552e19c09SStanislav Kinsbursky
616cc27e0d4SJeff Layton /* XXX: The legacy code won't work in a container */
617cc27e0d4SJeff Layton if (net != &init_net) {
61846cc8ba3SPaul Gortmaker pr_warn("NFSD: attempt to initialize legacy client tracking in a container ignored.\n");
619cc27e0d4SJeff Layton return -EINVAL;
620cc27e0d4SJeff Layton }
621cc27e0d4SJeff Layton
62252e19c09SStanislav Kinsbursky status = nfs4_legacy_state_init(net);
6232a4317c5SJeff Layton if (status)
62452e19c09SStanislav Kinsbursky return status;
62552e19c09SStanislav Kinsbursky
62652e19c09SStanislav Kinsbursky status = nfsd4_load_reboot_recovery_data(net);
62752e19c09SStanislav Kinsbursky if (status)
62852e19c09SStanislav Kinsbursky goto err;
629f988a7b7SPaul Menzel pr_info("NFSD: Using legacy client tracking operations.\n");
63052e19c09SStanislav Kinsbursky return 0;
63152e19c09SStanislav Kinsbursky
63252e19c09SStanislav Kinsbursky err:
63352e19c09SStanislav Kinsbursky nfs4_legacy_state_shutdown(net);
6342a4317c5SJeff Layton return status;
6352a4317c5SJeff Layton }
6362a4317c5SJeff Layton
6372a4317c5SJeff Layton static void
nfsd4_legacy_tracking_exit(struct net * net)6382a4317c5SJeff Layton nfsd4_legacy_tracking_exit(struct net *net)
6392a4317c5SJeff Layton {
64052e19c09SStanislav Kinsbursky struct nfsd_net *nn = net_generic(net, nfsd_net_id);
64152e19c09SStanislav Kinsbursky
64252e19c09SStanislav Kinsbursky nfs4_release_reclaim(nn);
64315d176c1SKinglong Mee nfsd4_shutdown_recdir(net);
64452e19c09SStanislav Kinsbursky nfs4_legacy_state_shutdown(net);
6452a4317c5SJeff Layton }
6462a4317c5SJeff Layton
64748483bf2SJ. Bruce Fields /*
64848483bf2SJ. Bruce Fields * Change the NFSv4 recovery directory to recdir.
64948483bf2SJ. Bruce Fields */
65048483bf2SJ. Bruce Fields int
nfs4_reset_recoverydir(char * recdir)65148483bf2SJ. Bruce Fields nfs4_reset_recoverydir(char *recdir)
65248483bf2SJ. Bruce Fields {
65348483bf2SJ. Bruce Fields int status;
65448483bf2SJ. Bruce Fields struct path path;
65548483bf2SJ. Bruce Fields
65648483bf2SJ. Bruce Fields status = kern_path(recdir, LOOKUP_FOLLOW, &path);
65748483bf2SJ. Bruce Fields if (status)
65848483bf2SJ. Bruce Fields return status;
65948483bf2SJ. Bruce Fields status = -ENOTDIR;
660e36cb0b8SDavid Howells if (d_is_dir(path.dentry)) {
661*02841754SChuck Lever strscpy(user_recovery_dirname, recdir,
662*02841754SChuck Lever sizeof(user_recovery_dirname));
66348483bf2SJ. Bruce Fields status = 0;
66448483bf2SJ. Bruce Fields }
66548483bf2SJ. Bruce Fields path_put(&path);
66648483bf2SJ. Bruce Fields return status;
66748483bf2SJ. Bruce Fields }
66848483bf2SJ. Bruce Fields
66948483bf2SJ. Bruce Fields char *
nfs4_recoverydir(void)67048483bf2SJ. Bruce Fields nfs4_recoverydir(void)
67148483bf2SJ. Bruce Fields {
67248483bf2SJ. Bruce Fields return user_recovery_dirname;
67348483bf2SJ. Bruce Fields }
6742a4317c5SJeff Layton
6752a4317c5SJeff Layton static int
nfsd4_check_legacy_client(struct nfs4_client * clp)6762a4317c5SJeff Layton nfsd4_check_legacy_client(struct nfs4_client *clp)
6772a4317c5SJeff Layton {
6782216d449SJeff Layton int status;
6792216d449SJeff Layton char dname[HEXDIR_LEN];
6800ce0c2b5SJeff Layton struct nfs4_client_reclaim *crp;
68152e19c09SStanislav Kinsbursky struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
6826b189105SScott Mayhew struct xdr_netobj name;
6830ce0c2b5SJeff Layton
6842a4317c5SJeff Layton /* did we already find that this client is stable? */
6852a4317c5SJeff Layton if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
6862a4317c5SJeff Layton return 0;
6872a4317c5SJeff Layton
6882216d449SJeff Layton status = nfs4_make_rec_clidname(dname, &clp->cl_name);
6892216d449SJeff Layton if (status) {
6907255e716SJeff Layton legacy_recdir_name_error(clp, status);
6912216d449SJeff Layton return status;
6922216d449SJeff Layton }
6932216d449SJeff Layton
6942a4317c5SJeff Layton /* look for it in the reclaim hashtable otherwise */
6956b189105SScott Mayhew name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
6966b189105SScott Mayhew if (!name.data) {
6976b189105SScott Mayhew dprintk("%s: failed to allocate memory for name.data!\n",
6986b189105SScott Mayhew __func__);
6996b189105SScott Mayhew goto out_enoent;
7006b189105SScott Mayhew }
7016b189105SScott Mayhew name.len = HEXDIR_LEN;
7026b189105SScott Mayhew crp = nfsd4_find_reclaim_client(name, nn);
7036b189105SScott Mayhew kfree(name.data);
7040ce0c2b5SJeff Layton if (crp) {
7052a4317c5SJeff Layton set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
7060ce0c2b5SJeff Layton crp->cr_clp = clp;
7072a4317c5SJeff Layton return 0;
7082a4317c5SJeff Layton }
7092a4317c5SJeff Layton
7106b189105SScott Mayhew out_enoent:
7112a4317c5SJeff Layton return -ENOENT;
7122a4317c5SJeff Layton }
7132a4317c5SJeff Layton
7147c582e4fSJulia Lawall static const struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
71552e19c09SStanislav Kinsbursky .init = nfsd4_legacy_tracking_init,
7162a4317c5SJeff Layton .exit = nfsd4_legacy_tracking_exit,
7172a4317c5SJeff Layton .create = nfsd4_create_clid_dir,
7182a4317c5SJeff Layton .remove = nfsd4_remove_clid_dir,
7192a4317c5SJeff Layton .check = nfsd4_check_legacy_client,
7202a4317c5SJeff Layton .grace_done = nfsd4_recdir_purge_old,
72111a60d15SScott Mayhew .version = 1,
72211a60d15SScott Mayhew .msglen = 0,
7232a4317c5SJeff Layton };
7242a4317c5SJeff Layton
725f3f80148SJeff Layton /* Globals */
726f3f80148SJeff Layton #define NFSD_PIPE_DIR "nfsd"
727f3f80148SJeff Layton #define NFSD_CLD_PIPE "cld"
728f3f80148SJeff Layton
729f3f80148SJeff Layton /* per-net-ns structure for holding cld upcall info */
730f3f80148SJeff Layton struct cld_net {
731f3f80148SJeff Layton struct rpc_pipe *cn_pipe;
732f3f80148SJeff Layton spinlock_t cn_lock;
733f3f80148SJeff Layton struct list_head cn_list;
734f3f80148SJeff Layton unsigned int cn_xid;
7358a9f4f41SScott Mayhew bool cn_has_legacy;
7366ee95d1cSScott Mayhew struct crypto_shash *cn_tfm;
737f3f80148SJeff Layton };
738f3f80148SJeff Layton
739f3f80148SJeff Layton struct cld_upcall {
740f3f80148SJeff Layton struct list_head cu_list;
741f3f80148SJeff Layton struct cld_net *cu_net;
742b493fd31SScott Mayhew struct completion cu_done;
74311a60d15SScott Mayhew union {
74411a60d15SScott Mayhew struct cld_msg_hdr cu_hdr;
745f3f80148SJeff Layton struct cld_msg cu_msg;
7466ee95d1cSScott Mayhew struct cld_msg_v2 cu_msg_v2;
74711a60d15SScott Mayhew } cu_u;
748f3f80148SJeff Layton };
749f3f80148SJeff Layton
750f3f80148SJeff Layton static int
__cld_pipe_upcall(struct rpc_pipe * pipe,void * cmsg,struct nfsd_net * nn)751df60446cSScott Mayhew __cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg, struct nfsd_net *nn)
752f3f80148SJeff Layton {
753f3f80148SJeff Layton int ret;
754f3f80148SJeff Layton struct rpc_pipe_msg msg;
75511a60d15SScott Mayhew struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, cu_u);
756f3f80148SJeff Layton
757f3f80148SJeff Layton memset(&msg, 0, sizeof(msg));
758f3f80148SJeff Layton msg.data = cmsg;
75911a60d15SScott Mayhew msg.len = nn->client_tracking_ops->msglen;
760f3f80148SJeff Layton
761f3f80148SJeff Layton ret = rpc_queue_upcall(pipe, &msg);
762f3f80148SJeff Layton if (ret < 0) {
763f3f80148SJeff Layton goto out;
764f3f80148SJeff Layton }
765f3f80148SJeff Layton
766b493fd31SScott Mayhew wait_for_completion(&cup->cu_done);
767f3f80148SJeff Layton
768f3f80148SJeff Layton if (msg.errno < 0)
769f3f80148SJeff Layton ret = msg.errno;
770f3f80148SJeff Layton out:
771f3f80148SJeff Layton return ret;
772f3f80148SJeff Layton }
773f3f80148SJeff Layton
774f3f80148SJeff Layton static int
cld_pipe_upcall(struct rpc_pipe * pipe,void * cmsg,struct nfsd_net * nn)775df60446cSScott Mayhew cld_pipe_upcall(struct rpc_pipe *pipe, void *cmsg, struct nfsd_net *nn)
776f3f80148SJeff Layton {
777f3f80148SJeff Layton int ret;
778f3f80148SJeff Layton
779f3f80148SJeff Layton /*
780f3f80148SJeff Layton * -EAGAIN occurs when pipe is closed and reopened while there are
781f3f80148SJeff Layton * upcalls queued.
782f3f80148SJeff Layton */
783f3f80148SJeff Layton do {
784df60446cSScott Mayhew ret = __cld_pipe_upcall(pipe, cmsg, nn);
785f3f80148SJeff Layton } while (ret == -EAGAIN);
786f3f80148SJeff Layton
787f3f80148SJeff Layton return ret;
788f3f80148SJeff Layton }
789f3f80148SJeff Layton
790f3f80148SJeff Layton static ssize_t
__cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user * cmsg,struct nfsd_net * nn)7916ee95d1cSScott Mayhew __cld_pipe_inprogress_downcall(const struct cld_msg_v2 __user *cmsg,
79274725959SScott Mayhew struct nfsd_net *nn)
79374725959SScott Mayhew {
7946ee95d1cSScott Mayhew uint8_t cmd, princhashlen;
7956ee95d1cSScott Mayhew struct xdr_netobj name, princhash = { .len = 0, .data = NULL };
79674725959SScott Mayhew uint16_t namelen;
7978a9f4f41SScott Mayhew struct cld_net *cn = nn->cld_net;
79874725959SScott Mayhew
79974725959SScott Mayhew if (get_user(cmd, &cmsg->cm_cmd)) {
80074725959SScott Mayhew dprintk("%s: error when copying cmd from userspace", __func__);
80174725959SScott Mayhew return -EFAULT;
80274725959SScott Mayhew }
80374725959SScott Mayhew if (cmd == Cld_GraceStart) {
8046ee95d1cSScott Mayhew if (nn->client_tracking_ops->version >= 2) {
8056ee95d1cSScott Mayhew const struct cld_clntinfo __user *ci;
8066ee95d1cSScott Mayhew
8076ee95d1cSScott Mayhew ci = &cmsg->cm_u.cm_clntinfo;
8086ee95d1cSScott Mayhew if (get_user(namelen, &ci->cc_name.cn_len))
80974725959SScott Mayhew return -EFAULT;
810318f7085SLi Lingfeng if (!namelen) {
811318f7085SLi Lingfeng dprintk("%s: namelen should not be zero", __func__);
812318f7085SLi Lingfeng return -EINVAL;
813318f7085SLi Lingfeng }
8146ee95d1cSScott Mayhew name.data = memdup_user(&ci->cc_name.cn_id, namelen);
815d44899b8SChristophe JAILLET if (IS_ERR(name.data))
81630a30fccSChristophe JAILLET return PTR_ERR(name.data);
81774725959SScott Mayhew name.len = namelen;
8186ee95d1cSScott Mayhew get_user(princhashlen, &ci->cc_princhash.cp_len);
8196ee95d1cSScott Mayhew if (princhashlen > 0) {
8206ee95d1cSScott Mayhew princhash.data = memdup_user(
8216ee95d1cSScott Mayhew &ci->cc_princhash.cp_data,
8226ee95d1cSScott Mayhew princhashlen);
823d44899b8SChristophe JAILLET if (IS_ERR(princhash.data)) {
824fd1ef880SChristophe JAILLET kfree(name.data);
82530a30fccSChristophe JAILLET return PTR_ERR(princhash.data);
826fd1ef880SChristophe JAILLET }
8276ee95d1cSScott Mayhew princhash.len = princhashlen;
8286ee95d1cSScott Mayhew } else
8296ee95d1cSScott Mayhew princhash.len = 0;
8306ee95d1cSScott Mayhew } else {
8316ee95d1cSScott Mayhew const struct cld_name __user *cnm;
8326ee95d1cSScott Mayhew
8336ee95d1cSScott Mayhew cnm = &cmsg->cm_u.cm_name;
8346ee95d1cSScott Mayhew if (get_user(namelen, &cnm->cn_len))
8356ee95d1cSScott Mayhew return -EFAULT;
836318f7085SLi Lingfeng if (!namelen) {
837318f7085SLi Lingfeng dprintk("%s: namelen should not be zero", __func__);
838318f7085SLi Lingfeng return -EINVAL;
839318f7085SLi Lingfeng }
8406ee95d1cSScott Mayhew name.data = memdup_user(&cnm->cn_id, namelen);
841d44899b8SChristophe JAILLET if (IS_ERR(name.data))
84230a30fccSChristophe JAILLET return PTR_ERR(name.data);
8436ee95d1cSScott Mayhew name.len = namelen;
8446ee95d1cSScott Mayhew }
8458a9f4f41SScott Mayhew if (name.len > 5 && memcmp(name.data, "hash:", 5) == 0) {
8468a9f4f41SScott Mayhew name.len = name.len - 5;
8478a9f4f41SScott Mayhew memmove(name.data, name.data + 5, name.len);
8488a9f4f41SScott Mayhew cn->cn_has_legacy = true;
8498a9f4f41SScott Mayhew }
8506ee95d1cSScott Mayhew if (!nfs4_client_to_reclaim(name, princhash, nn)) {
85174725959SScott Mayhew kfree(name.data);
8526ee95d1cSScott Mayhew kfree(princhash.data);
85374725959SScott Mayhew return -EFAULT;
85474725959SScott Mayhew }
85511a60d15SScott Mayhew return nn->client_tracking_ops->msglen;
85674725959SScott Mayhew }
85774725959SScott Mayhew return -EFAULT;
85874725959SScott Mayhew }
85974725959SScott Mayhew
86074725959SScott Mayhew static ssize_t
cld_pipe_downcall(struct file * filp,const char __user * src,size_t mlen)861f3f80148SJeff Layton cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
862f3f80148SJeff Layton {
863f3f80148SJeff Layton struct cld_upcall *tmp, *cup;
86411a60d15SScott Mayhew struct cld_msg_hdr __user *hdr = (struct cld_msg_hdr __user *)src;
8656ee95d1cSScott Mayhew struct cld_msg_v2 __user *cmsg = (struct cld_msg_v2 __user *)src;
866f3f80148SJeff Layton uint32_t xid;
867ef8a1a10SAl Viro struct nfsd_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info,
868f3f80148SJeff Layton nfsd_net_id);
869f3f80148SJeff Layton struct cld_net *cn = nn->cld_net;
87074725959SScott Mayhew int16_t status;
871f3f80148SJeff Layton
87211a60d15SScott Mayhew if (mlen != nn->client_tracking_ops->msglen) {
8738a7dc4b0SRandy Dunlap dprintk("%s: got %zu bytes, expected %zu\n", __func__, mlen,
87411a60d15SScott Mayhew nn->client_tracking_ops->msglen);
875f3f80148SJeff Layton return -EINVAL;
876f3f80148SJeff Layton }
877f3f80148SJeff Layton
878f3f80148SJeff Layton /* copy just the xid so we can try to find that */
87911a60d15SScott Mayhew if (copy_from_user(&xid, &hdr->cm_xid, sizeof(xid)) != 0) {
880f3f80148SJeff Layton dprintk("%s: error when copying xid from userspace", __func__);
881f3f80148SJeff Layton return -EFAULT;
882f3f80148SJeff Layton }
883f3f80148SJeff Layton
88474725959SScott Mayhew /*
88574725959SScott Mayhew * copy the status so we know whether to remove the upcall from the
88674725959SScott Mayhew * list (for -EINPROGRESS, we just want to make sure the xid is
88774725959SScott Mayhew * valid, not remove the upcall from the list)
88874725959SScott Mayhew */
88911a60d15SScott Mayhew if (get_user(status, &hdr->cm_status)) {
89074725959SScott Mayhew dprintk("%s: error when copying status from userspace", __func__);
89174725959SScott Mayhew return -EFAULT;
89274725959SScott Mayhew }
89374725959SScott Mayhew
894f3f80148SJeff Layton /* walk the list and find corresponding xid */
895f3f80148SJeff Layton cup = NULL;
896f3f80148SJeff Layton spin_lock(&cn->cn_lock);
897f3f80148SJeff Layton list_for_each_entry(tmp, &cn->cn_list, cu_list) {
89811a60d15SScott Mayhew if (get_unaligned(&tmp->cu_u.cu_hdr.cm_xid) == xid) {
899f3f80148SJeff Layton cup = tmp;
90074725959SScott Mayhew if (status != -EINPROGRESS)
901f3f80148SJeff Layton list_del_init(&cup->cu_list);
902f3f80148SJeff Layton break;
903f3f80148SJeff Layton }
904f3f80148SJeff Layton }
905f3f80148SJeff Layton spin_unlock(&cn->cn_lock);
906f3f80148SJeff Layton
907f3f80148SJeff Layton /* couldn't find upcall? */
908f3f80148SJeff Layton if (!cup) {
90921f72c9fSJeff Layton dprintk("%s: couldn't find upcall -- xid=%u\n", __func__, xid);
910f3f80148SJeff Layton return -EINVAL;
911f3f80148SJeff Layton }
912f3f80148SJeff Layton
91374725959SScott Mayhew if (status == -EINPROGRESS)
91474725959SScott Mayhew return __cld_pipe_inprogress_downcall(cmsg, nn);
91574725959SScott Mayhew
9166ee95d1cSScott Mayhew if (copy_from_user(&cup->cu_u.cu_msg_v2, src, mlen) != 0)
917f3f80148SJeff Layton return -EFAULT;
918f3f80148SJeff Layton
919b493fd31SScott Mayhew complete(&cup->cu_done);
920f3f80148SJeff Layton return mlen;
921f3f80148SJeff Layton }
922f3f80148SJeff Layton
923f3f80148SJeff Layton static void
cld_pipe_destroy_msg(struct rpc_pipe_msg * msg)924f3f80148SJeff Layton cld_pipe_destroy_msg(struct rpc_pipe_msg *msg)
925f3f80148SJeff Layton {
926f3f80148SJeff Layton struct cld_msg *cmsg = msg->data;
927f3f80148SJeff Layton struct cld_upcall *cup = container_of(cmsg, struct cld_upcall,
92811a60d15SScott Mayhew cu_u.cu_msg);
929f3f80148SJeff Layton
930f3f80148SJeff Layton /* errno >= 0 means we got a downcall */
931f3f80148SJeff Layton if (msg->errno >= 0)
932f3f80148SJeff Layton return;
933f3f80148SJeff Layton
934b493fd31SScott Mayhew complete(&cup->cu_done);
935f3f80148SJeff Layton }
936f3f80148SJeff Layton
937f3f80148SJeff Layton static const struct rpc_pipe_ops cld_upcall_ops = {
938f3f80148SJeff Layton .upcall = rpc_pipe_generic_upcall,
939f3f80148SJeff Layton .downcall = cld_pipe_downcall,
940f3f80148SJeff Layton .destroy_msg = cld_pipe_destroy_msg,
941f3f80148SJeff Layton };
942f3f80148SJeff Layton
943f3f80148SJeff Layton static struct dentry *
nfsd4_cld_register_sb(struct super_block * sb,struct rpc_pipe * pipe)944f3f80148SJeff Layton nfsd4_cld_register_sb(struct super_block *sb, struct rpc_pipe *pipe)
945f3f80148SJeff Layton {
946f3f80148SJeff Layton struct dentry *dir, *dentry;
947f3f80148SJeff Layton
948f3f80148SJeff Layton dir = rpc_d_lookup_sb(sb, NFSD_PIPE_DIR);
949f3f80148SJeff Layton if (dir == NULL)
950f3f80148SJeff Layton return ERR_PTR(-ENOENT);
951f3f80148SJeff Layton dentry = rpc_mkpipe_dentry(dir, NFSD_CLD_PIPE, NULL, pipe);
952f3f80148SJeff Layton dput(dir);
953f3f80148SJeff Layton return dentry;
954f3f80148SJeff Layton }
955f3f80148SJeff Layton
956f3f80148SJeff Layton static void
nfsd4_cld_unregister_sb(struct rpc_pipe * pipe)957f3f80148SJeff Layton nfsd4_cld_unregister_sb(struct rpc_pipe *pipe)
958f3f80148SJeff Layton {
959f3f80148SJeff Layton if (pipe->dentry)
960f3f80148SJeff Layton rpc_unlink(pipe->dentry);
961f3f80148SJeff Layton }
962f3f80148SJeff Layton
963f3f80148SJeff Layton static struct dentry *
nfsd4_cld_register_net(struct net * net,struct rpc_pipe * pipe)964f3f80148SJeff Layton nfsd4_cld_register_net(struct net *net, struct rpc_pipe *pipe)
965f3f80148SJeff Layton {
966f3f80148SJeff Layton struct super_block *sb;
967f3f80148SJeff Layton struct dentry *dentry;
968f3f80148SJeff Layton
969f3f80148SJeff Layton sb = rpc_get_sb_net(net);
970f3f80148SJeff Layton if (!sb)
971f3f80148SJeff Layton return NULL;
972f3f80148SJeff Layton dentry = nfsd4_cld_register_sb(sb, pipe);
973f3f80148SJeff Layton rpc_put_sb_net(net);
974f3f80148SJeff Layton return dentry;
975f3f80148SJeff Layton }
976f3f80148SJeff Layton
977f3f80148SJeff Layton static void
nfsd4_cld_unregister_net(struct net * net,struct rpc_pipe * pipe)978f3f80148SJeff Layton nfsd4_cld_unregister_net(struct net *net, struct rpc_pipe *pipe)
979f3f80148SJeff Layton {
980f3f80148SJeff Layton struct super_block *sb;
981f3f80148SJeff Layton
982f3f80148SJeff Layton sb = rpc_get_sb_net(net);
983f3f80148SJeff Layton if (sb) {
984f3f80148SJeff Layton nfsd4_cld_unregister_sb(pipe);
985f3f80148SJeff Layton rpc_put_sb_net(net);
986f3f80148SJeff Layton }
987f3f80148SJeff Layton }
988f3f80148SJeff Layton
989f3f80148SJeff Layton /* Initialize rpc_pipefs pipe for communication with client tracking daemon */
990f3f80148SJeff Layton static int
__nfsd4_init_cld_pipe(struct net * net)99186921607SScott Mayhew __nfsd4_init_cld_pipe(struct net *net)
992f3f80148SJeff Layton {
993f3f80148SJeff Layton int ret;
994f3f80148SJeff Layton struct dentry *dentry;
995f3f80148SJeff Layton struct nfsd_net *nn = net_generic(net, nfsd_net_id);
996f3f80148SJeff Layton struct cld_net *cn;
997f3f80148SJeff Layton
998f3f80148SJeff Layton if (nn->cld_net)
999f3f80148SJeff Layton return 0;
1000f3f80148SJeff Layton
1001f3f80148SJeff Layton cn = kzalloc(sizeof(*cn), GFP_KERNEL);
1002f3f80148SJeff Layton if (!cn) {
1003f3f80148SJeff Layton ret = -ENOMEM;
1004f3f80148SJeff Layton goto err;
1005f3f80148SJeff Layton }
1006f3f80148SJeff Layton
1007f3f80148SJeff Layton cn->cn_pipe = rpc_mkpipe_data(&cld_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
1008f3f80148SJeff Layton if (IS_ERR(cn->cn_pipe)) {
1009f3f80148SJeff Layton ret = PTR_ERR(cn->cn_pipe);
1010f3f80148SJeff Layton goto err;
1011f3f80148SJeff Layton }
1012f3f80148SJeff Layton spin_lock_init(&cn->cn_lock);
1013f3f80148SJeff Layton INIT_LIST_HEAD(&cn->cn_list);
1014f3f80148SJeff Layton
1015f3f80148SJeff Layton dentry = nfsd4_cld_register_net(net, cn->cn_pipe);
1016f3f80148SJeff Layton if (IS_ERR(dentry)) {
1017f3f80148SJeff Layton ret = PTR_ERR(dentry);
1018f3f80148SJeff Layton goto err_destroy_data;
1019f3f80148SJeff Layton }
1020f3f80148SJeff Layton
1021f3f80148SJeff Layton cn->cn_pipe->dentry = dentry;
10228a9f4f41SScott Mayhew cn->cn_has_legacy = false;
1023f3f80148SJeff Layton nn->cld_net = cn;
1024f3f80148SJeff Layton return 0;
1025f3f80148SJeff Layton
1026f3f80148SJeff Layton err_destroy_data:
1027f3f80148SJeff Layton rpc_destroy_pipe_data(cn->cn_pipe);
1028f3f80148SJeff Layton err:
1029f3f80148SJeff Layton kfree(cn);
1030f3f80148SJeff Layton printk(KERN_ERR "NFSD: unable to create nfsdcld upcall pipe (%d)\n",
1031f3f80148SJeff Layton ret);
1032f3f80148SJeff Layton return ret;
1033f3f80148SJeff Layton }
1034f3f80148SJeff Layton
103586921607SScott Mayhew static int
nfsd4_init_cld_pipe(struct net * net)103686921607SScott Mayhew nfsd4_init_cld_pipe(struct net *net)
103786921607SScott Mayhew {
103886921607SScott Mayhew int status;
103986921607SScott Mayhew
104086921607SScott Mayhew status = __nfsd4_init_cld_pipe(net);
104186921607SScott Mayhew if (!status)
1042f988a7b7SPaul Menzel pr_info("NFSD: Using old nfsdcld client tracking operations.\n");
104386921607SScott Mayhew return status;
104486921607SScott Mayhew }
104586921607SScott Mayhew
1046f3f80148SJeff Layton static void
nfsd4_remove_cld_pipe(struct net * net)1047f3f80148SJeff Layton nfsd4_remove_cld_pipe(struct net *net)
1048f3f80148SJeff Layton {
1049f3f80148SJeff Layton struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1050f3f80148SJeff Layton struct cld_net *cn = nn->cld_net;
1051f3f80148SJeff Layton
1052f3f80148SJeff Layton nfsd4_cld_unregister_net(net, cn->cn_pipe);
1053f3f80148SJeff Layton rpc_destroy_pipe_data(cn->cn_pipe);
10546ee95d1cSScott Mayhew if (cn->cn_tfm)
10556ee95d1cSScott Mayhew crypto_free_shash(cn->cn_tfm);
1056f3f80148SJeff Layton kfree(nn->cld_net);
1057f3f80148SJeff Layton nn->cld_net = NULL;
1058f3f80148SJeff Layton }
1059f3f80148SJeff Layton
1060f3f80148SJeff Layton static struct cld_upcall *
alloc_cld_upcall(struct nfsd_net * nn)106111a60d15SScott Mayhew alloc_cld_upcall(struct nfsd_net *nn)
1062f3f80148SJeff Layton {
1063f3f80148SJeff Layton struct cld_upcall *new, *tmp;
106411a60d15SScott Mayhew struct cld_net *cn = nn->cld_net;
1065f3f80148SJeff Layton
1066f3f80148SJeff Layton new = kzalloc(sizeof(*new), GFP_KERNEL);
1067f3f80148SJeff Layton if (!new)
1068f3f80148SJeff Layton return new;
1069f3f80148SJeff Layton
1070f3f80148SJeff Layton /* FIXME: hard cap on number in flight? */
1071f3f80148SJeff Layton restart_search:
1072f3f80148SJeff Layton spin_lock(&cn->cn_lock);
1073f3f80148SJeff Layton list_for_each_entry(tmp, &cn->cn_list, cu_list) {
107411a60d15SScott Mayhew if (tmp->cu_u.cu_msg.cm_xid == cn->cn_xid) {
1075f3f80148SJeff Layton cn->cn_xid++;
1076f3f80148SJeff Layton spin_unlock(&cn->cn_lock);
1077f3f80148SJeff Layton goto restart_search;
1078f3f80148SJeff Layton }
1079f3f80148SJeff Layton }
1080b493fd31SScott Mayhew init_completion(&new->cu_done);
108111a60d15SScott Mayhew new->cu_u.cu_msg.cm_vers = nn->client_tracking_ops->version;
108211a60d15SScott Mayhew put_unaligned(cn->cn_xid++, &new->cu_u.cu_msg.cm_xid);
1083f3f80148SJeff Layton new->cu_net = cn;
1084f3f80148SJeff Layton list_add(&new->cu_list, &cn->cn_list);
1085f3f80148SJeff Layton spin_unlock(&cn->cn_lock);
1086f3f80148SJeff Layton
108711a60d15SScott Mayhew dprintk("%s: allocated xid %u\n", __func__, new->cu_u.cu_msg.cm_xid);
1088f3f80148SJeff Layton
1089f3f80148SJeff Layton return new;
1090f3f80148SJeff Layton }
1091f3f80148SJeff Layton
1092f3f80148SJeff Layton static void
free_cld_upcall(struct cld_upcall * victim)1093f3f80148SJeff Layton free_cld_upcall(struct cld_upcall *victim)
1094f3f80148SJeff Layton {
1095f3f80148SJeff Layton struct cld_net *cn = victim->cu_net;
1096f3f80148SJeff Layton
1097f3f80148SJeff Layton spin_lock(&cn->cn_lock);
1098f3f80148SJeff Layton list_del(&victim->cu_list);
1099f3f80148SJeff Layton spin_unlock(&cn->cn_lock);
1100f3f80148SJeff Layton kfree(victim);
1101f3f80148SJeff Layton }
1102f3f80148SJeff Layton
1103f3f80148SJeff Layton /* Ask daemon to create a new record */
1104f3f80148SJeff Layton static void
nfsd4_cld_create(struct nfs4_client * clp)1105f3f80148SJeff Layton nfsd4_cld_create(struct nfs4_client *clp)
1106f3f80148SJeff Layton {
1107f3f80148SJeff Layton int ret;
1108f3f80148SJeff Layton struct cld_upcall *cup;
1109c212cecfSStanislav Kinsbursky struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1110f3f80148SJeff Layton struct cld_net *cn = nn->cld_net;
1111f3f80148SJeff Layton
1112f3f80148SJeff Layton /* Don't upcall if it's already stored */
1113f3f80148SJeff Layton if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1114f3f80148SJeff Layton return;
1115f3f80148SJeff Layton
111611a60d15SScott Mayhew cup = alloc_cld_upcall(nn);
1117f3f80148SJeff Layton if (!cup) {
1118f3f80148SJeff Layton ret = -ENOMEM;
1119f3f80148SJeff Layton goto out_err;
1120f3f80148SJeff Layton }
1121f3f80148SJeff Layton
112211a60d15SScott Mayhew cup->cu_u.cu_msg.cm_cmd = Cld_Create;
112311a60d15SScott Mayhew cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
112411a60d15SScott Mayhew memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
1125f3f80148SJeff Layton clp->cl_name.len);
1126f3f80148SJeff Layton
1127df60446cSScott Mayhew ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
1128f3f80148SJeff Layton if (!ret) {
112911a60d15SScott Mayhew ret = cup->cu_u.cu_msg.cm_status;
1130f3f80148SJeff Layton set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1131f3f80148SJeff Layton }
1132f3f80148SJeff Layton
1133f3f80148SJeff Layton free_cld_upcall(cup);
1134f3f80148SJeff Layton out_err:
1135f3f80148SJeff Layton if (ret)
1136f3f80148SJeff Layton printk(KERN_ERR "NFSD: Unable to create client "
1137f3f80148SJeff Layton "record on stable storage: %d\n", ret);
1138f3f80148SJeff Layton }
1139f3f80148SJeff Layton
1140f3f80148SJeff Layton /* Ask daemon to create a new record */
1141f3f80148SJeff Layton static void
nfsd4_cld_create_v2(struct nfs4_client * clp)11426ee95d1cSScott Mayhew nfsd4_cld_create_v2(struct nfs4_client *clp)
11436ee95d1cSScott Mayhew {
11446ee95d1cSScott Mayhew int ret;
11456ee95d1cSScott Mayhew struct cld_upcall *cup;
11466ee95d1cSScott Mayhew struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
11476ee95d1cSScott Mayhew struct cld_net *cn = nn->cld_net;
11486ee95d1cSScott Mayhew struct cld_msg_v2 *cmsg;
11496ee95d1cSScott Mayhew struct crypto_shash *tfm = cn->cn_tfm;
11506ee95d1cSScott Mayhew struct xdr_netobj cksum;
11516ee95d1cSScott Mayhew char *principal = NULL;
11526ee95d1cSScott Mayhew
11536ee95d1cSScott Mayhew /* Don't upcall if it's already stored */
11546ee95d1cSScott Mayhew if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
11556ee95d1cSScott Mayhew return;
11566ee95d1cSScott Mayhew
11576ee95d1cSScott Mayhew cup = alloc_cld_upcall(nn);
11586ee95d1cSScott Mayhew if (!cup) {
11596ee95d1cSScott Mayhew ret = -ENOMEM;
11606ee95d1cSScott Mayhew goto out_err;
11616ee95d1cSScott Mayhew }
11626ee95d1cSScott Mayhew
11636ee95d1cSScott Mayhew cmsg = &cup->cu_u.cu_msg_v2;
11646ee95d1cSScott Mayhew cmsg->cm_cmd = Cld_Create;
11656ee95d1cSScott Mayhew cmsg->cm_u.cm_clntinfo.cc_name.cn_len = clp->cl_name.len;
11666ee95d1cSScott Mayhew memcpy(cmsg->cm_u.cm_clntinfo.cc_name.cn_id, clp->cl_name.data,
11676ee95d1cSScott Mayhew clp->cl_name.len);
11686ee95d1cSScott Mayhew if (clp->cl_cred.cr_raw_principal)
11696ee95d1cSScott Mayhew principal = clp->cl_cred.cr_raw_principal;
11706ee95d1cSScott Mayhew else if (clp->cl_cred.cr_principal)
11716ee95d1cSScott Mayhew principal = clp->cl_cred.cr_principal;
11726ee95d1cSScott Mayhew if (principal) {
11736ee95d1cSScott Mayhew cksum.len = crypto_shash_digestsize(tfm);
11746ee95d1cSScott Mayhew cksum.data = kmalloc(cksum.len, GFP_KERNEL);
11756ee95d1cSScott Mayhew if (cksum.data == NULL) {
11766ee95d1cSScott Mayhew ret = -ENOMEM;
11776ee95d1cSScott Mayhew goto out;
11786ee95d1cSScott Mayhew }
1179ea794db2SEric Biggers ret = crypto_shash_tfm_digest(tfm, principal, strlen(principal),
11806ee95d1cSScott Mayhew cksum.data);
11816ee95d1cSScott Mayhew if (ret) {
11826ee95d1cSScott Mayhew kfree(cksum.data);
11836ee95d1cSScott Mayhew goto out;
11846ee95d1cSScott Mayhew }
11856ee95d1cSScott Mayhew cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = cksum.len;
11866ee95d1cSScott Mayhew memcpy(cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data,
11876ee95d1cSScott Mayhew cksum.data, cksum.len);
11886ee95d1cSScott Mayhew kfree(cksum.data);
11896ee95d1cSScott Mayhew } else
11906ee95d1cSScott Mayhew cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = 0;
11916ee95d1cSScott Mayhew
1192df60446cSScott Mayhew ret = cld_pipe_upcall(cn->cn_pipe, cmsg, nn);
11936ee95d1cSScott Mayhew if (!ret) {
11946ee95d1cSScott Mayhew ret = cmsg->cm_status;
11956ee95d1cSScott Mayhew set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
11966ee95d1cSScott Mayhew }
11976ee95d1cSScott Mayhew
11986ee95d1cSScott Mayhew out:
11996ee95d1cSScott Mayhew free_cld_upcall(cup);
12006ee95d1cSScott Mayhew out_err:
12016ee95d1cSScott Mayhew if (ret)
12026ee95d1cSScott Mayhew pr_err("NFSD: Unable to create client record on stable storage: %d\n",
12036ee95d1cSScott Mayhew ret);
12046ee95d1cSScott Mayhew }
12056ee95d1cSScott Mayhew
12066ee95d1cSScott Mayhew /* Ask daemon to create a new record */
12076ee95d1cSScott Mayhew static void
nfsd4_cld_remove(struct nfs4_client * clp)1208f3f80148SJeff Layton nfsd4_cld_remove(struct nfs4_client *clp)
1209f3f80148SJeff Layton {
1210f3f80148SJeff Layton int ret;
1211f3f80148SJeff Layton struct cld_upcall *cup;
1212c212cecfSStanislav Kinsbursky struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1213f3f80148SJeff Layton struct cld_net *cn = nn->cld_net;
1214f3f80148SJeff Layton
1215f3f80148SJeff Layton /* Don't upcall if it's already removed */
1216f3f80148SJeff Layton if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1217f3f80148SJeff Layton return;
1218f3f80148SJeff Layton
121911a60d15SScott Mayhew cup = alloc_cld_upcall(nn);
1220f3f80148SJeff Layton if (!cup) {
1221f3f80148SJeff Layton ret = -ENOMEM;
1222f3f80148SJeff Layton goto out_err;
1223f3f80148SJeff Layton }
1224f3f80148SJeff Layton
122511a60d15SScott Mayhew cup->cu_u.cu_msg.cm_cmd = Cld_Remove;
122611a60d15SScott Mayhew cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
122711a60d15SScott Mayhew memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
1228f3f80148SJeff Layton clp->cl_name.len);
1229f3f80148SJeff Layton
1230df60446cSScott Mayhew ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
1231f3f80148SJeff Layton if (!ret) {
123211a60d15SScott Mayhew ret = cup->cu_u.cu_msg.cm_status;
1233f3f80148SJeff Layton clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1234f3f80148SJeff Layton }
1235f3f80148SJeff Layton
1236f3f80148SJeff Layton free_cld_upcall(cup);
1237f3f80148SJeff Layton out_err:
1238f3f80148SJeff Layton if (ret)
1239f3f80148SJeff Layton printk(KERN_ERR "NFSD: Unable to remove client "
1240f3f80148SJeff Layton "record from stable storage: %d\n", ret);
1241f3f80148SJeff Layton }
1242f3f80148SJeff Layton
124374725959SScott Mayhew /*
124474725959SScott Mayhew * For older nfsdcld's that do not allow us to "slurp" the clients
124574725959SScott Mayhew * from the tracking database during startup.
124674725959SScott Mayhew *
124774725959SScott Mayhew * Check for presence of a record, and update its timestamp
124874725959SScott Mayhew */
1249f3f80148SJeff Layton static int
nfsd4_cld_check_v0(struct nfs4_client * clp)125074725959SScott Mayhew nfsd4_cld_check_v0(struct nfs4_client *clp)
1251f3f80148SJeff Layton {
1252f3f80148SJeff Layton int ret;
1253f3f80148SJeff Layton struct cld_upcall *cup;
1254c212cecfSStanislav Kinsbursky struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1255f3f80148SJeff Layton struct cld_net *cn = nn->cld_net;
1256f3f80148SJeff Layton
1257f3f80148SJeff Layton /* Don't upcall if one was already stored during this grace pd */
1258f3f80148SJeff Layton if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1259f3f80148SJeff Layton return 0;
1260f3f80148SJeff Layton
126111a60d15SScott Mayhew cup = alloc_cld_upcall(nn);
1262f3f80148SJeff Layton if (!cup) {
1263f3f80148SJeff Layton printk(KERN_ERR "NFSD: Unable to check client record on "
1264f3f80148SJeff Layton "stable storage: %d\n", -ENOMEM);
1265f3f80148SJeff Layton return -ENOMEM;
1266f3f80148SJeff Layton }
1267f3f80148SJeff Layton
126811a60d15SScott Mayhew cup->cu_u.cu_msg.cm_cmd = Cld_Check;
126911a60d15SScott Mayhew cup->cu_u.cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
127011a60d15SScott Mayhew memcpy(cup->cu_u.cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
1271f3f80148SJeff Layton clp->cl_name.len);
1272f3f80148SJeff Layton
1273df60446cSScott Mayhew ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
1274f3f80148SJeff Layton if (!ret) {
127511a60d15SScott Mayhew ret = cup->cu_u.cu_msg.cm_status;
1276f3f80148SJeff Layton set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1277f3f80148SJeff Layton }
1278f3f80148SJeff Layton
1279f3f80148SJeff Layton free_cld_upcall(cup);
1280f3f80148SJeff Layton return ret;
1281f3f80148SJeff Layton }
1282f3f80148SJeff Layton
128374725959SScott Mayhew /*
128474725959SScott Mayhew * For newer nfsdcld's that allow us to "slurp" the clients
128574725959SScott Mayhew * from the tracking database during startup.
128674725959SScott Mayhew *
128774725959SScott Mayhew * Check for presence of a record in the reclaim_str_hashtbl
128874725959SScott Mayhew */
128974725959SScott Mayhew static int
nfsd4_cld_check(struct nfs4_client * clp)129074725959SScott Mayhew nfsd4_cld_check(struct nfs4_client *clp)
129174725959SScott Mayhew {
129274725959SScott Mayhew struct nfs4_client_reclaim *crp;
129374725959SScott Mayhew struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
12948a9f4f41SScott Mayhew struct cld_net *cn = nn->cld_net;
12958a9f4f41SScott Mayhew int status;
12968a9f4f41SScott Mayhew char dname[HEXDIR_LEN];
12978a9f4f41SScott Mayhew struct xdr_netobj name;
129874725959SScott Mayhew
129974725959SScott Mayhew /* did we already find that this client is stable? */
130074725959SScott Mayhew if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
130174725959SScott Mayhew return 0;
130274725959SScott Mayhew
130374725959SScott Mayhew /* look for it in the reclaim hashtable otherwise */
130474725959SScott Mayhew crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
13058a9f4f41SScott Mayhew if (crp)
13068a9f4f41SScott Mayhew goto found;
13078a9f4f41SScott Mayhew
13088a9f4f41SScott Mayhew if (cn->cn_has_legacy) {
13098a9f4f41SScott Mayhew status = nfs4_make_rec_clidname(dname, &clp->cl_name);
13108a9f4f41SScott Mayhew if (status)
13118a9f4f41SScott Mayhew return -ENOENT;
13128a9f4f41SScott Mayhew
13138a9f4f41SScott Mayhew name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
13148a9f4f41SScott Mayhew if (!name.data) {
13158a9f4f41SScott Mayhew dprintk("%s: failed to allocate memory for name.data!\n",
13168a9f4f41SScott Mayhew __func__);
13178a9f4f41SScott Mayhew return -ENOENT;
13188a9f4f41SScott Mayhew }
13198a9f4f41SScott Mayhew name.len = HEXDIR_LEN;
13208a9f4f41SScott Mayhew crp = nfsd4_find_reclaim_client(name, nn);
13218a9f4f41SScott Mayhew kfree(name.data);
13228a9f4f41SScott Mayhew if (crp)
13238a9f4f41SScott Mayhew goto found;
13248a9f4f41SScott Mayhew
13258a9f4f41SScott Mayhew }
13268a9f4f41SScott Mayhew return -ENOENT;
13278a9f4f41SScott Mayhew found:
132874725959SScott Mayhew crp->cr_clp = clp;
132974725959SScott Mayhew return 0;
133074725959SScott Mayhew }
133174725959SScott Mayhew
133274725959SScott Mayhew static int
nfsd4_cld_check_v2(struct nfs4_client * clp)13336ee95d1cSScott Mayhew nfsd4_cld_check_v2(struct nfs4_client *clp)
13346ee95d1cSScott Mayhew {
13356ee95d1cSScott Mayhew struct nfs4_client_reclaim *crp;
13366ee95d1cSScott Mayhew struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
13376ee95d1cSScott Mayhew struct cld_net *cn = nn->cld_net;
13386ee95d1cSScott Mayhew int status;
13396ee95d1cSScott Mayhew char dname[HEXDIR_LEN];
13406ee95d1cSScott Mayhew struct xdr_netobj name;
13416ee95d1cSScott Mayhew struct crypto_shash *tfm = cn->cn_tfm;
13426ee95d1cSScott Mayhew struct xdr_netobj cksum;
13436ee95d1cSScott Mayhew char *principal = NULL;
13446ee95d1cSScott Mayhew
13456ee95d1cSScott Mayhew /* did we already find that this client is stable? */
13466ee95d1cSScott Mayhew if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
13476ee95d1cSScott Mayhew return 0;
13486ee95d1cSScott Mayhew
13496ee95d1cSScott Mayhew /* look for it in the reclaim hashtable otherwise */
13506ee95d1cSScott Mayhew crp = nfsd4_find_reclaim_client(clp->cl_name, nn);
13516ee95d1cSScott Mayhew if (crp)
13526ee95d1cSScott Mayhew goto found;
13536ee95d1cSScott Mayhew
13546ee95d1cSScott Mayhew if (cn->cn_has_legacy) {
13556ee95d1cSScott Mayhew status = nfs4_make_rec_clidname(dname, &clp->cl_name);
13566ee95d1cSScott Mayhew if (status)
13576ee95d1cSScott Mayhew return -ENOENT;
13586ee95d1cSScott Mayhew
13596ee95d1cSScott Mayhew name.data = kmemdup(dname, HEXDIR_LEN, GFP_KERNEL);
13606ee95d1cSScott Mayhew if (!name.data) {
13616ee95d1cSScott Mayhew dprintk("%s: failed to allocate memory for name.data\n",
13626ee95d1cSScott Mayhew __func__);
13636ee95d1cSScott Mayhew return -ENOENT;
13646ee95d1cSScott Mayhew }
13656ee95d1cSScott Mayhew name.len = HEXDIR_LEN;
13666ee95d1cSScott Mayhew crp = nfsd4_find_reclaim_client(name, nn);
13676ee95d1cSScott Mayhew kfree(name.data);
13686ee95d1cSScott Mayhew if (crp)
13696ee95d1cSScott Mayhew goto found;
13706ee95d1cSScott Mayhew
13716ee95d1cSScott Mayhew }
13726ee95d1cSScott Mayhew return -ENOENT;
13736ee95d1cSScott Mayhew found:
13746ee95d1cSScott Mayhew if (crp->cr_princhash.len) {
13756ee95d1cSScott Mayhew if (clp->cl_cred.cr_raw_principal)
13766ee95d1cSScott Mayhew principal = clp->cl_cred.cr_raw_principal;
13776ee95d1cSScott Mayhew else if (clp->cl_cred.cr_principal)
13786ee95d1cSScott Mayhew principal = clp->cl_cred.cr_principal;
13796ee95d1cSScott Mayhew if (principal == NULL)
13806ee95d1cSScott Mayhew return -ENOENT;
13816ee95d1cSScott Mayhew cksum.len = crypto_shash_digestsize(tfm);
13826ee95d1cSScott Mayhew cksum.data = kmalloc(cksum.len, GFP_KERNEL);
13836ee95d1cSScott Mayhew if (cksum.data == NULL)
13846ee95d1cSScott Mayhew return -ENOENT;
1385ea794db2SEric Biggers status = crypto_shash_tfm_digest(tfm, principal,
1386ea794db2SEric Biggers strlen(principal), cksum.data);
13876ee95d1cSScott Mayhew if (status) {
13886ee95d1cSScott Mayhew kfree(cksum.data);
13896ee95d1cSScott Mayhew return -ENOENT;
13906ee95d1cSScott Mayhew }
13916ee95d1cSScott Mayhew if (memcmp(crp->cr_princhash.data, cksum.data,
13926ee95d1cSScott Mayhew crp->cr_princhash.len)) {
13936ee95d1cSScott Mayhew kfree(cksum.data);
13946ee95d1cSScott Mayhew return -ENOENT;
13956ee95d1cSScott Mayhew }
13966ee95d1cSScott Mayhew kfree(cksum.data);
13976ee95d1cSScott Mayhew }
13986ee95d1cSScott Mayhew crp->cr_clp = clp;
13996ee95d1cSScott Mayhew return 0;
14006ee95d1cSScott Mayhew }
14016ee95d1cSScott Mayhew
14026ee95d1cSScott Mayhew static int
nfsd4_cld_grace_start(struct nfsd_net * nn)140374725959SScott Mayhew nfsd4_cld_grace_start(struct nfsd_net *nn)
140474725959SScott Mayhew {
140574725959SScott Mayhew int ret;
140674725959SScott Mayhew struct cld_upcall *cup;
140774725959SScott Mayhew struct cld_net *cn = nn->cld_net;
140874725959SScott Mayhew
140911a60d15SScott Mayhew cup = alloc_cld_upcall(nn);
141074725959SScott Mayhew if (!cup) {
141174725959SScott Mayhew ret = -ENOMEM;
141274725959SScott Mayhew goto out_err;
141374725959SScott Mayhew }
141474725959SScott Mayhew
141511a60d15SScott Mayhew cup->cu_u.cu_msg.cm_cmd = Cld_GraceStart;
1416df60446cSScott Mayhew ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
141774725959SScott Mayhew if (!ret)
141811a60d15SScott Mayhew ret = cup->cu_u.cu_msg.cm_status;
141974725959SScott Mayhew
142074725959SScott Mayhew free_cld_upcall(cup);
142174725959SScott Mayhew out_err:
142274725959SScott Mayhew if (ret)
142374725959SScott Mayhew dprintk("%s: Unable to get clients from userspace: %d\n",
142474725959SScott Mayhew __func__, ret);
142574725959SScott Mayhew return ret;
142674725959SScott Mayhew }
142774725959SScott Mayhew
142874725959SScott Mayhew /* For older nfsdcld's that need cm_gracetime */
1429f3f80148SJeff Layton static void
nfsd4_cld_grace_done_v0(struct nfsd_net * nn)143074725959SScott Mayhew nfsd4_cld_grace_done_v0(struct nfsd_net *nn)
1431f3f80148SJeff Layton {
1432f3f80148SJeff Layton int ret;
1433f3f80148SJeff Layton struct cld_upcall *cup;
1434f3f80148SJeff Layton struct cld_net *cn = nn->cld_net;
1435f3f80148SJeff Layton
143611a60d15SScott Mayhew cup = alloc_cld_upcall(nn);
1437f3f80148SJeff Layton if (!cup) {
1438f3f80148SJeff Layton ret = -ENOMEM;
1439f3f80148SJeff Layton goto out_err;
1440f3f80148SJeff Layton }
1441f3f80148SJeff Layton
144211a60d15SScott Mayhew cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
14439cc76801SArnd Bergmann cup->cu_u.cu_msg.cm_u.cm_gracetime = nn->boot_time;
1444df60446cSScott Mayhew ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
1445f3f80148SJeff Layton if (!ret)
144611a60d15SScott Mayhew ret = cup->cu_u.cu_msg.cm_status;
1447f3f80148SJeff Layton
1448f3f80148SJeff Layton free_cld_upcall(cup);
1449f3f80148SJeff Layton out_err:
1450f3f80148SJeff Layton if (ret)
1451f3f80148SJeff Layton printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
1452f3f80148SJeff Layton }
1453f3f80148SJeff Layton
145474725959SScott Mayhew /*
145574725959SScott Mayhew * For newer nfsdcld's that do not need cm_gracetime. We also need to call
145674725959SScott Mayhew * nfs4_release_reclaim() to clear out the reclaim_str_hashtbl.
145774725959SScott Mayhew */
145874725959SScott Mayhew static void
nfsd4_cld_grace_done(struct nfsd_net * nn)145974725959SScott Mayhew nfsd4_cld_grace_done(struct nfsd_net *nn)
146074725959SScott Mayhew {
146174725959SScott Mayhew int ret;
146274725959SScott Mayhew struct cld_upcall *cup;
146374725959SScott Mayhew struct cld_net *cn = nn->cld_net;
146474725959SScott Mayhew
146511a60d15SScott Mayhew cup = alloc_cld_upcall(nn);
146674725959SScott Mayhew if (!cup) {
146774725959SScott Mayhew ret = -ENOMEM;
146874725959SScott Mayhew goto out_err;
146974725959SScott Mayhew }
147074725959SScott Mayhew
147111a60d15SScott Mayhew cup->cu_u.cu_msg.cm_cmd = Cld_GraceDone;
1472df60446cSScott Mayhew ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
147374725959SScott Mayhew if (!ret)
147411a60d15SScott Mayhew ret = cup->cu_u.cu_msg.cm_status;
147574725959SScott Mayhew
147674725959SScott Mayhew free_cld_upcall(cup);
147774725959SScott Mayhew out_err:
147874725959SScott Mayhew nfs4_release_reclaim(nn);
147974725959SScott Mayhew if (ret)
148074725959SScott Mayhew printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
148174725959SScott Mayhew }
148274725959SScott Mayhew
148374725959SScott Mayhew static int
nfs4_cld_state_init(struct net * net)148474725959SScott Mayhew nfs4_cld_state_init(struct net *net)
148574725959SScott Mayhew {
148674725959SScott Mayhew struct nfsd_net *nn = net_generic(net, nfsd_net_id);
148774725959SScott Mayhew int i;
148874725959SScott Mayhew
148974725959SScott Mayhew nn->reclaim_str_hashtbl = kmalloc_array(CLIENT_HASH_SIZE,
149074725959SScott Mayhew sizeof(struct list_head),
149174725959SScott Mayhew GFP_KERNEL);
149274725959SScott Mayhew if (!nn->reclaim_str_hashtbl)
149374725959SScott Mayhew return -ENOMEM;
149474725959SScott Mayhew
149574725959SScott Mayhew for (i = 0; i < CLIENT_HASH_SIZE; i++)
149674725959SScott Mayhew INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]);
149774725959SScott Mayhew nn->reclaim_str_hashtbl_size = 0;
1498362063a5SScott Mayhew nn->track_reclaim_completes = true;
1499362063a5SScott Mayhew atomic_set(&nn->nr_reclaim_complete, 0);
150074725959SScott Mayhew
150174725959SScott Mayhew return 0;
150274725959SScott Mayhew }
150374725959SScott Mayhew
150474725959SScott Mayhew static void
nfs4_cld_state_shutdown(struct net * net)150574725959SScott Mayhew nfs4_cld_state_shutdown(struct net *net)
150674725959SScott Mayhew {
150774725959SScott Mayhew struct nfsd_net *nn = net_generic(net, nfsd_net_id);
150874725959SScott Mayhew
1509362063a5SScott Mayhew nn->track_reclaim_completes = false;
151074725959SScott Mayhew kfree(nn->reclaim_str_hashtbl);
151174725959SScott Mayhew }
151274725959SScott Mayhew
151386921607SScott Mayhew static bool
cld_running(struct nfsd_net * nn)151486921607SScott Mayhew cld_running(struct nfsd_net *nn)
151586921607SScott Mayhew {
151686921607SScott Mayhew struct cld_net *cn = nn->cld_net;
151786921607SScott Mayhew struct rpc_pipe *pipe = cn->cn_pipe;
151886921607SScott Mayhew
151986921607SScott Mayhew return pipe->nreaders || pipe->nwriters;
152086921607SScott Mayhew }
152186921607SScott Mayhew
152274725959SScott Mayhew static int
nfsd4_cld_get_version(struct nfsd_net * nn)152311a60d15SScott Mayhew nfsd4_cld_get_version(struct nfsd_net *nn)
152411a60d15SScott Mayhew {
152511a60d15SScott Mayhew int ret = 0;
152611a60d15SScott Mayhew struct cld_upcall *cup;
152711a60d15SScott Mayhew struct cld_net *cn = nn->cld_net;
152811a60d15SScott Mayhew uint8_t version;
152911a60d15SScott Mayhew
153011a60d15SScott Mayhew cup = alloc_cld_upcall(nn);
153111a60d15SScott Mayhew if (!cup) {
153211a60d15SScott Mayhew ret = -ENOMEM;
153311a60d15SScott Mayhew goto out_err;
153411a60d15SScott Mayhew }
153511a60d15SScott Mayhew cup->cu_u.cu_msg.cm_cmd = Cld_GetVersion;
1536df60446cSScott Mayhew ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_u.cu_msg, nn);
153711a60d15SScott Mayhew if (!ret) {
153811a60d15SScott Mayhew ret = cup->cu_u.cu_msg.cm_status;
153911a60d15SScott Mayhew if (ret)
154011a60d15SScott Mayhew goto out_free;
154111a60d15SScott Mayhew version = cup->cu_u.cu_msg.cm_u.cm_version;
154211a60d15SScott Mayhew dprintk("%s: userspace returned version %u\n",
154311a60d15SScott Mayhew __func__, version);
154411a60d15SScott Mayhew if (version < 1)
154511a60d15SScott Mayhew version = 1;
154611a60d15SScott Mayhew else if (version > CLD_UPCALL_VERSION)
154711a60d15SScott Mayhew version = CLD_UPCALL_VERSION;
154811a60d15SScott Mayhew
154911a60d15SScott Mayhew switch (version) {
155011a60d15SScott Mayhew case 1:
155111a60d15SScott Mayhew nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
155211a60d15SScott Mayhew break;
15536ee95d1cSScott Mayhew case 2:
15546ee95d1cSScott Mayhew nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v2;
15556ee95d1cSScott Mayhew break;
155611a60d15SScott Mayhew default:
155711a60d15SScott Mayhew break;
155811a60d15SScott Mayhew }
155911a60d15SScott Mayhew }
156011a60d15SScott Mayhew out_free:
156111a60d15SScott Mayhew free_cld_upcall(cup);
156211a60d15SScott Mayhew out_err:
156311a60d15SScott Mayhew if (ret)
156411a60d15SScott Mayhew dprintk("%s: Unable to get version from userspace: %d\n",
156511a60d15SScott Mayhew __func__, ret);
156611a60d15SScott Mayhew return ret;
156711a60d15SScott Mayhew }
156811a60d15SScott Mayhew
156911a60d15SScott Mayhew static int
nfsd4_cld_tracking_init(struct net * net)157074725959SScott Mayhew nfsd4_cld_tracking_init(struct net *net)
157174725959SScott Mayhew {
157274725959SScott Mayhew int status;
157374725959SScott Mayhew struct nfsd_net *nn = net_generic(net, nfsd_net_id);
157486921607SScott Mayhew bool running;
157586921607SScott Mayhew int retries = 10;
157618b9a895SScott Mayhew struct crypto_shash *tfm;
157774725959SScott Mayhew
157874725959SScott Mayhew status = nfs4_cld_state_init(net);
157974725959SScott Mayhew if (status)
158074725959SScott Mayhew return status;
158174725959SScott Mayhew
158286921607SScott Mayhew status = __nfsd4_init_cld_pipe(net);
158374725959SScott Mayhew if (status)
158474725959SScott Mayhew goto err_shutdown;
158574725959SScott Mayhew
158686921607SScott Mayhew /*
158786921607SScott Mayhew * rpc pipe upcalls take 30 seconds to time out, so we don't want to
158886921607SScott Mayhew * queue an upcall unless we know that nfsdcld is running (because we
158986921607SScott Mayhew * want this to fail fast so that nfsd4_client_tracking_init() can try
159086921607SScott Mayhew * the next client tracking method). nfsdcld should already be running
159186921607SScott Mayhew * before nfsd is started, so the wait here is for nfsdcld to open the
159286921607SScott Mayhew * pipefs file we just created.
159386921607SScott Mayhew */
159486921607SScott Mayhew while (!(running = cld_running(nn)) && retries--)
159586921607SScott Mayhew msleep(100);
159686921607SScott Mayhew
159786921607SScott Mayhew if (!running) {
159886921607SScott Mayhew status = -ETIMEDOUT;
159986921607SScott Mayhew goto err_remove;
160086921607SScott Mayhew }
160118b9a895SScott Mayhew tfm = crypto_alloc_shash("sha256", 0, 0);
160218b9a895SScott Mayhew if (IS_ERR(tfm)) {
160318b9a895SScott Mayhew status = PTR_ERR(tfm);
160418b9a895SScott Mayhew goto err_remove;
160518b9a895SScott Mayhew }
160618b9a895SScott Mayhew nn->cld_net->cn_tfm = tfm;
160786921607SScott Mayhew
160811a60d15SScott Mayhew status = nfsd4_cld_get_version(nn);
160911a60d15SScott Mayhew if (status == -EOPNOTSUPP)
161011a60d15SScott Mayhew pr_warn("NFSD: nfsdcld GetVersion upcall failed. Please upgrade nfsdcld.\n");
161111a60d15SScott Mayhew
161274725959SScott Mayhew status = nfsd4_cld_grace_start(nn);
161374725959SScott Mayhew if (status) {
161474725959SScott Mayhew if (status == -EOPNOTSUPP)
161511a60d15SScott Mayhew pr_warn("NFSD: nfsdcld GraceStart upcall failed. Please upgrade nfsdcld.\n");
161674725959SScott Mayhew nfs4_release_reclaim(nn);
161774725959SScott Mayhew goto err_remove;
161886921607SScott Mayhew } else
1619f988a7b7SPaul Menzel pr_info("NFSD: Using nfsdcld client tracking operations.\n");
162074725959SScott Mayhew return 0;
162174725959SScott Mayhew
162274725959SScott Mayhew err_remove:
162374725959SScott Mayhew nfsd4_remove_cld_pipe(net);
162474725959SScott Mayhew err_shutdown:
162574725959SScott Mayhew nfs4_cld_state_shutdown(net);
162674725959SScott Mayhew return status;
162774725959SScott Mayhew }
162874725959SScott Mayhew
162974725959SScott Mayhew static void
nfsd4_cld_tracking_exit(struct net * net)163074725959SScott Mayhew nfsd4_cld_tracking_exit(struct net *net)
163174725959SScott Mayhew {
163274725959SScott Mayhew struct nfsd_net *nn = net_generic(net, nfsd_net_id);
163374725959SScott Mayhew
163474725959SScott Mayhew nfs4_release_reclaim(nn);
163574725959SScott Mayhew nfsd4_remove_cld_pipe(net);
163674725959SScott Mayhew nfs4_cld_state_shutdown(net);
163774725959SScott Mayhew }
163874725959SScott Mayhew
163974725959SScott Mayhew /* For older nfsdcld's */
164074725959SScott Mayhew static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v0 = {
1641f3f80148SJeff Layton .init = nfsd4_init_cld_pipe,
1642f3f80148SJeff Layton .exit = nfsd4_remove_cld_pipe,
1643f3f80148SJeff Layton .create = nfsd4_cld_create,
1644f3f80148SJeff Layton .remove = nfsd4_cld_remove,
164574725959SScott Mayhew .check = nfsd4_cld_check_v0,
164674725959SScott Mayhew .grace_done = nfsd4_cld_grace_done_v0,
164711a60d15SScott Mayhew .version = 1,
164811a60d15SScott Mayhew .msglen = sizeof(struct cld_msg),
164974725959SScott Mayhew };
165074725959SScott Mayhew
165174725959SScott Mayhew /* For newer nfsdcld's */
165274725959SScott Mayhew static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
165374725959SScott Mayhew .init = nfsd4_cld_tracking_init,
165474725959SScott Mayhew .exit = nfsd4_cld_tracking_exit,
165574725959SScott Mayhew .create = nfsd4_cld_create,
165674725959SScott Mayhew .remove = nfsd4_cld_remove,
1657f3f80148SJeff Layton .check = nfsd4_cld_check,
1658f3f80148SJeff Layton .grace_done = nfsd4_cld_grace_done,
165911a60d15SScott Mayhew .version = 1,
166011a60d15SScott Mayhew .msglen = sizeof(struct cld_msg),
1661f3f80148SJeff Layton };
1662f3f80148SJeff Layton
16636ee95d1cSScott Mayhew /* v2 create/check ops include the principal, if available */
16646ee95d1cSScott Mayhew static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops_v2 = {
16656ee95d1cSScott Mayhew .init = nfsd4_cld_tracking_init,
16666ee95d1cSScott Mayhew .exit = nfsd4_cld_tracking_exit,
16676ee95d1cSScott Mayhew .create = nfsd4_cld_create_v2,
16686ee95d1cSScott Mayhew .remove = nfsd4_cld_remove,
16696ee95d1cSScott Mayhew .check = nfsd4_cld_check_v2,
16706ee95d1cSScott Mayhew .grace_done = nfsd4_cld_grace_done,
16716ee95d1cSScott Mayhew .version = 2,
16726ee95d1cSScott Mayhew .msglen = sizeof(struct cld_msg_v2),
16736ee95d1cSScott Mayhew };
16746ee95d1cSScott Mayhew
16752873d214SJeff Layton /* upcall via usermodehelper */
16762873d214SJeff Layton static char cltrack_prog[PATH_MAX] = "/sbin/nfsdcltrack";
16772873d214SJeff Layton module_param_string(cltrack_prog, cltrack_prog, sizeof(cltrack_prog),
16782873d214SJeff Layton S_IRUGO|S_IWUSR);
16792873d214SJeff Layton MODULE_PARM_DESC(cltrack_prog, "Path to the nfsdcltrack upcall program");
16802873d214SJeff Layton
1681f3aa7e24SJeff Layton static bool cltrack_legacy_disable;
1682f3aa7e24SJeff Layton module_param(cltrack_legacy_disable, bool, S_IRUGO|S_IWUSR);
1683f3aa7e24SJeff Layton MODULE_PARM_DESC(cltrack_legacy_disable,
1684f3aa7e24SJeff Layton "Disable legacy recoverydir conversion. Default: false");
1685f3aa7e24SJeff Layton
1686f3aa7e24SJeff Layton #define LEGACY_TOPDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_TOPDIR="
1687f3aa7e24SJeff Layton #define LEGACY_RECDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_RECDIR="
1688d4318acdSJeff Layton #define HAS_SESSION_ENV_PREFIX "NFSDCLTRACK_CLIENT_HAS_SESSION="
1689d4318acdSJeff Layton #define GRACE_START_ENV_PREFIX "NFSDCLTRACK_GRACE_START="
1690f3aa7e24SJeff Layton
1691f3aa7e24SJeff Layton static char *
nfsd4_cltrack_legacy_topdir(void)1692f3aa7e24SJeff Layton nfsd4_cltrack_legacy_topdir(void)
16932873d214SJeff Layton {
1694f3aa7e24SJeff Layton int copied;
1695f3aa7e24SJeff Layton size_t len;
1696f3aa7e24SJeff Layton char *result;
1697f3aa7e24SJeff Layton
1698f3aa7e24SJeff Layton if (cltrack_legacy_disable)
1699f3aa7e24SJeff Layton return NULL;
1700f3aa7e24SJeff Layton
1701f3aa7e24SJeff Layton len = strlen(LEGACY_TOPDIR_ENV_PREFIX) +
1702f3aa7e24SJeff Layton strlen(nfs4_recoverydir()) + 1;
1703f3aa7e24SJeff Layton
1704f3aa7e24SJeff Layton result = kmalloc(len, GFP_KERNEL);
1705f3aa7e24SJeff Layton if (!result)
1706f3aa7e24SJeff Layton return result;
1707f3aa7e24SJeff Layton
1708f3aa7e24SJeff Layton copied = snprintf(result, len, LEGACY_TOPDIR_ENV_PREFIX "%s",
1709f3aa7e24SJeff Layton nfs4_recoverydir());
1710f3aa7e24SJeff Layton if (copied >= len) {
1711f3aa7e24SJeff Layton /* just return nothing if output was truncated */
1712f3aa7e24SJeff Layton kfree(result);
1713f3aa7e24SJeff Layton return NULL;
1714f3aa7e24SJeff Layton }
1715f3aa7e24SJeff Layton
1716f3aa7e24SJeff Layton return result;
1717f3aa7e24SJeff Layton }
1718f3aa7e24SJeff Layton
1719f3aa7e24SJeff Layton static char *
nfsd4_cltrack_legacy_recdir(const struct xdr_netobj * name)17202216d449SJeff Layton nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name)
1721f3aa7e24SJeff Layton {
1722f3aa7e24SJeff Layton int copied;
1723f3aa7e24SJeff Layton size_t len;
1724f3aa7e24SJeff Layton char *result;
1725f3aa7e24SJeff Layton
1726f3aa7e24SJeff Layton if (cltrack_legacy_disable)
1727f3aa7e24SJeff Layton return NULL;
1728f3aa7e24SJeff Layton
1729f3aa7e24SJeff Layton /* +1 is for '/' between "topdir" and "recdir" */
1730f3aa7e24SJeff Layton len = strlen(LEGACY_RECDIR_ENV_PREFIX) +
1731f3aa7e24SJeff Layton strlen(nfs4_recoverydir()) + 1 + HEXDIR_LEN;
1732f3aa7e24SJeff Layton
1733f3aa7e24SJeff Layton result = kmalloc(len, GFP_KERNEL);
1734f3aa7e24SJeff Layton if (!result)
1735f3aa7e24SJeff Layton return result;
1736f3aa7e24SJeff Layton
17372216d449SJeff Layton copied = snprintf(result, len, LEGACY_RECDIR_ENV_PREFIX "%s/",
17382216d449SJeff Layton nfs4_recoverydir());
17392216d449SJeff Layton if (copied > (len - HEXDIR_LEN)) {
17402216d449SJeff Layton /* just return nothing if output will be truncated */
17412216d449SJeff Layton kfree(result);
17422216d449SJeff Layton return NULL;
17432216d449SJeff Layton }
17442216d449SJeff Layton
17452216d449SJeff Layton copied = nfs4_make_rec_clidname(result + copied, name);
17462216d449SJeff Layton if (copied) {
1747f3aa7e24SJeff Layton kfree(result);
1748f3aa7e24SJeff Layton return NULL;
1749f3aa7e24SJeff Layton }
1750f3aa7e24SJeff Layton
1751f3aa7e24SJeff Layton return result;
1752f3aa7e24SJeff Layton }
1753f3aa7e24SJeff Layton
1754d4318acdSJeff Layton static char *
nfsd4_cltrack_client_has_session(struct nfs4_client * clp)1755d4318acdSJeff Layton nfsd4_cltrack_client_has_session(struct nfs4_client *clp)
1756f3aa7e24SJeff Layton {
1757d4318acdSJeff Layton int copied;
1758d4318acdSJeff Layton size_t len;
1759d4318acdSJeff Layton char *result;
1760d4318acdSJeff Layton
1761d4318acdSJeff Layton /* prefix + Y/N character + terminating NULL */
1762d4318acdSJeff Layton len = strlen(HAS_SESSION_ENV_PREFIX) + 1 + 1;
1763d4318acdSJeff Layton
1764d4318acdSJeff Layton result = kmalloc(len, GFP_KERNEL);
1765d4318acdSJeff Layton if (!result)
1766d4318acdSJeff Layton return result;
1767d4318acdSJeff Layton
1768d4318acdSJeff Layton copied = snprintf(result, len, HAS_SESSION_ENV_PREFIX "%c",
1769d4318acdSJeff Layton clp->cl_minorversion ? 'Y' : 'N');
1770d4318acdSJeff Layton if (copied >= len) {
1771d4318acdSJeff Layton /* just return nothing if output was truncated */
1772d4318acdSJeff Layton kfree(result);
1773d4318acdSJeff Layton return NULL;
1774d4318acdSJeff Layton }
1775d4318acdSJeff Layton
1776d4318acdSJeff Layton return result;
1777d4318acdSJeff Layton }
1778d4318acdSJeff Layton
1779d4318acdSJeff Layton static char *
nfsd4_cltrack_grace_start(time64_t grace_start)17809cc76801SArnd Bergmann nfsd4_cltrack_grace_start(time64_t grace_start)
1781d4318acdSJeff Layton {
1782d4318acdSJeff Layton int copied;
1783d4318acdSJeff Layton size_t len;
1784d4318acdSJeff Layton char *result;
1785d4318acdSJeff Layton
1786d4318acdSJeff Layton /* prefix + max width of int64_t string + terminating NULL */
1787d4318acdSJeff Layton len = strlen(GRACE_START_ENV_PREFIX) + 22 + 1;
1788d4318acdSJeff Layton
1789d4318acdSJeff Layton result = kmalloc(len, GFP_KERNEL);
1790d4318acdSJeff Layton if (!result)
1791d4318acdSJeff Layton return result;
1792d4318acdSJeff Layton
17939cc76801SArnd Bergmann copied = snprintf(result, len, GRACE_START_ENV_PREFIX "%lld",
1794d4318acdSJeff Layton grace_start);
1795d4318acdSJeff Layton if (copied >= len) {
1796d4318acdSJeff Layton /* just return nothing if output was truncated */
1797d4318acdSJeff Layton kfree(result);
1798d4318acdSJeff Layton return NULL;
1799d4318acdSJeff Layton }
1800d4318acdSJeff Layton
1801d4318acdSJeff Layton return result;
1802d4318acdSJeff Layton }
1803d4318acdSJeff Layton
1804d4318acdSJeff Layton static int
nfsd4_umh_cltrack_upcall(char * cmd,char * arg,char * env0,char * env1)1805d4318acdSJeff Layton nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *env0, char *env1)
1806d4318acdSJeff Layton {
1807d4318acdSJeff Layton char *envp[3];
18082873d214SJeff Layton char *argv[4];
18092873d214SJeff Layton int ret;
18102873d214SJeff Layton
18112873d214SJeff Layton if (unlikely(!cltrack_prog[0])) {
18122873d214SJeff Layton dprintk("%s: cltrack_prog is disabled\n", __func__);
18132873d214SJeff Layton return -EACCES;
18142873d214SJeff Layton }
18152873d214SJeff Layton
18162873d214SJeff Layton dprintk("%s: cmd: %s\n", __func__, cmd);
18172873d214SJeff Layton dprintk("%s: arg: %s\n", __func__, arg ? arg : "(null)");
1818d4318acdSJeff Layton dprintk("%s: env0: %s\n", __func__, env0 ? env0 : "(null)");
1819d4318acdSJeff Layton dprintk("%s: env1: %s\n", __func__, env1 ? env1 : "(null)");
1820f3aa7e24SJeff Layton
1821d4318acdSJeff Layton envp[0] = env0;
1822d4318acdSJeff Layton envp[1] = env1;
1823d4318acdSJeff Layton envp[2] = NULL;
18242873d214SJeff Layton
18252873d214SJeff Layton argv[0] = (char *)cltrack_prog;
18262873d214SJeff Layton argv[1] = cmd;
18272873d214SJeff Layton argv[2] = arg;
18282873d214SJeff Layton argv[3] = NULL;
18292873d214SJeff Layton
18302873d214SJeff Layton ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
18312873d214SJeff Layton /*
18322873d214SJeff Layton * Disable the upcall mechanism if we're getting an ENOENT or EACCES
18332873d214SJeff Layton * error. The admin can re-enable it on the fly by using sysfs
18342873d214SJeff Layton * once the problem has been fixed.
18352873d214SJeff Layton */
18362873d214SJeff Layton if (ret == -ENOENT || ret == -EACCES) {
18372873d214SJeff Layton dprintk("NFSD: %s was not found or isn't executable (%d). "
18382873d214SJeff Layton "Setting cltrack_prog to blank string!",
18392873d214SJeff Layton cltrack_prog, ret);
18402873d214SJeff Layton cltrack_prog[0] = '\0';
18412873d214SJeff Layton }
18422873d214SJeff Layton dprintk("%s: %s return value: %d\n", __func__, cltrack_prog, ret);
18432873d214SJeff Layton
18442873d214SJeff Layton return ret;
18452873d214SJeff Layton }
18462873d214SJeff Layton
18472873d214SJeff Layton static char *
bin_to_hex_dup(const unsigned char * src,int srclen)18482873d214SJeff Layton bin_to_hex_dup(const unsigned char *src, int srclen)
18492873d214SJeff Layton {
185012b4157bSAndy Shevchenko char *buf;
18512873d214SJeff Layton
18522873d214SJeff Layton /* +1 for terminating NULL */
185312b4157bSAndy Shevchenko buf = kzalloc((srclen * 2) + 1, GFP_KERNEL);
18542873d214SJeff Layton if (!buf)
18552873d214SJeff Layton return buf;
18562873d214SJeff Layton
185712b4157bSAndy Shevchenko bin2hex(buf, src, srclen);
18582873d214SJeff Layton return buf;
18592873d214SJeff Layton }
18602873d214SJeff Layton
18612873d214SJeff Layton static int
nfsd4_umh_cltrack_init(struct net * net)1862d4318acdSJeff Layton nfsd4_umh_cltrack_init(struct net *net)
18632873d214SJeff Layton {
1864d4318acdSJeff Layton int ret;
1865d4318acdSJeff Layton struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1866d4318acdSJeff Layton char *grace_start = nfsd4_cltrack_grace_start(nn->boot_time);
1867d4318acdSJeff Layton
186871a50306SStanislav Kinsbursky /* XXX: The usermode helper s not working in container yet. */
186971a50306SStanislav Kinsbursky if (net != &init_net) {
187046cc8ba3SPaul Gortmaker pr_warn("NFSD: attempt to initialize umh client tracking in a container ignored.\n");
1871956ccef3SSudip Mukherjee kfree(grace_start);
187271a50306SStanislav Kinsbursky return -EINVAL;
187371a50306SStanislav Kinsbursky }
1874d4318acdSJeff Layton
1875d4318acdSJeff Layton ret = nfsd4_umh_cltrack_upcall("init", NULL, grace_start, NULL);
1876d4318acdSJeff Layton kfree(grace_start);
187786921607SScott Mayhew if (!ret)
1878f988a7b7SPaul Menzel pr_info("NFSD: Using UMH upcall client tracking operations.\n");
1879d4318acdSJeff Layton return ret;
18802873d214SJeff Layton }
18812873d214SJeff Layton
18822873d214SJeff Layton static void
nfsd4_cltrack_upcall_lock(struct nfs4_client * clp)1883d682e750SJeff Layton nfsd4_cltrack_upcall_lock(struct nfs4_client *clp)
1884d682e750SJeff Layton {
1885d682e750SJeff Layton wait_on_bit_lock(&clp->cl_flags, NFSD4_CLIENT_UPCALL_LOCK,
1886d682e750SJeff Layton TASK_UNINTERRUPTIBLE);
1887d682e750SJeff Layton }
1888d682e750SJeff Layton
1889d682e750SJeff Layton static void
nfsd4_cltrack_upcall_unlock(struct nfs4_client * clp)1890d682e750SJeff Layton nfsd4_cltrack_upcall_unlock(struct nfs4_client *clp)
1891d682e750SJeff Layton {
1892d682e750SJeff Layton smp_mb__before_atomic();
1893d682e750SJeff Layton clear_bit(NFSD4_CLIENT_UPCALL_LOCK, &clp->cl_flags);
1894d682e750SJeff Layton smp_mb__after_atomic();
1895d682e750SJeff Layton wake_up_bit(&clp->cl_flags, NFSD4_CLIENT_UPCALL_LOCK);
1896d682e750SJeff Layton }
1897d682e750SJeff Layton
1898d682e750SJeff Layton static void
nfsd4_umh_cltrack_create(struct nfs4_client * clp)18992873d214SJeff Layton nfsd4_umh_cltrack_create(struct nfs4_client *clp)
19002873d214SJeff Layton {
1901d4318acdSJeff Layton char *hexid, *has_session, *grace_start;
1902d4318acdSJeff Layton struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
19032873d214SJeff Layton
190465decb65SJeff Layton /*
190565decb65SJeff Layton * With v4.0 clients, there's little difference in outcome between a
190665decb65SJeff Layton * create and check operation, and we can end up calling into this
190765decb65SJeff Layton * function multiple times per client (once for each openowner). So,
190865decb65SJeff Layton * for v4.0 clients skip upcalling once the client has been recorded
190965decb65SJeff Layton * on stable storage.
191065decb65SJeff Layton *
191165decb65SJeff Layton * For v4.1+ clients, the outcome of the two operations is different,
191265decb65SJeff Layton * so we must ensure that we upcall for the create operation. v4.1+
191365decb65SJeff Layton * clients call this on RECLAIM_COMPLETE though, so we should only end
191465decb65SJeff Layton * up doing a single create upcall per client.
191565decb65SJeff Layton */
191665decb65SJeff Layton if (clp->cl_minorversion == 0 &&
191765decb65SJeff Layton test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
191865decb65SJeff Layton return;
191965decb65SJeff Layton
19202873d214SJeff Layton hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
19212873d214SJeff Layton if (!hexid) {
19222873d214SJeff Layton dprintk("%s: can't allocate memory for upcall!\n", __func__);
19232873d214SJeff Layton return;
19242873d214SJeff Layton }
1925d682e750SJeff Layton
1926d4318acdSJeff Layton has_session = nfsd4_cltrack_client_has_session(clp);
1927d4318acdSJeff Layton grace_start = nfsd4_cltrack_grace_start(nn->boot_time);
1928d682e750SJeff Layton
1929d682e750SJeff Layton nfsd4_cltrack_upcall_lock(clp);
1930788a7914SJeff Layton if (!nfsd4_umh_cltrack_upcall("create", hexid, has_session, grace_start))
1931788a7914SJeff Layton set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1932d682e750SJeff Layton nfsd4_cltrack_upcall_unlock(clp);
1933d682e750SJeff Layton
1934d4318acdSJeff Layton kfree(has_session);
1935d4318acdSJeff Layton kfree(grace_start);
19362873d214SJeff Layton kfree(hexid);
19372873d214SJeff Layton }
19382873d214SJeff Layton
19392873d214SJeff Layton static void
nfsd4_umh_cltrack_remove(struct nfs4_client * clp)19402873d214SJeff Layton nfsd4_umh_cltrack_remove(struct nfs4_client *clp)
19412873d214SJeff Layton {
19422873d214SJeff Layton char *hexid;
19432873d214SJeff Layton
1944788a7914SJeff Layton if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1945788a7914SJeff Layton return;
1946788a7914SJeff Layton
19472873d214SJeff Layton hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
19482873d214SJeff Layton if (!hexid) {
19492873d214SJeff Layton dprintk("%s: can't allocate memory for upcall!\n", __func__);
19502873d214SJeff Layton return;
19512873d214SJeff Layton }
1952d682e750SJeff Layton
1953d682e750SJeff Layton nfsd4_cltrack_upcall_lock(clp);
1954788a7914SJeff Layton if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags) &&
1955788a7914SJeff Layton nfsd4_umh_cltrack_upcall("remove", hexid, NULL, NULL) == 0)
1956788a7914SJeff Layton clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1957d682e750SJeff Layton nfsd4_cltrack_upcall_unlock(clp);
1958d682e750SJeff Layton
19592873d214SJeff Layton kfree(hexid);
19602873d214SJeff Layton }
19612873d214SJeff Layton
19622873d214SJeff Layton static int
nfsd4_umh_cltrack_check(struct nfs4_client * clp)19632873d214SJeff Layton nfsd4_umh_cltrack_check(struct nfs4_client *clp)
19642873d214SJeff Layton {
19652873d214SJeff Layton int ret;
1966d4318acdSJeff Layton char *hexid, *has_session, *legacy;
19672873d214SJeff Layton
1968788a7914SJeff Layton if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
1969788a7914SJeff Layton return 0;
1970788a7914SJeff Layton
19712873d214SJeff Layton hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len);
19722873d214SJeff Layton if (!hexid) {
19732873d214SJeff Layton dprintk("%s: can't allocate memory for upcall!\n", __func__);
19742873d214SJeff Layton return -ENOMEM;
19752873d214SJeff Layton }
1976d4318acdSJeff Layton
1977d4318acdSJeff Layton has_session = nfsd4_cltrack_client_has_session(clp);
19782216d449SJeff Layton legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name);
1979d682e750SJeff Layton
1980d682e750SJeff Layton nfsd4_cltrack_upcall_lock(clp);
1981788a7914SJeff Layton if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) {
1982788a7914SJeff Layton ret = 0;
1983788a7914SJeff Layton } else {
1984d4318acdSJeff Layton ret = nfsd4_umh_cltrack_upcall("check", hexid, has_session, legacy);
1985788a7914SJeff Layton if (ret == 0)
1986788a7914SJeff Layton set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
1987788a7914SJeff Layton }
1988d682e750SJeff Layton nfsd4_cltrack_upcall_unlock(clp);
1989d4318acdSJeff Layton kfree(has_session);
1990f3aa7e24SJeff Layton kfree(legacy);
19912873d214SJeff Layton kfree(hexid);
1992d4318acdSJeff Layton
19932873d214SJeff Layton return ret;
19942873d214SJeff Layton }
19952873d214SJeff Layton
19962873d214SJeff Layton static void
nfsd4_umh_cltrack_grace_done(struct nfsd_net * nn)1997919b8049SJeff Layton nfsd4_umh_cltrack_grace_done(struct nfsd_net *nn)
19982873d214SJeff Layton {
1999f3aa7e24SJeff Layton char *legacy;
20002873d214SJeff Layton char timestr[22]; /* FIXME: better way to determine max size? */
20012873d214SJeff Layton
20029cc76801SArnd Bergmann sprintf(timestr, "%lld", nn->boot_time);
2003f3aa7e24SJeff Layton legacy = nfsd4_cltrack_legacy_topdir();
2004d4318acdSJeff Layton nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy, NULL);
2005f3aa7e24SJeff Layton kfree(legacy);
20062873d214SJeff Layton }
20072873d214SJeff Layton
20087c582e4fSJulia Lawall static const struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = {
20092873d214SJeff Layton .init = nfsd4_umh_cltrack_init,
20102873d214SJeff Layton .exit = NULL,
20112873d214SJeff Layton .create = nfsd4_umh_cltrack_create,
20122873d214SJeff Layton .remove = nfsd4_umh_cltrack_remove,
20132873d214SJeff Layton .check = nfsd4_umh_cltrack_check,
20142873d214SJeff Layton .grace_done = nfsd4_umh_cltrack_grace_done,
201511a60d15SScott Mayhew .version = 1,
201611a60d15SScott Mayhew .msglen = 0,
20172873d214SJeff Layton };
20182873d214SJeff Layton
20192a4317c5SJeff Layton int
nfsd4_client_tracking_init(struct net * net)20202a4317c5SJeff Layton nfsd4_client_tracking_init(struct net *net)
20212a4317c5SJeff Layton {
20222a4317c5SJeff Layton int status;
2023f3f80148SJeff Layton struct path path;
20249a9c6478SStanislav Kinsbursky struct nfsd_net *nn = net_generic(net, nfsd_net_id);
20252a4317c5SJeff Layton
20262d77bf0aSJeff Layton /* just run the init if it the method is already decided */
20279a9c6478SStanislav Kinsbursky if (nn->client_tracking_ops)
20282d77bf0aSJeff Layton goto do_init;
20292d77bf0aSJeff Layton
203086921607SScott Mayhew /* First, try to use nfsdcld */
203186921607SScott Mayhew nn->client_tracking_ops = &nfsd4_cld_tracking_ops;
203286921607SScott Mayhew status = nn->client_tracking_ops->init(net);
203386921607SScott Mayhew if (!status)
203486921607SScott Mayhew return status;
203586921607SScott Mayhew if (status != -ETIMEDOUT) {
203686921607SScott Mayhew nn->client_tracking_ops = &nfsd4_cld_tracking_ops_v0;
203786921607SScott Mayhew status = nn->client_tracking_ops->init(net);
203886921607SScott Mayhew if (!status)
203986921607SScott Mayhew return status;
204086921607SScott Mayhew }
204186921607SScott Mayhew
20422d77bf0aSJeff Layton /*
204386921607SScott Mayhew * Next, try the UMH upcall.
20442d77bf0aSJeff Layton */
20459a9c6478SStanislav Kinsbursky nn->client_tracking_ops = &nfsd4_umh_tracking_ops;
20469a9c6478SStanislav Kinsbursky status = nn->client_tracking_ops->init(net);
20472d77bf0aSJeff Layton if (!status)
20482d77bf0aSJeff Layton return status;
20492d77bf0aSJeff Layton
20502d77bf0aSJeff Layton /*
205186921607SScott Mayhew * Finally, See if the recoverydir exists and is a directory.
205286921607SScott Mayhew * If it is, then use the legacy ops.
20532d77bf0aSJeff Layton */
20549a9c6478SStanislav Kinsbursky nn->client_tracking_ops = &nfsd4_legacy_tracking_ops;
2055f3f80148SJeff Layton status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path);
2056f3f80148SJeff Layton if (!status) {
2057e36cb0b8SDavid Howells status = d_is_dir(path.dentry);
2058f3f80148SJeff Layton path_put(&path);
205986921607SScott Mayhew if (!status) {
206086921607SScott Mayhew status = -EINVAL;
206186921607SScott Mayhew goto out;
206286921607SScott Mayhew }
2063f3f80148SJeff Layton }
20642a4317c5SJeff Layton
20652d77bf0aSJeff Layton do_init:
20669a9c6478SStanislav Kinsbursky status = nn->client_tracking_ops->init(net);
206786921607SScott Mayhew out:
20682a4317c5SJeff Layton if (status) {
20692a4317c5SJeff Layton printk(KERN_WARNING "NFSD: Unable to initialize client "
20702a4317c5SJeff Layton "recovery tracking! (%d)\n", status);
20719a9c6478SStanislav Kinsbursky nn->client_tracking_ops = NULL;
20722a4317c5SJeff Layton }
20732a4317c5SJeff Layton return status;
20742a4317c5SJeff Layton }
20752a4317c5SJeff Layton
20762a4317c5SJeff Layton void
nfsd4_client_tracking_exit(struct net * net)20772a4317c5SJeff Layton nfsd4_client_tracking_exit(struct net *net)
20782a4317c5SJeff Layton {
20799a9c6478SStanislav Kinsbursky struct nfsd_net *nn = net_generic(net, nfsd_net_id);
20809a9c6478SStanislav Kinsbursky
20819a9c6478SStanislav Kinsbursky if (nn->client_tracking_ops) {
20829a9c6478SStanislav Kinsbursky if (nn->client_tracking_ops->exit)
20839a9c6478SStanislav Kinsbursky nn->client_tracking_ops->exit(net);
20849a9c6478SStanislav Kinsbursky nn->client_tracking_ops = NULL;
20852a4317c5SJeff Layton }
20862a4317c5SJeff Layton }
20872a4317c5SJeff Layton
20882a4317c5SJeff Layton void
nfsd4_client_record_create(struct nfs4_client * clp)20892a4317c5SJeff Layton nfsd4_client_record_create(struct nfs4_client *clp)
20902a4317c5SJeff Layton {
20919a9c6478SStanislav Kinsbursky struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
20929a9c6478SStanislav Kinsbursky
20939a9c6478SStanislav Kinsbursky if (nn->client_tracking_ops)
20949a9c6478SStanislav Kinsbursky nn->client_tracking_ops->create(clp);
20952a4317c5SJeff Layton }
20962a4317c5SJeff Layton
20972a4317c5SJeff Layton void
nfsd4_client_record_remove(struct nfs4_client * clp)20982a4317c5SJeff Layton nfsd4_client_record_remove(struct nfs4_client *clp)
20992a4317c5SJeff Layton {
21009a9c6478SStanislav Kinsbursky struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
21019a9c6478SStanislav Kinsbursky
21029a9c6478SStanislav Kinsbursky if (nn->client_tracking_ops)
21039a9c6478SStanislav Kinsbursky nn->client_tracking_ops->remove(clp);
21042a4317c5SJeff Layton }
21052a4317c5SJeff Layton
21062a4317c5SJeff Layton int
nfsd4_client_record_check(struct nfs4_client * clp)21072a4317c5SJeff Layton nfsd4_client_record_check(struct nfs4_client *clp)
21082a4317c5SJeff Layton {
21099a9c6478SStanislav Kinsbursky struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
21109a9c6478SStanislav Kinsbursky
21119a9c6478SStanislav Kinsbursky if (nn->client_tracking_ops)
21129a9c6478SStanislav Kinsbursky return nn->client_tracking_ops->check(clp);
21132a4317c5SJeff Layton
21142a4317c5SJeff Layton return -EOPNOTSUPP;
21152a4317c5SJeff Layton }
21162a4317c5SJeff Layton
21172a4317c5SJeff Layton void
nfsd4_record_grace_done(struct nfsd_net * nn)2118919b8049SJeff Layton nfsd4_record_grace_done(struct nfsd_net *nn)
21192a4317c5SJeff Layton {
21209a9c6478SStanislav Kinsbursky if (nn->client_tracking_ops)
2121919b8049SJeff Layton nn->client_tracking_ops->grace_done(nn);
21222a4317c5SJeff Layton }
2123813fd320SJeff Layton
2124813fd320SJeff Layton static int
rpc_pipefs_event(struct notifier_block * nb,unsigned long event,void * ptr)2125813fd320SJeff Layton rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr)
2126813fd320SJeff Layton {
2127813fd320SJeff Layton struct super_block *sb = ptr;
2128813fd320SJeff Layton struct net *net = sb->s_fs_info;
2129813fd320SJeff Layton struct nfsd_net *nn = net_generic(net, nfsd_net_id);
2130813fd320SJeff Layton struct cld_net *cn = nn->cld_net;
2131813fd320SJeff Layton struct dentry *dentry;
2132813fd320SJeff Layton int ret = 0;
2133813fd320SJeff Layton
2134813fd320SJeff Layton if (!try_module_get(THIS_MODULE))
2135813fd320SJeff Layton return 0;
2136813fd320SJeff Layton
2137813fd320SJeff Layton if (!cn) {
2138813fd320SJeff Layton module_put(THIS_MODULE);
2139813fd320SJeff Layton return 0;
2140813fd320SJeff Layton }
2141813fd320SJeff Layton
2142813fd320SJeff Layton switch (event) {
2143813fd320SJeff Layton case RPC_PIPEFS_MOUNT:
2144813fd320SJeff Layton dentry = nfsd4_cld_register_sb(sb, cn->cn_pipe);
2145813fd320SJeff Layton if (IS_ERR(dentry)) {
2146813fd320SJeff Layton ret = PTR_ERR(dentry);
2147813fd320SJeff Layton break;
2148813fd320SJeff Layton }
2149813fd320SJeff Layton cn->cn_pipe->dentry = dentry;
2150813fd320SJeff Layton break;
2151813fd320SJeff Layton case RPC_PIPEFS_UMOUNT:
2152813fd320SJeff Layton if (cn->cn_pipe->dentry)
2153813fd320SJeff Layton nfsd4_cld_unregister_sb(cn->cn_pipe);
2154813fd320SJeff Layton break;
2155813fd320SJeff Layton default:
2156813fd320SJeff Layton ret = -ENOTSUPP;
2157813fd320SJeff Layton break;
2158813fd320SJeff Layton }
2159813fd320SJeff Layton module_put(THIS_MODULE);
2160813fd320SJeff Layton return ret;
2161813fd320SJeff Layton }
2162813fd320SJeff Layton
21632355c596SJ. Bruce Fields static struct notifier_block nfsd4_cld_block = {
2164813fd320SJeff Layton .notifier_call = rpc_pipefs_event,
2165813fd320SJeff Layton };
2166797a9d79SJeff Layton
2167797a9d79SJeff Layton int
register_cld_notifier(void)2168797a9d79SJeff Layton register_cld_notifier(void)
2169797a9d79SJeff Layton {
2170b10252c7SAlexander Sverdlin WARN_ON(!nfsd_net_id);
2171797a9d79SJeff Layton return rpc_pipefs_notifier_register(&nfsd4_cld_block);
2172797a9d79SJeff Layton }
2173797a9d79SJeff Layton
2174797a9d79SJeff Layton void
unregister_cld_notifier(void)2175797a9d79SJeff Layton unregister_cld_notifier(void)
2176797a9d79SJeff Layton {
2177797a9d79SJeff Layton rpc_pipefs_notifier_unregister(&nfsd4_cld_block);
2178797a9d79SJeff Layton }
2179