| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 1 | /* | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 2 | *  Copyright (c) 2004 The Regents of the University of Michigan. | 
| Jeff Layton | f3f8014 | 2012-03-21 09:52:07 -0400 | [diff] [blame] | 3 | *  Copyright (c) 2012 Jeff Layton <jlayton@redhat.com> | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 4 | *  All rights reserved. | 
|  | 5 | * | 
|  | 6 | *  Andy Adamson <andros@citi.umich.edu> | 
|  | 7 | * | 
|  | 8 | *  Redistribution and use in source and binary forms, with or without | 
|  | 9 | *  modification, are permitted provided that the following conditions | 
|  | 10 | *  are met: | 
|  | 11 | * | 
|  | 12 | *  1. Redistributions of source code must retain the above copyright | 
|  | 13 | *     notice, this list of conditions and the following disclaimer. | 
|  | 14 | *  2. Redistributions in binary form must reproduce the above copyright | 
|  | 15 | *     notice, this list of conditions and the following disclaimer in the | 
|  | 16 | *     documentation and/or other materials provided with the distribution. | 
|  | 17 | *  3. Neither the name of the University nor the names of its | 
|  | 18 | *     contributors may be used to endorse or promote products derived | 
|  | 19 | *     from this software without specific prior written permission. | 
|  | 20 | * | 
|  | 21 | *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | 
|  | 22 | *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | 
|  | 23 | *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
|  | 24 | *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
|  | 25 | *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
|  | 26 | *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
|  | 27 | *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | 
|  | 28 | *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | 
|  | 29 | *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | 
|  | 30 | *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 
|  | 31 | *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | 32 | * | 
|  | 33 | */ | 
|  | 34 |  | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 35 | #include <linux/file.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 36 | #include <linux/slab.h> | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 37 | #include <linux/namei.h> | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 38 | #include <linux/crypto.h> | 
| Alexey Dobriyan | e8edc6e | 2007-05-21 01:22:52 +0400 | [diff] [blame] | 39 | #include <linux/sched.h> | 
| Jeff Layton | f3f8014 | 2012-03-21 09:52:07 -0400 | [diff] [blame] | 40 | #include <linux/fs.h> | 
| Jeff Layton | 813fd32 | 2012-03-21 09:52:08 -0400 | [diff] [blame] | 41 | #include <linux/module.h> | 
| Jeff Layton | f3f8014 | 2012-03-21 09:52:07 -0400 | [diff] [blame] | 42 | #include <net/net_namespace.h> | 
|  | 43 | #include <linux/sunrpc/rpc_pipe_fs.h> | 
|  | 44 | #include <linux/sunrpc/clnt.h> | 
|  | 45 | #include <linux/nfsd/cld.h> | 
| Boaz Harrosh | 9a74af2 | 2009-12-03 20:30:56 +0200 | [diff] [blame] | 46 |  | 
|  | 47 | #include "nfsd.h" | 
|  | 48 | #include "state.h" | 
| J. Bruce Fields | 0a3adad | 2009-11-04 18:12:35 -0500 | [diff] [blame] | 49 | #include "vfs.h" | 
| Jeff Layton | f3f8014 | 2012-03-21 09:52:07 -0400 | [diff] [blame] | 50 | #include "netns.h" | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 51 |  | 
|  | 52 | #define NFSDDBG_FACILITY                NFSDDBG_PROC | 
|  | 53 |  | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 54 | /* Declarations */ | 
|  | 55 | struct nfsd4_client_tracking_ops { | 
|  | 56 | int (*init)(struct net *); | 
|  | 57 | void (*exit)(struct net *); | 
|  | 58 | void (*create)(struct nfs4_client *); | 
|  | 59 | void (*remove)(struct nfs4_client *); | 
|  | 60 | int (*check)(struct nfs4_client *); | 
|  | 61 | void (*grace_done)(struct net *, time_t); | 
|  | 62 | }; | 
|  | 63 |  | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 64 | /* Globals */ | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 65 | static struct file *rec_file; | 
| J. Bruce Fields | 48483bf | 2011-08-26 20:40:28 -0400 | [diff] [blame] | 66 | static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 67 | static struct nfsd4_client_tracking_ops *client_tracking_ops; | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 68 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 69 | static int | 
|  | 70 | nfs4_save_creds(const struct cred **original_creds) | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 71 | { | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 72 | struct cred *new; | 
|  | 73 |  | 
|  | 74 | new = prepare_creds(); | 
|  | 75 | if (!new) | 
|  | 76 | return -ENOMEM; | 
|  | 77 |  | 
|  | 78 | new->fsuid = 0; | 
|  | 79 | new->fsgid = 0; | 
|  | 80 | *original_creds = override_creds(new); | 
|  | 81 | put_cred(new); | 
|  | 82 | return 0; | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 83 | } | 
|  | 84 |  | 
|  | 85 | static void | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 86 | nfs4_reset_creds(const struct cred *original) | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 87 | { | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 88 | revert_creds(original); | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 89 | } | 
|  | 90 |  | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 91 | static void | 
|  | 92 | md5_to_hex(char *out, char *md5) | 
|  | 93 | { | 
|  | 94 | int i; | 
|  | 95 |  | 
|  | 96 | for (i=0; i<16; i++) { | 
|  | 97 | unsigned char c = md5[i]; | 
|  | 98 |  | 
|  | 99 | *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1); | 
|  | 100 | *out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1); | 
|  | 101 | } | 
|  | 102 | *out = '\0'; | 
|  | 103 | } | 
|  | 104 |  | 
| Al Viro | b37ad28 | 2006-10-19 23:28:59 -0700 | [diff] [blame] | 105 | __be32 | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 106 | nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) | 
|  | 107 | { | 
|  | 108 | struct xdr_netobj cksum; | 
| Herbert Xu | 3505868 | 2006-08-24 19:10:20 +1000 | [diff] [blame] | 109 | struct hash_desc desc; | 
| Jens Axboe | 60c74f8 | 2007-10-22 19:43:30 +0200 | [diff] [blame] | 110 | struct scatterlist sg; | 
| J. Bruce Fields | 3e77246 | 2011-08-10 19:07:33 -0400 | [diff] [blame] | 111 | __be32 status = nfserr_jukebox; | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 112 |  | 
|  | 113 | dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n", | 
|  | 114 | clname->len, clname->data); | 
| Herbert Xu | 3505868 | 2006-08-24 19:10:20 +1000 | [diff] [blame] | 115 | desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP; | 
|  | 116 | desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); | 
|  | 117 | if (IS_ERR(desc.tfm)) | 
|  | 118 | goto out_no_tfm; | 
|  | 119 | cksum.len = crypto_hash_digestsize(desc.tfm); | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 120 | cksum.data = kmalloc(cksum.len, GFP_KERNEL); | 
|  | 121 | if (cksum.data == NULL) | 
|  | 122 | goto out; | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 123 |  | 
| Jens Axboe | 60c74f8 | 2007-10-22 19:43:30 +0200 | [diff] [blame] | 124 | sg_init_one(&sg, clname->data, clname->len); | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 125 |  | 
| Jens Axboe | 60c74f8 | 2007-10-22 19:43:30 +0200 | [diff] [blame] | 126 | if (crypto_hash_digest(&desc, &sg, sg.length, cksum.data)) | 
| Herbert Xu | 3505868 | 2006-08-24 19:10:20 +1000 | [diff] [blame] | 127 | goto out; | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 128 |  | 
|  | 129 | md5_to_hex(dname, cksum.data); | 
|  | 130 |  | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 131 | status = nfs_ok; | 
|  | 132 | out: | 
| Krishna Kumar | 2bd9e7b | 2008-10-20 11:47:09 +0530 | [diff] [blame] | 133 | kfree(cksum.data); | 
| Herbert Xu | 3505868 | 2006-08-24 19:10:20 +1000 | [diff] [blame] | 134 | crypto_free_hash(desc.tfm); | 
|  | 135 | out_no_tfm: | 
| NeilBrown | a55370a | 2005-06-23 22:03:52 -0700 | [diff] [blame] | 136 | return status; | 
|  | 137 | } | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 138 |  | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 139 | static void | 
|  | 140 | nfsd4_create_clid_dir(struct nfs4_client *clp) | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 141 | { | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 142 | const struct cred *original_cred; | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 143 | char *dname = clp->cl_recdir; | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 144 | struct dentry *dir, *dentry; | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 145 | int status; | 
|  | 146 |  | 
|  | 147 | dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); | 
|  | 148 |  | 
| Jeff Layton | a52d726 | 2012-03-21 09:52:02 -0400 | [diff] [blame] | 149 | if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 
| J. Bruce Fields | 7a6ef8c | 2012-01-05 15:38:41 -0500 | [diff] [blame] | 150 | return; | 
| J. Bruce Fields | b854889 | 2012-01-02 17:49:12 -0500 | [diff] [blame] | 151 | if (!rec_file) | 
| J. Bruce Fields | 7a6ef8c | 2012-01-05 15:38:41 -0500 | [diff] [blame] | 152 | return; | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 153 | status = nfs4_save_creds(&original_cred); | 
|  | 154 | if (status < 0) | 
| J. Bruce Fields | 7a6ef8c | 2012-01-05 15:38:41 -0500 | [diff] [blame] | 155 | return; | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 156 |  | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 157 | dir = rec_file->f_path.dentry; | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 158 | /* lock the parent */ | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 159 | mutex_lock(&dir->d_inode->i_mutex); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 160 |  | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 161 | dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 162 | if (IS_ERR(dentry)) { | 
|  | 163 | status = PTR_ERR(dentry); | 
|  | 164 | goto out_unlock; | 
|  | 165 | } | 
| Boaz Harrosh | 6577aac | 2011-08-12 17:30:12 -0700 | [diff] [blame] | 166 | if (dentry->d_inode) | 
| J. Bruce Fields | aec3968 | 2012-01-02 17:30:05 -0500 | [diff] [blame] | 167 | /* | 
|  | 168 | * In the 4.1 case, where we're called from | 
|  | 169 | * reclaim_complete(), records from the previous reboot | 
|  | 170 | * may still be left, so this is OK. | 
|  | 171 | * | 
|  | 172 | * In the 4.0 case, we should never get here; but we may | 
|  | 173 | * as well be forgiving and just succeed silently. | 
|  | 174 | */ | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 175 | goto out_put; | 
| Al Viro | a561be7 | 2011-11-23 11:57:51 -0500 | [diff] [blame] | 176 | status = mnt_want_write_file(rec_file); | 
| Dave Hansen | 463c319 | 2008-02-15 14:37:57 -0800 | [diff] [blame] | 177 | if (status) | 
|  | 178 | goto out_put; | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 179 | status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU); | 
| Al Viro | 2a79f17 | 2011-12-09 08:06:57 -0500 | [diff] [blame] | 180 | mnt_drop_write_file(rec_file); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 181 | out_put: | 
|  | 182 | dput(dentry); | 
|  | 183 | out_unlock: | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 184 | mutex_unlock(&dir->d_inode->i_mutex); | 
| Boaz Harrosh | 6577aac | 2011-08-12 17:30:12 -0700 | [diff] [blame] | 185 | if (status == 0) | 
| Christoph Hellwig | 8018ab0 | 2010-03-22 17:32:25 +0100 | [diff] [blame] | 186 | vfs_fsync(rec_file, 0); | 
| Boaz Harrosh | 6577aac | 2011-08-12 17:30:12 -0700 | [diff] [blame] | 187 | else | 
|  | 188 | printk(KERN_ERR "NFSD: failed to write recovery record" | 
|  | 189 | " (err %d); please check that %s exists" | 
|  | 190 | " and is writeable", status, | 
|  | 191 | user_recovery_dirname); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 192 | nfs4_reset_creds(original_cred); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 193 | } | 
|  | 194 |  | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 195 | typedef int (recdir_func)(struct dentry *, struct dentry *); | 
|  | 196 |  | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 197 | struct name_list { | 
|  | 198 | char name[HEXDIR_LEN]; | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 199 | struct list_head list; | 
|  | 200 | }; | 
|  | 201 |  | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 202 | static int | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 203 | nfsd4_build_namelist(void *arg, const char *name, int namlen, | 
| David Howells | afefdbb | 2006-10-03 01:13:46 -0700 | [diff] [blame] | 204 | loff_t offset, u64 ino, unsigned int d_type) | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 205 | { | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 206 | struct list_head *names = arg; | 
|  | 207 | struct name_list *entry; | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 208 |  | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 209 | if (namlen != HEXDIR_LEN - 1) | 
| Al Viro | b37ad28 | 2006-10-19 23:28:59 -0700 | [diff] [blame] | 210 | return 0; | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 211 | entry = kmalloc(sizeof(struct name_list), GFP_KERNEL); | 
|  | 212 | if (entry == NULL) | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 213 | return -ENOMEM; | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 214 | memcpy(entry->name, name, HEXDIR_LEN - 1); | 
|  | 215 | entry->name[HEXDIR_LEN - 1] = '\0'; | 
|  | 216 | list_add(&entry->list, names); | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 217 | return 0; | 
|  | 218 | } | 
|  | 219 |  | 
|  | 220 | static int | 
| Al Viro | 5b4b299 | 2011-07-07 18:43:21 -0400 | [diff] [blame] | 221 | nfsd4_list_rec_dir(recdir_func *f) | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 222 | { | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 223 | const struct cred *original_cred; | 
| Al Viro | 5b4b299 | 2011-07-07 18:43:21 -0400 | [diff] [blame] | 224 | struct dentry *dir = rec_file->f_path.dentry; | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 225 | LIST_HEAD(names); | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 226 | int status; | 
|  | 227 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 228 | status = nfs4_save_creds(&original_cred); | 
|  | 229 | if (status < 0) | 
|  | 230 | return status; | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 231 |  | 
| Al Viro | 5b4b299 | 2011-07-07 18:43:21 -0400 | [diff] [blame] | 232 | status = vfs_llseek(rec_file, 0, SEEK_SET); | 
|  | 233 | if (status < 0) { | 
|  | 234 | nfs4_reset_creds(original_cred); | 
|  | 235 | return status; | 
|  | 236 | } | 
|  | 237 |  | 
|  | 238 | status = vfs_readdir(rec_file, nfsd4_build_namelist, &names); | 
| J. Bruce Fields | 8daed1e | 2009-05-11 16:10:19 -0400 | [diff] [blame] | 239 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 240 | while (!list_empty(&names)) { | 
| Al Viro | 5b4b299 | 2011-07-07 18:43:21 -0400 | [diff] [blame] | 241 | struct name_list *entry; | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 242 | entry = list_entry(names.next, struct name_list, list); | 
| Al Viro | 5b4b299 | 2011-07-07 18:43:21 -0400 | [diff] [blame] | 243 | if (!status) { | 
|  | 244 | struct dentry *dentry; | 
|  | 245 | dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1); | 
|  | 246 | if (IS_ERR(dentry)) { | 
|  | 247 | status = PTR_ERR(dentry); | 
|  | 248 | break; | 
|  | 249 | } | 
|  | 250 | status = f(dir, dentry); | 
|  | 251 | dput(dentry); | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 252 | } | 
| J. Bruce Fields | 05f4f67 | 2009-03-13 16:02:59 -0400 | [diff] [blame] | 253 | list_del(&entry->list); | 
|  | 254 | kfree(entry); | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 255 | } | 
| David Woodhouse | 2f9092e | 2009-04-20 23:18:37 +0100 | [diff] [blame] | 256 | mutex_unlock(&dir->d_inode->i_mutex); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 257 | nfs4_reset_creds(original_cred); | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 258 | return status; | 
|  | 259 | } | 
|  | 260 |  | 
|  | 261 | static int | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 262 | nfsd4_unlink_clid_dir(char *name, int namlen) | 
|  | 263 | { | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 264 | struct dentry *dir, *dentry; | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 265 | int status; | 
|  | 266 |  | 
|  | 267 | dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name); | 
|  | 268 |  | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 269 | dir = rec_file->f_path.dentry; | 
|  | 270 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); | 
|  | 271 | dentry = lookup_one_len(name, dir, namlen); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 272 | if (IS_ERR(dentry)) { | 
|  | 273 | status = PTR_ERR(dentry); | 
| David Woodhouse | 2f9092e | 2009-04-20 23:18:37 +0100 | [diff] [blame] | 274 | goto out_unlock; | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 275 | } | 
|  | 276 | status = -ENOENT; | 
|  | 277 | if (!dentry->d_inode) | 
|  | 278 | goto out; | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 279 | status = vfs_rmdir(dir->d_inode, dentry); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 280 | out: | 
|  | 281 | dput(dentry); | 
| David Woodhouse | 2f9092e | 2009-04-20 23:18:37 +0100 | [diff] [blame] | 282 | out_unlock: | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 283 | mutex_unlock(&dir->d_inode->i_mutex); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 284 | return status; | 
|  | 285 | } | 
|  | 286 |  | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 287 | static void | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 288 | nfsd4_remove_clid_dir(struct nfs4_client *clp) | 
|  | 289 | { | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 290 | const struct cred *original_cred; | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 291 | int status; | 
|  | 292 |  | 
| Jeff Layton | a52d726 | 2012-03-21 09:52:02 -0400 | [diff] [blame] | 293 | if (!rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 294 | return; | 
|  | 295 |  | 
| Al Viro | a561be7 | 2011-11-23 11:57:51 -0500 | [diff] [blame] | 296 | status = mnt_want_write_file(rec_file); | 
| Dave Hansen | 0622753 | 2008-02-15 14:37:34 -0800 | [diff] [blame] | 297 | if (status) | 
|  | 298 | goto out; | 
| Jeff Layton | a52d726 | 2012-03-21 09:52:02 -0400 | [diff] [blame] | 299 | clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 300 |  | 
|  | 301 | status = nfs4_save_creds(&original_cred); | 
|  | 302 | if (status < 0) | 
|  | 303 | goto out; | 
|  | 304 |  | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 305 | status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1); | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 306 | nfs4_reset_creds(original_cred); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 307 | if (status == 0) | 
| Christoph Hellwig | 8018ab0 | 2010-03-22 17:32:25 +0100 | [diff] [blame] | 308 | vfs_fsync(rec_file, 0); | 
| Al Viro | 2a79f17 | 2011-12-09 08:06:57 -0500 | [diff] [blame] | 309 | mnt_drop_write_file(rec_file); | 
| Dave Hansen | 0622753 | 2008-02-15 14:37:34 -0800 | [diff] [blame] | 310 | out: | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 311 | if (status) | 
|  | 312 | printk("NFSD: Failed to remove expired client state directory" | 
|  | 313 | " %.*s\n", HEXDIR_LEN, clp->cl_recdir); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 314 | } | 
|  | 315 |  | 
|  | 316 | static int | 
|  | 317 | purge_old(struct dentry *parent, struct dentry *child) | 
|  | 318 | { | 
|  | 319 | int status; | 
|  | 320 |  | 
| Andy Adamson | a1bcecd | 2009-04-03 08:28:05 +0300 | [diff] [blame] | 321 | if (nfs4_has_reclaimed_state(child->d_name.name, false)) | 
| Al Viro | b37ad28 | 2006-10-19 23:28:59 -0700 | [diff] [blame] | 322 | return 0; | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 323 |  | 
| David Woodhouse | 2f9092e | 2009-04-20 23:18:37 +0100 | [diff] [blame] | 324 | status = vfs_rmdir(parent->d_inode, child); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 325 | if (status) | 
|  | 326 | printk("failed to remove client recovery directory %s\n", | 
|  | 327 | child->d_name.name); | 
|  | 328 | /* Keep trying, success or failure: */ | 
| Al Viro | b37ad28 | 2006-10-19 23:28:59 -0700 | [diff] [blame] | 329 | return 0; | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 330 | } | 
|  | 331 |  | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 332 | static void | 
|  | 333 | nfsd4_recdir_purge_old(struct net *net, time_t boot_time) | 
|  | 334 | { | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 335 | int status; | 
|  | 336 |  | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 337 | if (!rec_file) | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 338 | return; | 
| Al Viro | a561be7 | 2011-11-23 11:57:51 -0500 | [diff] [blame] | 339 | status = mnt_want_write_file(rec_file); | 
| Dave Hansen | 0622753 | 2008-02-15 14:37:34 -0800 | [diff] [blame] | 340 | if (status) | 
|  | 341 | goto out; | 
| Al Viro | 5b4b299 | 2011-07-07 18:43:21 -0400 | [diff] [blame] | 342 | status = nfsd4_list_rec_dir(purge_old); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 343 | if (status == 0) | 
| Christoph Hellwig | 8018ab0 | 2010-03-22 17:32:25 +0100 | [diff] [blame] | 344 | vfs_fsync(rec_file, 0); | 
| Al Viro | 2a79f17 | 2011-12-09 08:06:57 -0500 | [diff] [blame] | 345 | mnt_drop_write_file(rec_file); | 
| Dave Hansen | 0622753 | 2008-02-15 14:37:34 -0800 | [diff] [blame] | 346 | out: | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 347 | if (status) | 
|  | 348 | printk("nfsd4: failed to purge old clients from recovery" | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 349 | " directory %s\n", rec_file->f_path.dentry->d_name.name); | 
| NeilBrown | c7b9a45 | 2005-06-23 22:04:30 -0700 | [diff] [blame] | 350 | } | 
|  | 351 |  | 
|  | 352 | static int | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 353 | load_recdir(struct dentry *parent, struct dentry *child) | 
|  | 354 | { | 
|  | 355 | if (child->d_name.len != HEXDIR_LEN - 1) { | 
|  | 356 | printk("nfsd4: illegal name %s in recovery directory\n", | 
|  | 357 | child->d_name.name); | 
|  | 358 | /* Keep trying; maybe the others are OK: */ | 
| Al Viro | b37ad28 | 2006-10-19 23:28:59 -0700 | [diff] [blame] | 359 | return 0; | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 360 | } | 
|  | 361 | nfs4_client_to_reclaim(child->d_name.name); | 
| Al Viro | b37ad28 | 2006-10-19 23:28:59 -0700 | [diff] [blame] | 362 | return 0; | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 363 | } | 
|  | 364 |  | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 365 | static int | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 366 | nfsd4_recdir_load(void) { | 
|  | 367 | int status; | 
|  | 368 |  | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 369 | if (!rec_file) | 
|  | 370 | return 0; | 
|  | 371 |  | 
| Al Viro | 5b4b299 | 2011-07-07 18:43:21 -0400 | [diff] [blame] | 372 | status = nfsd4_list_rec_dir(load_recdir); | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 373 | if (status) | 
|  | 374 | printk("nfsd4: failed loading clients from recovery" | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 375 | " directory %s\n", rec_file->f_path.dentry->d_name.name); | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 376 | return status; | 
|  | 377 | } | 
|  | 378 |  | 
|  | 379 | /* | 
|  | 380 | * Hold reference to the recovery directory. | 
|  | 381 | */ | 
|  | 382 |  | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 383 | static int | 
|  | 384 | nfsd4_init_recdir(void) | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 385 | { | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 386 | const struct cred *original_cred; | 
|  | 387 | int status; | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 388 |  | 
|  | 389 | printk("NFSD: Using %s as the NFSv4 state recovery directory\n", | 
| J. Bruce Fields | 48483bf | 2011-08-26 20:40:28 -0400 | [diff] [blame] | 390 | user_recovery_dirname); | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 391 |  | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 392 | BUG_ON(rec_file); | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 393 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 394 | status = nfs4_save_creds(&original_cred); | 
|  | 395 | if (status < 0) { | 
|  | 396 | printk("NFSD: Unable to change credentials to find recovery" | 
|  | 397 | " directory: error %d\n", | 
|  | 398 | status); | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 399 | return status; | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 400 | } | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 401 |  | 
| J. Bruce Fields | 48483bf | 2011-08-26 20:40:28 -0400 | [diff] [blame] | 402 | rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 403 | if (IS_ERR(rec_file)) { | 
| J. Bruce Fields | c2642ab | 2006-01-18 17:43:29 -0800 | [diff] [blame] | 404 | printk("NFSD: unable to find recovery directory %s\n", | 
| J. Bruce Fields | 48483bf | 2011-08-26 20:40:28 -0400 | [diff] [blame] | 405 | user_recovery_dirname); | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 406 | status = PTR_ERR(rec_file); | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 407 | rec_file = NULL; | 
|  | 408 | } | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 409 |  | 
| David Howells | d84f4f9 | 2008-11-14 10:39:23 +1100 | [diff] [blame] | 410 | nfs4_reset_creds(original_cred); | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 411 | return status; | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 412 | } | 
|  | 413 |  | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 414 | static int | 
|  | 415 | nfsd4_load_reboot_recovery_data(struct net *net) | 
|  | 416 | { | 
|  | 417 | int status; | 
|  | 418 |  | 
| Jeff Layton | cc27e0d | 2012-03-21 09:52:09 -0400 | [diff] [blame] | 419 | /* XXX: The legacy code won't work in a container */ | 
|  | 420 | if (net != &init_net) { | 
|  | 421 | WARN(1, KERN_ERR "NFSD: attempt to initialize legacy client " | 
|  | 422 | "tracking in a container!\n"); | 
|  | 423 | return -EINVAL; | 
|  | 424 | } | 
|  | 425 |  | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 426 | nfs4_lock_state(); | 
|  | 427 | status = nfsd4_init_recdir(); | 
|  | 428 | if (!status) | 
|  | 429 | status = nfsd4_recdir_load(); | 
|  | 430 | nfs4_unlock_state(); | 
|  | 431 | if (status) | 
|  | 432 | printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n"); | 
|  | 433 | return status; | 
|  | 434 | } | 
|  | 435 |  | 
|  | 436 | static void | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 437 | nfsd4_shutdown_recdir(void) | 
|  | 438 | { | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 439 | if (!rec_file) | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 440 | return; | 
| Christoph Hellwig | e970a57 | 2010-03-22 17:32:14 +0100 | [diff] [blame] | 441 | fput(rec_file); | 
|  | 442 | rec_file = NULL; | 
| NeilBrown | 190e4fb | 2005-06-23 22:04:25 -0700 | [diff] [blame] | 443 | } | 
| J. Bruce Fields | 48483bf | 2011-08-26 20:40:28 -0400 | [diff] [blame] | 444 |  | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 445 | static void | 
|  | 446 | nfsd4_legacy_tracking_exit(struct net *net) | 
|  | 447 | { | 
|  | 448 | nfs4_release_reclaim(); | 
|  | 449 | nfsd4_shutdown_recdir(); | 
|  | 450 | } | 
|  | 451 |  | 
| J. Bruce Fields | 48483bf | 2011-08-26 20:40:28 -0400 | [diff] [blame] | 452 | /* | 
|  | 453 | * Change the NFSv4 recovery directory to recdir. | 
|  | 454 | */ | 
|  | 455 | int | 
|  | 456 | nfs4_reset_recoverydir(char *recdir) | 
|  | 457 | { | 
|  | 458 | int status; | 
|  | 459 | struct path path; | 
|  | 460 |  | 
|  | 461 | status = kern_path(recdir, LOOKUP_FOLLOW, &path); | 
|  | 462 | if (status) | 
|  | 463 | return status; | 
|  | 464 | status = -ENOTDIR; | 
|  | 465 | if (S_ISDIR(path.dentry->d_inode->i_mode)) { | 
|  | 466 | strcpy(user_recovery_dirname, recdir); | 
|  | 467 | status = 0; | 
|  | 468 | } | 
|  | 469 | path_put(&path); | 
|  | 470 | return status; | 
|  | 471 | } | 
|  | 472 |  | 
|  | 473 | char * | 
|  | 474 | nfs4_recoverydir(void) | 
|  | 475 | { | 
|  | 476 | return user_recovery_dirname; | 
|  | 477 | } | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 478 |  | 
|  | 479 | static int | 
|  | 480 | nfsd4_check_legacy_client(struct nfs4_client *clp) | 
|  | 481 | { | 
|  | 482 | /* did we already find that this client is stable? */ | 
|  | 483 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 
|  | 484 | return 0; | 
|  | 485 |  | 
|  | 486 | /* look for it in the reclaim hashtable otherwise */ | 
|  | 487 | if (nfsd4_find_reclaim_client(clp)) { | 
|  | 488 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | 
|  | 489 | return 0; | 
|  | 490 | } | 
|  | 491 |  | 
|  | 492 | return -ENOENT; | 
|  | 493 | } | 
|  | 494 |  | 
|  | 495 | static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = { | 
|  | 496 | .init		= nfsd4_load_reboot_recovery_data, | 
|  | 497 | .exit		= nfsd4_legacy_tracking_exit, | 
|  | 498 | .create		= nfsd4_create_clid_dir, | 
|  | 499 | .remove		= nfsd4_remove_clid_dir, | 
|  | 500 | .check		= nfsd4_check_legacy_client, | 
|  | 501 | .grace_done	= nfsd4_recdir_purge_old, | 
|  | 502 | }; | 
|  | 503 |  | 
| Jeff Layton | f3f8014 | 2012-03-21 09:52:07 -0400 | [diff] [blame] | 504 | /* Globals */ | 
|  | 505 | #define NFSD_PIPE_DIR		"nfsd" | 
|  | 506 | #define NFSD_CLD_PIPE		"cld" | 
|  | 507 |  | 
|  | 508 | /* per-net-ns structure for holding cld upcall info */ | 
|  | 509 | struct cld_net { | 
|  | 510 | struct rpc_pipe		*cn_pipe; | 
|  | 511 | spinlock_t		 cn_lock; | 
|  | 512 | struct list_head	 cn_list; | 
|  | 513 | unsigned int		 cn_xid; | 
|  | 514 | }; | 
|  | 515 |  | 
|  | 516 | struct cld_upcall { | 
|  | 517 | struct list_head	 cu_list; | 
|  | 518 | struct cld_net		*cu_net; | 
|  | 519 | struct task_struct	*cu_task; | 
|  | 520 | struct cld_msg		 cu_msg; | 
|  | 521 | }; | 
|  | 522 |  | 
|  | 523 | static int | 
|  | 524 | __cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg) | 
|  | 525 | { | 
|  | 526 | int ret; | 
|  | 527 | struct rpc_pipe_msg msg; | 
|  | 528 |  | 
|  | 529 | memset(&msg, 0, sizeof(msg)); | 
|  | 530 | msg.data = cmsg; | 
|  | 531 | msg.len = sizeof(*cmsg); | 
|  | 532 |  | 
|  | 533 | /* | 
|  | 534 | * Set task state before we queue the upcall. That prevents | 
|  | 535 | * wake_up_process in the downcall from racing with schedule. | 
|  | 536 | */ | 
|  | 537 | set_current_state(TASK_UNINTERRUPTIBLE); | 
|  | 538 | ret = rpc_queue_upcall(pipe, &msg); | 
|  | 539 | if (ret < 0) { | 
|  | 540 | set_current_state(TASK_RUNNING); | 
|  | 541 | goto out; | 
|  | 542 | } | 
|  | 543 |  | 
|  | 544 | schedule(); | 
|  | 545 | set_current_state(TASK_RUNNING); | 
|  | 546 |  | 
|  | 547 | if (msg.errno < 0) | 
|  | 548 | ret = msg.errno; | 
|  | 549 | out: | 
|  | 550 | return ret; | 
|  | 551 | } | 
|  | 552 |  | 
|  | 553 | static int | 
|  | 554 | cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg) | 
|  | 555 | { | 
|  | 556 | int ret; | 
|  | 557 |  | 
|  | 558 | /* | 
|  | 559 | * -EAGAIN occurs when pipe is closed and reopened while there are | 
|  | 560 | *  upcalls queued. | 
|  | 561 | */ | 
|  | 562 | do { | 
|  | 563 | ret = __cld_pipe_upcall(pipe, cmsg); | 
|  | 564 | } while (ret == -EAGAIN); | 
|  | 565 |  | 
|  | 566 | return ret; | 
|  | 567 | } | 
|  | 568 |  | 
|  | 569 | static ssize_t | 
|  | 570 | cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) | 
|  | 571 | { | 
|  | 572 | struct cld_upcall *tmp, *cup; | 
|  | 573 | struct cld_msg *cmsg = (struct cld_msg *)src; | 
|  | 574 | uint32_t xid; | 
|  | 575 | struct nfsd_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info, | 
|  | 576 | nfsd_net_id); | 
|  | 577 | struct cld_net *cn = nn->cld_net; | 
|  | 578 |  | 
|  | 579 | if (mlen != sizeof(*cmsg)) { | 
| Randy Dunlap | 8a7dc4b | 2012-04-30 12:25:31 -0700 | [diff] [blame] | 580 | dprintk("%s: got %zu bytes, expected %zu\n", __func__, mlen, | 
| Jeff Layton | f3f8014 | 2012-03-21 09:52:07 -0400 | [diff] [blame] | 581 | sizeof(*cmsg)); | 
|  | 582 | return -EINVAL; | 
|  | 583 | } | 
|  | 584 |  | 
|  | 585 | /* copy just the xid so we can try to find that */ | 
|  | 586 | if (copy_from_user(&xid, &cmsg->cm_xid, sizeof(xid)) != 0) { | 
|  | 587 | dprintk("%s: error when copying xid from userspace", __func__); | 
|  | 588 | return -EFAULT; | 
|  | 589 | } | 
|  | 590 |  | 
|  | 591 | /* walk the list and find corresponding xid */ | 
|  | 592 | cup = NULL; | 
|  | 593 | spin_lock(&cn->cn_lock); | 
|  | 594 | list_for_each_entry(tmp, &cn->cn_list, cu_list) { | 
|  | 595 | if (get_unaligned(&tmp->cu_msg.cm_xid) == xid) { | 
|  | 596 | cup = tmp; | 
|  | 597 | list_del_init(&cup->cu_list); | 
|  | 598 | break; | 
|  | 599 | } | 
|  | 600 | } | 
|  | 601 | spin_unlock(&cn->cn_lock); | 
|  | 602 |  | 
|  | 603 | /* couldn't find upcall? */ | 
|  | 604 | if (!cup) { | 
| Jeff Layton | 21f72c9 | 2012-03-28 07:36:01 -0400 | [diff] [blame] | 605 | dprintk("%s: couldn't find upcall -- xid=%u\n", __func__, xid); | 
| Jeff Layton | f3f8014 | 2012-03-21 09:52:07 -0400 | [diff] [blame] | 606 | return -EINVAL; | 
|  | 607 | } | 
|  | 608 |  | 
|  | 609 | if (copy_from_user(&cup->cu_msg, src, mlen) != 0) | 
|  | 610 | return -EFAULT; | 
|  | 611 |  | 
|  | 612 | wake_up_process(cup->cu_task); | 
|  | 613 | return mlen; | 
|  | 614 | } | 
|  | 615 |  | 
|  | 616 | static void | 
|  | 617 | cld_pipe_destroy_msg(struct rpc_pipe_msg *msg) | 
|  | 618 | { | 
|  | 619 | struct cld_msg *cmsg = msg->data; | 
|  | 620 | struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, | 
|  | 621 | cu_msg); | 
|  | 622 |  | 
|  | 623 | /* errno >= 0 means we got a downcall */ | 
|  | 624 | if (msg->errno >= 0) | 
|  | 625 | return; | 
|  | 626 |  | 
|  | 627 | wake_up_process(cup->cu_task); | 
|  | 628 | } | 
|  | 629 |  | 
|  | 630 | static const struct rpc_pipe_ops cld_upcall_ops = { | 
|  | 631 | .upcall		= rpc_pipe_generic_upcall, | 
|  | 632 | .downcall	= cld_pipe_downcall, | 
|  | 633 | .destroy_msg	= cld_pipe_destroy_msg, | 
|  | 634 | }; | 
|  | 635 |  | 
|  | 636 | static struct dentry * | 
|  | 637 | nfsd4_cld_register_sb(struct super_block *sb, struct rpc_pipe *pipe) | 
|  | 638 | { | 
|  | 639 | struct dentry *dir, *dentry; | 
|  | 640 |  | 
|  | 641 | dir = rpc_d_lookup_sb(sb, NFSD_PIPE_DIR); | 
|  | 642 | if (dir == NULL) | 
|  | 643 | return ERR_PTR(-ENOENT); | 
|  | 644 | dentry = rpc_mkpipe_dentry(dir, NFSD_CLD_PIPE, NULL, pipe); | 
|  | 645 | dput(dir); | 
|  | 646 | return dentry; | 
|  | 647 | } | 
|  | 648 |  | 
|  | 649 | static void | 
|  | 650 | nfsd4_cld_unregister_sb(struct rpc_pipe *pipe) | 
|  | 651 | { | 
|  | 652 | if (pipe->dentry) | 
|  | 653 | rpc_unlink(pipe->dentry); | 
|  | 654 | } | 
|  | 655 |  | 
|  | 656 | static struct dentry * | 
|  | 657 | nfsd4_cld_register_net(struct net *net, struct rpc_pipe *pipe) | 
|  | 658 | { | 
|  | 659 | struct super_block *sb; | 
|  | 660 | struct dentry *dentry; | 
|  | 661 |  | 
|  | 662 | sb = rpc_get_sb_net(net); | 
|  | 663 | if (!sb) | 
|  | 664 | return NULL; | 
|  | 665 | dentry = nfsd4_cld_register_sb(sb, pipe); | 
|  | 666 | rpc_put_sb_net(net); | 
|  | 667 | return dentry; | 
|  | 668 | } | 
|  | 669 |  | 
|  | 670 | static void | 
|  | 671 | nfsd4_cld_unregister_net(struct net *net, struct rpc_pipe *pipe) | 
|  | 672 | { | 
|  | 673 | struct super_block *sb; | 
|  | 674 |  | 
|  | 675 | sb = rpc_get_sb_net(net); | 
|  | 676 | if (sb) { | 
|  | 677 | nfsd4_cld_unregister_sb(pipe); | 
|  | 678 | rpc_put_sb_net(net); | 
|  | 679 | } | 
|  | 680 | } | 
|  | 681 |  | 
|  | 682 | /* Initialize rpc_pipefs pipe for communication with client tracking daemon */ | 
|  | 683 | static int | 
|  | 684 | nfsd4_init_cld_pipe(struct net *net) | 
|  | 685 | { | 
|  | 686 | int ret; | 
|  | 687 | struct dentry *dentry; | 
|  | 688 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | 
|  | 689 | struct cld_net *cn; | 
|  | 690 |  | 
|  | 691 | if (nn->cld_net) | 
|  | 692 | return 0; | 
|  | 693 |  | 
|  | 694 | cn = kzalloc(sizeof(*cn), GFP_KERNEL); | 
|  | 695 | if (!cn) { | 
|  | 696 | ret = -ENOMEM; | 
|  | 697 | goto err; | 
|  | 698 | } | 
|  | 699 |  | 
|  | 700 | cn->cn_pipe = rpc_mkpipe_data(&cld_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); | 
|  | 701 | if (IS_ERR(cn->cn_pipe)) { | 
|  | 702 | ret = PTR_ERR(cn->cn_pipe); | 
|  | 703 | goto err; | 
|  | 704 | } | 
|  | 705 | spin_lock_init(&cn->cn_lock); | 
|  | 706 | INIT_LIST_HEAD(&cn->cn_list); | 
|  | 707 |  | 
|  | 708 | dentry = nfsd4_cld_register_net(net, cn->cn_pipe); | 
|  | 709 | if (IS_ERR(dentry)) { | 
|  | 710 | ret = PTR_ERR(dentry); | 
|  | 711 | goto err_destroy_data; | 
|  | 712 | } | 
|  | 713 |  | 
|  | 714 | cn->cn_pipe->dentry = dentry; | 
|  | 715 | nn->cld_net = cn; | 
|  | 716 | return 0; | 
|  | 717 |  | 
|  | 718 | err_destroy_data: | 
|  | 719 | rpc_destroy_pipe_data(cn->cn_pipe); | 
|  | 720 | err: | 
|  | 721 | kfree(cn); | 
|  | 722 | printk(KERN_ERR "NFSD: unable to create nfsdcld upcall pipe (%d)\n", | 
|  | 723 | ret); | 
|  | 724 | return ret; | 
|  | 725 | } | 
|  | 726 |  | 
|  | 727 | static void | 
|  | 728 | nfsd4_remove_cld_pipe(struct net *net) | 
|  | 729 | { | 
|  | 730 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | 
|  | 731 | struct cld_net *cn = nn->cld_net; | 
|  | 732 |  | 
|  | 733 | nfsd4_cld_unregister_net(net, cn->cn_pipe); | 
|  | 734 | rpc_destroy_pipe_data(cn->cn_pipe); | 
|  | 735 | kfree(nn->cld_net); | 
|  | 736 | nn->cld_net = NULL; | 
|  | 737 | } | 
|  | 738 |  | 
|  | 739 | static struct cld_upcall * | 
|  | 740 | alloc_cld_upcall(struct cld_net *cn) | 
|  | 741 | { | 
|  | 742 | struct cld_upcall *new, *tmp; | 
|  | 743 |  | 
|  | 744 | new = kzalloc(sizeof(*new), GFP_KERNEL); | 
|  | 745 | if (!new) | 
|  | 746 | return new; | 
|  | 747 |  | 
|  | 748 | /* FIXME: hard cap on number in flight? */ | 
|  | 749 | restart_search: | 
|  | 750 | spin_lock(&cn->cn_lock); | 
|  | 751 | list_for_each_entry(tmp, &cn->cn_list, cu_list) { | 
|  | 752 | if (tmp->cu_msg.cm_xid == cn->cn_xid) { | 
|  | 753 | cn->cn_xid++; | 
|  | 754 | spin_unlock(&cn->cn_lock); | 
|  | 755 | goto restart_search; | 
|  | 756 | } | 
|  | 757 | } | 
|  | 758 | new->cu_task = current; | 
|  | 759 | new->cu_msg.cm_vers = CLD_UPCALL_VERSION; | 
|  | 760 | put_unaligned(cn->cn_xid++, &new->cu_msg.cm_xid); | 
|  | 761 | new->cu_net = cn; | 
|  | 762 | list_add(&new->cu_list, &cn->cn_list); | 
|  | 763 | spin_unlock(&cn->cn_lock); | 
|  | 764 |  | 
|  | 765 | dprintk("%s: allocated xid %u\n", __func__, new->cu_msg.cm_xid); | 
|  | 766 |  | 
|  | 767 | return new; | 
|  | 768 | } | 
|  | 769 |  | 
|  | 770 | static void | 
|  | 771 | free_cld_upcall(struct cld_upcall *victim) | 
|  | 772 | { | 
|  | 773 | struct cld_net *cn = victim->cu_net; | 
|  | 774 |  | 
|  | 775 | spin_lock(&cn->cn_lock); | 
|  | 776 | list_del(&victim->cu_list); | 
|  | 777 | spin_unlock(&cn->cn_lock); | 
|  | 778 | kfree(victim); | 
|  | 779 | } | 
|  | 780 |  | 
|  | 781 | /* Ask daemon to create a new record */ | 
|  | 782 | static void | 
|  | 783 | nfsd4_cld_create(struct nfs4_client *clp) | 
|  | 784 | { | 
|  | 785 | int ret; | 
|  | 786 | struct cld_upcall *cup; | 
|  | 787 | /* FIXME: determine net from clp */ | 
|  | 788 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | 
|  | 789 | struct cld_net *cn = nn->cld_net; | 
|  | 790 |  | 
|  | 791 | /* Don't upcall if it's already stored */ | 
|  | 792 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 
|  | 793 | return; | 
|  | 794 |  | 
|  | 795 | cup = alloc_cld_upcall(cn); | 
|  | 796 | if (!cup) { | 
|  | 797 | ret = -ENOMEM; | 
|  | 798 | goto out_err; | 
|  | 799 | } | 
|  | 800 |  | 
|  | 801 | cup->cu_msg.cm_cmd = Cld_Create; | 
|  | 802 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | 
|  | 803 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | 
|  | 804 | clp->cl_name.len); | 
|  | 805 |  | 
|  | 806 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | 
|  | 807 | if (!ret) { | 
|  | 808 | ret = cup->cu_msg.cm_status; | 
|  | 809 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | 
|  | 810 | } | 
|  | 811 |  | 
|  | 812 | free_cld_upcall(cup); | 
|  | 813 | out_err: | 
|  | 814 | if (ret) | 
|  | 815 | printk(KERN_ERR "NFSD: Unable to create client " | 
|  | 816 | "record on stable storage: %d\n", ret); | 
|  | 817 | } | 
|  | 818 |  | 
|  | 819 | /* Ask daemon to create a new record */ | 
|  | 820 | static void | 
|  | 821 | nfsd4_cld_remove(struct nfs4_client *clp) | 
|  | 822 | { | 
|  | 823 | int ret; | 
|  | 824 | struct cld_upcall *cup; | 
|  | 825 | /* FIXME: determine net from clp */ | 
|  | 826 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | 
|  | 827 | struct cld_net *cn = nn->cld_net; | 
|  | 828 |  | 
|  | 829 | /* Don't upcall if it's already removed */ | 
|  | 830 | if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 
|  | 831 | return; | 
|  | 832 |  | 
|  | 833 | cup = alloc_cld_upcall(cn); | 
|  | 834 | if (!cup) { | 
|  | 835 | ret = -ENOMEM; | 
|  | 836 | goto out_err; | 
|  | 837 | } | 
|  | 838 |  | 
|  | 839 | cup->cu_msg.cm_cmd = Cld_Remove; | 
|  | 840 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | 
|  | 841 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | 
|  | 842 | clp->cl_name.len); | 
|  | 843 |  | 
|  | 844 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | 
|  | 845 | if (!ret) { | 
|  | 846 | ret = cup->cu_msg.cm_status; | 
|  | 847 | clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | 
|  | 848 | } | 
|  | 849 |  | 
|  | 850 | free_cld_upcall(cup); | 
|  | 851 | out_err: | 
|  | 852 | if (ret) | 
|  | 853 | printk(KERN_ERR "NFSD: Unable to remove client " | 
|  | 854 | "record from stable storage: %d\n", ret); | 
|  | 855 | } | 
|  | 856 |  | 
|  | 857 | /* Check for presence of a record, and update its timestamp */ | 
|  | 858 | static int | 
|  | 859 | nfsd4_cld_check(struct nfs4_client *clp) | 
|  | 860 | { | 
|  | 861 | int ret; | 
|  | 862 | struct cld_upcall *cup; | 
|  | 863 | /* FIXME: determine net from clp */ | 
|  | 864 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | 
|  | 865 | struct cld_net *cn = nn->cld_net; | 
|  | 866 |  | 
|  | 867 | /* Don't upcall if one was already stored during this grace pd */ | 
|  | 868 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 
|  | 869 | return 0; | 
|  | 870 |  | 
|  | 871 | cup = alloc_cld_upcall(cn); | 
|  | 872 | if (!cup) { | 
|  | 873 | printk(KERN_ERR "NFSD: Unable to check client record on " | 
|  | 874 | "stable storage: %d\n", -ENOMEM); | 
|  | 875 | return -ENOMEM; | 
|  | 876 | } | 
|  | 877 |  | 
|  | 878 | cup->cu_msg.cm_cmd = Cld_Check; | 
|  | 879 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | 
|  | 880 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | 
|  | 881 | clp->cl_name.len); | 
|  | 882 |  | 
|  | 883 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | 
|  | 884 | if (!ret) { | 
|  | 885 | ret = cup->cu_msg.cm_status; | 
|  | 886 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | 
|  | 887 | } | 
|  | 888 |  | 
|  | 889 | free_cld_upcall(cup); | 
|  | 890 | return ret; | 
|  | 891 | } | 
|  | 892 |  | 
|  | 893 | static void | 
|  | 894 | nfsd4_cld_grace_done(struct net *net, time_t boot_time) | 
|  | 895 | { | 
|  | 896 | int ret; | 
|  | 897 | struct cld_upcall *cup; | 
|  | 898 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | 
|  | 899 | struct cld_net *cn = nn->cld_net; | 
|  | 900 |  | 
|  | 901 | cup = alloc_cld_upcall(cn); | 
|  | 902 | if (!cup) { | 
|  | 903 | ret = -ENOMEM; | 
|  | 904 | goto out_err; | 
|  | 905 | } | 
|  | 906 |  | 
|  | 907 | cup->cu_msg.cm_cmd = Cld_GraceDone; | 
|  | 908 | cup->cu_msg.cm_u.cm_gracetime = (int64_t)boot_time; | 
|  | 909 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | 
|  | 910 | if (!ret) | 
|  | 911 | ret = cup->cu_msg.cm_status; | 
|  | 912 |  | 
|  | 913 | free_cld_upcall(cup); | 
|  | 914 | out_err: | 
|  | 915 | if (ret) | 
|  | 916 | printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret); | 
|  | 917 | } | 
|  | 918 |  | 
|  | 919 | static struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = { | 
|  | 920 | .init		= nfsd4_init_cld_pipe, | 
|  | 921 | .exit		= nfsd4_remove_cld_pipe, | 
|  | 922 | .create		= nfsd4_cld_create, | 
|  | 923 | .remove		= nfsd4_cld_remove, | 
|  | 924 | .check		= nfsd4_cld_check, | 
|  | 925 | .grace_done	= nfsd4_cld_grace_done, | 
|  | 926 | }; | 
|  | 927 |  | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 928 | int | 
|  | 929 | nfsd4_client_tracking_init(struct net *net) | 
|  | 930 | { | 
|  | 931 | int status; | 
| Jeff Layton | f3f8014 | 2012-03-21 09:52:07 -0400 | [diff] [blame] | 932 | struct path path; | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 933 |  | 
| Jeff Layton | f3f8014 | 2012-03-21 09:52:07 -0400 | [diff] [blame] | 934 | if (!client_tracking_ops) { | 
|  | 935 | client_tracking_ops = &nfsd4_cld_tracking_ops; | 
|  | 936 | status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path); | 
|  | 937 | if (!status) { | 
|  | 938 | if (S_ISDIR(path.dentry->d_inode->i_mode)) | 
|  | 939 | client_tracking_ops = | 
|  | 940 | &nfsd4_legacy_tracking_ops; | 
|  | 941 | path_put(&path); | 
|  | 942 | } | 
|  | 943 | } | 
| Jeff Layton | 2a4317c | 2012-03-21 16:42:43 -0400 | [diff] [blame] | 944 |  | 
|  | 945 | status = client_tracking_ops->init(net); | 
|  | 946 | if (status) { | 
|  | 947 | printk(KERN_WARNING "NFSD: Unable to initialize client " | 
|  | 948 | "recovery tracking! (%d)\n", status); | 
|  | 949 | client_tracking_ops = NULL; | 
|  | 950 | } | 
|  | 951 | return status; | 
|  | 952 | } | 
|  | 953 |  | 
|  | 954 | void | 
|  | 955 | nfsd4_client_tracking_exit(struct net *net) | 
|  | 956 | { | 
|  | 957 | if (client_tracking_ops) { | 
|  | 958 | client_tracking_ops->exit(net); | 
|  | 959 | client_tracking_ops = NULL; | 
|  | 960 | } | 
|  | 961 | } | 
|  | 962 |  | 
|  | 963 | void | 
|  | 964 | nfsd4_client_record_create(struct nfs4_client *clp) | 
|  | 965 | { | 
|  | 966 | if (client_tracking_ops) | 
|  | 967 | client_tracking_ops->create(clp); | 
|  | 968 | } | 
|  | 969 |  | 
|  | 970 | void | 
|  | 971 | nfsd4_client_record_remove(struct nfs4_client *clp) | 
|  | 972 | { | 
|  | 973 | if (client_tracking_ops) | 
|  | 974 | client_tracking_ops->remove(clp); | 
|  | 975 | } | 
|  | 976 |  | 
|  | 977 | int | 
|  | 978 | nfsd4_client_record_check(struct nfs4_client *clp) | 
|  | 979 | { | 
|  | 980 | if (client_tracking_ops) | 
|  | 981 | return client_tracking_ops->check(clp); | 
|  | 982 |  | 
|  | 983 | return -EOPNOTSUPP; | 
|  | 984 | } | 
|  | 985 |  | 
|  | 986 | void | 
|  | 987 | nfsd4_record_grace_done(struct net *net, time_t boot_time) | 
|  | 988 | { | 
|  | 989 | if (client_tracking_ops) | 
|  | 990 | client_tracking_ops->grace_done(net, boot_time); | 
|  | 991 | } | 
| Jeff Layton | 813fd32 | 2012-03-21 09:52:08 -0400 | [diff] [blame] | 992 |  | 
|  | 993 | static int | 
|  | 994 | rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr) | 
|  | 995 | { | 
|  | 996 | struct super_block *sb = ptr; | 
|  | 997 | struct net *net = sb->s_fs_info; | 
|  | 998 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | 
|  | 999 | struct cld_net *cn = nn->cld_net; | 
|  | 1000 | struct dentry *dentry; | 
|  | 1001 | int ret = 0; | 
|  | 1002 |  | 
|  | 1003 | if (!try_module_get(THIS_MODULE)) | 
|  | 1004 | return 0; | 
|  | 1005 |  | 
|  | 1006 | if (!cn) { | 
|  | 1007 | module_put(THIS_MODULE); | 
|  | 1008 | return 0; | 
|  | 1009 | } | 
|  | 1010 |  | 
|  | 1011 | switch (event) { | 
|  | 1012 | case RPC_PIPEFS_MOUNT: | 
|  | 1013 | dentry = nfsd4_cld_register_sb(sb, cn->cn_pipe); | 
|  | 1014 | if (IS_ERR(dentry)) { | 
|  | 1015 | ret = PTR_ERR(dentry); | 
|  | 1016 | break; | 
|  | 1017 | } | 
|  | 1018 | cn->cn_pipe->dentry = dentry; | 
|  | 1019 | break; | 
|  | 1020 | case RPC_PIPEFS_UMOUNT: | 
|  | 1021 | if (cn->cn_pipe->dentry) | 
|  | 1022 | nfsd4_cld_unregister_sb(cn->cn_pipe); | 
|  | 1023 | break; | 
|  | 1024 | default: | 
|  | 1025 | ret = -ENOTSUPP; | 
|  | 1026 | break; | 
|  | 1027 | } | 
|  | 1028 | module_put(THIS_MODULE); | 
|  | 1029 | return ret; | 
|  | 1030 | } | 
|  | 1031 |  | 
|  | 1032 | struct notifier_block nfsd4_cld_block = { | 
|  | 1033 | .notifier_call = rpc_pipefs_event, | 
|  | 1034 | }; | 
| Jeff Layton | 797a9d7 | 2012-03-29 07:52:49 -0400 | [diff] [blame] | 1035 |  | 
|  | 1036 | int | 
|  | 1037 | register_cld_notifier(void) | 
|  | 1038 | { | 
|  | 1039 | return rpc_pipefs_notifier_register(&nfsd4_cld_block); | 
|  | 1040 | } | 
|  | 1041 |  | 
|  | 1042 | void | 
|  | 1043 | unregister_cld_notifier(void) | 
|  | 1044 | { | 
|  | 1045 | rpc_pipefs_notifier_unregister(&nfsd4_cld_block); | 
|  | 1046 | } |