blob: e616f88b7f199b801da20f941455f7b87e21171f [file] [log] [blame]
NeilBrowna55370a2005-06-23 22:03:52 -07001/*
NeilBrowna55370a2005-06-23 22:03:52 -07002* Copyright (c) 2004 The Regents of the University of Michigan.
3* All rights reserved.
4*
5* Andy Adamson <andros@citi.umich.edu>
6*
7* Redistribution and use in source and binary forms, with or without
8* modification, are permitted provided that the following conditions
9* are met:
10*
11* 1. Redistributions of source code must retain the above copyright
12* notice, this list of conditions and the following disclaimer.
13* 2. Redistributions in binary form must reproduce the above copyright
14* notice, this list of conditions and the following disclaimer in the
15* documentation and/or other materials provided with the distribution.
16* 3. Neither the name of the University nor the names of its
17* contributors may be used to endorse or promote products derived
18* from this software without specific prior written permission.
19*
20* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
21* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31*
32*/
33
NeilBrown190e4fb2005-06-23 22:04:25 -070034#include <linux/file.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090035#include <linux/slab.h>
NeilBrown190e4fb2005-06-23 22:04:25 -070036#include <linux/namei.h>
NeilBrowna55370a2005-06-23 22:03:52 -070037#include <linux/crypto.h>
Alexey Dobriyane8edc6e2007-05-21 01:22:52 +040038#include <linux/sched.h>
Boaz Harrosh9a74af22009-12-03 20:30:56 +020039
40#include "nfsd.h"
41#include "state.h"
J. Bruce Fields0a3adad2009-11-04 18:12:35 -050042#include "vfs.h"
NeilBrowna55370a2005-06-23 22:03:52 -070043
44#define NFSDDBG_FACILITY NFSDDBG_PROC
45
Jeff Layton2a4317c2012-03-21 16:42:43 -040046/* Declarations */
47struct nfsd4_client_tracking_ops {
48 int (*init)(struct net *);
49 void (*exit)(struct net *);
50 void (*create)(struct nfs4_client *);
51 void (*remove)(struct nfs4_client *);
52 int (*check)(struct nfs4_client *);
53 void (*grace_done)(struct net *, time_t);
54};
55
NeilBrown190e4fb2005-06-23 22:04:25 -070056/* Globals */
Christoph Hellwige970a572010-03-22 17:32:14 +010057static struct file *rec_file;
J. Bruce Fields48483bf2011-08-26 20:40:28 -040058static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
Jeff Layton2a4317c2012-03-21 16:42:43 -040059static struct nfsd4_client_tracking_ops *client_tracking_ops;
NeilBrown190e4fb2005-06-23 22:04:25 -070060
David Howellsd84f4f92008-11-14 10:39:23 +110061static int
62nfs4_save_creds(const struct cred **original_creds)
NeilBrown190e4fb2005-06-23 22:04:25 -070063{
David Howellsd84f4f92008-11-14 10:39:23 +110064 struct cred *new;
65
66 new = prepare_creds();
67 if (!new)
68 return -ENOMEM;
69
70 new->fsuid = 0;
71 new->fsgid = 0;
72 *original_creds = override_creds(new);
73 put_cred(new);
74 return 0;
NeilBrown190e4fb2005-06-23 22:04:25 -070075}
76
77static void
David Howellsd84f4f92008-11-14 10:39:23 +110078nfs4_reset_creds(const struct cred *original)
NeilBrown190e4fb2005-06-23 22:04:25 -070079{
David Howellsd84f4f92008-11-14 10:39:23 +110080 revert_creds(original);
NeilBrown190e4fb2005-06-23 22:04:25 -070081}
82
NeilBrowna55370a2005-06-23 22:03:52 -070083static void
84md5_to_hex(char *out, char *md5)
85{
86 int i;
87
88 for (i=0; i<16; i++) {
89 unsigned char c = md5[i];
90
91 *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
92 *out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
93 }
94 *out = '\0';
95}
96
Al Virob37ad282006-10-19 23:28:59 -070097__be32
NeilBrowna55370a2005-06-23 22:03:52 -070098nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname)
99{
100 struct xdr_netobj cksum;
Herbert Xu35058682006-08-24 19:10:20 +1000101 struct hash_desc desc;
Jens Axboe60c74f82007-10-22 19:43:30 +0200102 struct scatterlist sg;
J. Bruce Fields3e772462011-08-10 19:07:33 -0400103 __be32 status = nfserr_jukebox;
NeilBrowna55370a2005-06-23 22:03:52 -0700104
105 dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
106 clname->len, clname->data);
Herbert Xu35058682006-08-24 19:10:20 +1000107 desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
108 desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
109 if (IS_ERR(desc.tfm))
110 goto out_no_tfm;
111 cksum.len = crypto_hash_digestsize(desc.tfm);
NeilBrowna55370a2005-06-23 22:03:52 -0700112 cksum.data = kmalloc(cksum.len, GFP_KERNEL);
113 if (cksum.data == NULL)
114 goto out;
NeilBrowna55370a2005-06-23 22:03:52 -0700115
Jens Axboe60c74f82007-10-22 19:43:30 +0200116 sg_init_one(&sg, clname->data, clname->len);
NeilBrowna55370a2005-06-23 22:03:52 -0700117
Jens Axboe60c74f82007-10-22 19:43:30 +0200118 if (crypto_hash_digest(&desc, &sg, sg.length, cksum.data))
Herbert Xu35058682006-08-24 19:10:20 +1000119 goto out;
NeilBrowna55370a2005-06-23 22:03:52 -0700120
121 md5_to_hex(dname, cksum.data);
122
NeilBrowna55370a2005-06-23 22:03:52 -0700123 status = nfs_ok;
124out:
Krishna Kumar2bd9e7b2008-10-20 11:47:09 +0530125 kfree(cksum.data);
Herbert Xu35058682006-08-24 19:10:20 +1000126 crypto_free_hash(desc.tfm);
127out_no_tfm:
NeilBrowna55370a2005-06-23 22:03:52 -0700128 return status;
129}
NeilBrown190e4fb2005-06-23 22:04:25 -0700130
Jeff Layton2a4317c2012-03-21 16:42:43 -0400131static void
132nfsd4_create_clid_dir(struct nfs4_client *clp)
NeilBrownc7b9a452005-06-23 22:04:30 -0700133{
David Howellsd84f4f92008-11-14 10:39:23 +1100134 const struct cred *original_cred;
NeilBrownc7b9a452005-06-23 22:04:30 -0700135 char *dname = clp->cl_recdir;
Christoph Hellwige970a572010-03-22 17:32:14 +0100136 struct dentry *dir, *dentry;
NeilBrownc7b9a452005-06-23 22:04:30 -0700137 int status;
138
139 dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname);
140
Jeff Laytona52d7262012-03-21 09:52:02 -0400141 if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
J. Bruce Fields7a6ef8c2012-01-05 15:38:41 -0500142 return;
J. Bruce Fieldsb8548892012-01-02 17:49:12 -0500143 if (!rec_file)
J. Bruce Fields7a6ef8c2012-01-05 15:38:41 -0500144 return;
David Howellsd84f4f92008-11-14 10:39:23 +1100145 status = nfs4_save_creds(&original_cred);
146 if (status < 0)
J. Bruce Fields7a6ef8c2012-01-05 15:38:41 -0500147 return;
NeilBrownc7b9a452005-06-23 22:04:30 -0700148
Christoph Hellwige970a572010-03-22 17:32:14 +0100149 dir = rec_file->f_path.dentry;
NeilBrownc7b9a452005-06-23 22:04:30 -0700150 /* lock the parent */
Christoph Hellwige970a572010-03-22 17:32:14 +0100151 mutex_lock(&dir->d_inode->i_mutex);
NeilBrownc7b9a452005-06-23 22:04:30 -0700152
Christoph Hellwige970a572010-03-22 17:32:14 +0100153 dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1);
NeilBrownc7b9a452005-06-23 22:04:30 -0700154 if (IS_ERR(dentry)) {
155 status = PTR_ERR(dentry);
156 goto out_unlock;
157 }
Boaz Harrosh6577aac2011-08-12 17:30:12 -0700158 if (dentry->d_inode)
J. Bruce Fieldsaec39682012-01-02 17:30:05 -0500159 /*
160 * In the 4.1 case, where we're called from
161 * reclaim_complete(), records from the previous reboot
162 * may still be left, so this is OK.
163 *
164 * In the 4.0 case, we should never get here; but we may
165 * as well be forgiving and just succeed silently.
166 */
NeilBrownc7b9a452005-06-23 22:04:30 -0700167 goto out_put;
Al Viroa561be72011-11-23 11:57:51 -0500168 status = mnt_want_write_file(rec_file);
Dave Hansen463c3192008-02-15 14:37:57 -0800169 if (status)
170 goto out_put;
Christoph Hellwige970a572010-03-22 17:32:14 +0100171 status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU);
Al Viro2a79f172011-12-09 08:06:57 -0500172 mnt_drop_write_file(rec_file);
NeilBrownc7b9a452005-06-23 22:04:30 -0700173out_put:
174 dput(dentry);
175out_unlock:
Christoph Hellwige970a572010-03-22 17:32:14 +0100176 mutex_unlock(&dir->d_inode->i_mutex);
Boaz Harrosh6577aac2011-08-12 17:30:12 -0700177 if (status == 0)
Christoph Hellwig8018ab02010-03-22 17:32:25 +0100178 vfs_fsync(rec_file, 0);
Boaz Harrosh6577aac2011-08-12 17:30:12 -0700179 else
180 printk(KERN_ERR "NFSD: failed to write recovery record"
181 " (err %d); please check that %s exists"
182 " and is writeable", status,
183 user_recovery_dirname);
David Howellsd84f4f92008-11-14 10:39:23 +1100184 nfs4_reset_creds(original_cred);
NeilBrownc7b9a452005-06-23 22:04:30 -0700185}
186
NeilBrown190e4fb2005-06-23 22:04:25 -0700187typedef int (recdir_func)(struct dentry *, struct dentry *);
188
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400189struct name_list {
190 char name[HEXDIR_LEN];
NeilBrown190e4fb2005-06-23 22:04:25 -0700191 struct list_head list;
192};
193
NeilBrown190e4fb2005-06-23 22:04:25 -0700194static int
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400195nfsd4_build_namelist(void *arg, const char *name, int namlen,
David Howellsafefdbb2006-10-03 01:13:46 -0700196 loff_t offset, u64 ino, unsigned int d_type)
NeilBrown190e4fb2005-06-23 22:04:25 -0700197{
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400198 struct list_head *names = arg;
199 struct name_list *entry;
NeilBrown190e4fb2005-06-23 22:04:25 -0700200
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400201 if (namlen != HEXDIR_LEN - 1)
Al Virob37ad282006-10-19 23:28:59 -0700202 return 0;
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400203 entry = kmalloc(sizeof(struct name_list), GFP_KERNEL);
204 if (entry == NULL)
NeilBrown190e4fb2005-06-23 22:04:25 -0700205 return -ENOMEM;
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400206 memcpy(entry->name, name, HEXDIR_LEN - 1);
207 entry->name[HEXDIR_LEN - 1] = '\0';
208 list_add(&entry->list, names);
NeilBrown190e4fb2005-06-23 22:04:25 -0700209 return 0;
210}
211
212static int
Al Viro5b4b2992011-07-07 18:43:21 -0400213nfsd4_list_rec_dir(recdir_func *f)
NeilBrown190e4fb2005-06-23 22:04:25 -0700214{
David Howellsd84f4f92008-11-14 10:39:23 +1100215 const struct cred *original_cred;
Al Viro5b4b2992011-07-07 18:43:21 -0400216 struct dentry *dir = rec_file->f_path.dentry;
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400217 LIST_HEAD(names);
NeilBrown190e4fb2005-06-23 22:04:25 -0700218 int status;
219
David Howellsd84f4f92008-11-14 10:39:23 +1100220 status = nfs4_save_creds(&original_cred);
221 if (status < 0)
222 return status;
NeilBrown190e4fb2005-06-23 22:04:25 -0700223
Al Viro5b4b2992011-07-07 18:43:21 -0400224 status = vfs_llseek(rec_file, 0, SEEK_SET);
225 if (status < 0) {
226 nfs4_reset_creds(original_cred);
227 return status;
228 }
229
230 status = vfs_readdir(rec_file, nfsd4_build_namelist, &names);
J. Bruce Fields8daed1e2009-05-11 16:10:19 -0400231 mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400232 while (!list_empty(&names)) {
Al Viro5b4b2992011-07-07 18:43:21 -0400233 struct name_list *entry;
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400234 entry = list_entry(names.next, struct name_list, list);
Al Viro5b4b2992011-07-07 18:43:21 -0400235 if (!status) {
236 struct dentry *dentry;
237 dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
238 if (IS_ERR(dentry)) {
239 status = PTR_ERR(dentry);
240 break;
241 }
242 status = f(dir, dentry);
243 dput(dentry);
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400244 }
J. Bruce Fields05f4f672009-03-13 16:02:59 -0400245 list_del(&entry->list);
246 kfree(entry);
NeilBrown190e4fb2005-06-23 22:04:25 -0700247 }
David Woodhouse2f9092e2009-04-20 23:18:37 +0100248 mutex_unlock(&dir->d_inode->i_mutex);
David Howellsd84f4f92008-11-14 10:39:23 +1100249 nfs4_reset_creds(original_cred);
NeilBrown190e4fb2005-06-23 22:04:25 -0700250 return status;
251}
252
253static int
NeilBrownc7b9a452005-06-23 22:04:30 -0700254nfsd4_unlink_clid_dir(char *name, int namlen)
255{
Christoph Hellwige970a572010-03-22 17:32:14 +0100256 struct dentry *dir, *dentry;
NeilBrownc7b9a452005-06-23 22:04:30 -0700257 int status;
258
259 dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name);
260
Christoph Hellwige970a572010-03-22 17:32:14 +0100261 dir = rec_file->f_path.dentry;
262 mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
263 dentry = lookup_one_len(name, dir, namlen);
NeilBrownc7b9a452005-06-23 22:04:30 -0700264 if (IS_ERR(dentry)) {
265 status = PTR_ERR(dentry);
David Woodhouse2f9092e2009-04-20 23:18:37 +0100266 goto out_unlock;
NeilBrownc7b9a452005-06-23 22:04:30 -0700267 }
268 status = -ENOENT;
269 if (!dentry->d_inode)
270 goto out;
Christoph Hellwige970a572010-03-22 17:32:14 +0100271 status = vfs_rmdir(dir->d_inode, dentry);
NeilBrownc7b9a452005-06-23 22:04:30 -0700272out:
273 dput(dentry);
David Woodhouse2f9092e2009-04-20 23:18:37 +0100274out_unlock:
Christoph Hellwige970a572010-03-22 17:32:14 +0100275 mutex_unlock(&dir->d_inode->i_mutex);
NeilBrownc7b9a452005-06-23 22:04:30 -0700276 return status;
277}
278
Jeff Layton2a4317c2012-03-21 16:42:43 -0400279static void
NeilBrownc7b9a452005-06-23 22:04:30 -0700280nfsd4_remove_clid_dir(struct nfs4_client *clp)
281{
David Howellsd84f4f92008-11-14 10:39:23 +1100282 const struct cred *original_cred;
NeilBrownc7b9a452005-06-23 22:04:30 -0700283 int status;
284
Jeff Laytona52d7262012-03-21 09:52:02 -0400285 if (!rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
NeilBrownc7b9a452005-06-23 22:04:30 -0700286 return;
287
Al Viroa561be72011-11-23 11:57:51 -0500288 status = mnt_want_write_file(rec_file);
Dave Hansen06227532008-02-15 14:37:34 -0800289 if (status)
290 goto out;
Jeff Laytona52d7262012-03-21 09:52:02 -0400291 clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
David Howellsd84f4f92008-11-14 10:39:23 +1100292
293 status = nfs4_save_creds(&original_cred);
294 if (status < 0)
295 goto out;
296
NeilBrownc7b9a452005-06-23 22:04:30 -0700297 status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1);
David Howellsd84f4f92008-11-14 10:39:23 +1100298 nfs4_reset_creds(original_cred);
NeilBrownc7b9a452005-06-23 22:04:30 -0700299 if (status == 0)
Christoph Hellwig8018ab02010-03-22 17:32:25 +0100300 vfs_fsync(rec_file, 0);
Al Viro2a79f172011-12-09 08:06:57 -0500301 mnt_drop_write_file(rec_file);
Dave Hansen06227532008-02-15 14:37:34 -0800302out:
NeilBrownc7b9a452005-06-23 22:04:30 -0700303 if (status)
304 printk("NFSD: Failed to remove expired client state directory"
305 " %.*s\n", HEXDIR_LEN, clp->cl_recdir);
NeilBrownc7b9a452005-06-23 22:04:30 -0700306}
307
308static int
309purge_old(struct dentry *parent, struct dentry *child)
310{
311 int status;
312
Andy Adamsona1bcecd2009-04-03 08:28:05 +0300313 if (nfs4_has_reclaimed_state(child->d_name.name, false))
Al Virob37ad282006-10-19 23:28:59 -0700314 return 0;
NeilBrownc7b9a452005-06-23 22:04:30 -0700315
David Woodhouse2f9092e2009-04-20 23:18:37 +0100316 status = vfs_rmdir(parent->d_inode, child);
NeilBrownc7b9a452005-06-23 22:04:30 -0700317 if (status)
318 printk("failed to remove client recovery directory %s\n",
319 child->d_name.name);
320 /* Keep trying, success or failure: */
Al Virob37ad282006-10-19 23:28:59 -0700321 return 0;
NeilBrownc7b9a452005-06-23 22:04:30 -0700322}
323
Jeff Layton2a4317c2012-03-21 16:42:43 -0400324static void
325nfsd4_recdir_purge_old(struct net *net, time_t boot_time)
326{
NeilBrownc7b9a452005-06-23 22:04:30 -0700327 int status;
328
Christoph Hellwige970a572010-03-22 17:32:14 +0100329 if (!rec_file)
NeilBrownc7b9a452005-06-23 22:04:30 -0700330 return;
Al Viroa561be72011-11-23 11:57:51 -0500331 status = mnt_want_write_file(rec_file);
Dave Hansen06227532008-02-15 14:37:34 -0800332 if (status)
333 goto out;
Al Viro5b4b2992011-07-07 18:43:21 -0400334 status = nfsd4_list_rec_dir(purge_old);
NeilBrownc7b9a452005-06-23 22:04:30 -0700335 if (status == 0)
Christoph Hellwig8018ab02010-03-22 17:32:25 +0100336 vfs_fsync(rec_file, 0);
Al Viro2a79f172011-12-09 08:06:57 -0500337 mnt_drop_write_file(rec_file);
Dave Hansen06227532008-02-15 14:37:34 -0800338out:
NeilBrownc7b9a452005-06-23 22:04:30 -0700339 if (status)
340 printk("nfsd4: failed to purge old clients from recovery"
Christoph Hellwige970a572010-03-22 17:32:14 +0100341 " directory %s\n", rec_file->f_path.dentry->d_name.name);
NeilBrownc7b9a452005-06-23 22:04:30 -0700342}
343
344static int
NeilBrown190e4fb2005-06-23 22:04:25 -0700345load_recdir(struct dentry *parent, struct dentry *child)
346{
347 if (child->d_name.len != HEXDIR_LEN - 1) {
348 printk("nfsd4: illegal name %s in recovery directory\n",
349 child->d_name.name);
350 /* Keep trying; maybe the others are OK: */
Al Virob37ad282006-10-19 23:28:59 -0700351 return 0;
NeilBrown190e4fb2005-06-23 22:04:25 -0700352 }
353 nfs4_client_to_reclaim(child->d_name.name);
Al Virob37ad282006-10-19 23:28:59 -0700354 return 0;
NeilBrown190e4fb2005-06-23 22:04:25 -0700355}
356
Jeff Layton2a4317c2012-03-21 16:42:43 -0400357static int
NeilBrown190e4fb2005-06-23 22:04:25 -0700358nfsd4_recdir_load(void) {
359 int status;
360
Christoph Hellwige970a572010-03-22 17:32:14 +0100361 if (!rec_file)
362 return 0;
363
Al Viro5b4b2992011-07-07 18:43:21 -0400364 status = nfsd4_list_rec_dir(load_recdir);
NeilBrown190e4fb2005-06-23 22:04:25 -0700365 if (status)
366 printk("nfsd4: failed loading clients from recovery"
Christoph Hellwige970a572010-03-22 17:32:14 +0100367 " directory %s\n", rec_file->f_path.dentry->d_name.name);
NeilBrown190e4fb2005-06-23 22:04:25 -0700368 return status;
369}
370
371/*
372 * Hold reference to the recovery directory.
373 */
374
Jeff Layton2a4317c2012-03-21 16:42:43 -0400375static int
376nfsd4_init_recdir(void)
NeilBrown190e4fb2005-06-23 22:04:25 -0700377{
David Howellsd84f4f92008-11-14 10:39:23 +1100378 const struct cred *original_cred;
379 int status;
NeilBrown190e4fb2005-06-23 22:04:25 -0700380
381 printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
J. Bruce Fields48483bf2011-08-26 20:40:28 -0400382 user_recovery_dirname);
NeilBrown190e4fb2005-06-23 22:04:25 -0700383
Christoph Hellwige970a572010-03-22 17:32:14 +0100384 BUG_ON(rec_file);
NeilBrown190e4fb2005-06-23 22:04:25 -0700385
David Howellsd84f4f92008-11-14 10:39:23 +1100386 status = nfs4_save_creds(&original_cred);
387 if (status < 0) {
388 printk("NFSD: Unable to change credentials to find recovery"
389 " directory: error %d\n",
390 status);
Jeff Layton2a4317c2012-03-21 16:42:43 -0400391 return status;
David Howellsd84f4f92008-11-14 10:39:23 +1100392 }
NeilBrown190e4fb2005-06-23 22:04:25 -0700393
J. Bruce Fields48483bf2011-08-26 20:40:28 -0400394 rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0);
Christoph Hellwige970a572010-03-22 17:32:14 +0100395 if (IS_ERR(rec_file)) {
J. Bruce Fieldsc2642ab2006-01-18 17:43:29 -0800396 printk("NFSD: unable to find recovery directory %s\n",
J. Bruce Fields48483bf2011-08-26 20:40:28 -0400397 user_recovery_dirname);
Jeff Layton2a4317c2012-03-21 16:42:43 -0400398 status = PTR_ERR(rec_file);
Christoph Hellwige970a572010-03-22 17:32:14 +0100399 rec_file = NULL;
400 }
NeilBrown190e4fb2005-06-23 22:04:25 -0700401
David Howellsd84f4f92008-11-14 10:39:23 +1100402 nfs4_reset_creds(original_cred);
Jeff Layton2a4317c2012-03-21 16:42:43 -0400403 return status;
NeilBrown190e4fb2005-06-23 22:04:25 -0700404}
405
Jeff Layton2a4317c2012-03-21 16:42:43 -0400406static int
407nfsd4_load_reboot_recovery_data(struct net *net)
408{
409 int status;
410
411 nfs4_lock_state();
412 status = nfsd4_init_recdir();
413 if (!status)
414 status = nfsd4_recdir_load();
415 nfs4_unlock_state();
416 if (status)
417 printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n");
418 return status;
419}
420
421static void
NeilBrown190e4fb2005-06-23 22:04:25 -0700422nfsd4_shutdown_recdir(void)
423{
Christoph Hellwige970a572010-03-22 17:32:14 +0100424 if (!rec_file)
NeilBrown190e4fb2005-06-23 22:04:25 -0700425 return;
Christoph Hellwige970a572010-03-22 17:32:14 +0100426 fput(rec_file);
427 rec_file = NULL;
NeilBrown190e4fb2005-06-23 22:04:25 -0700428}
J. Bruce Fields48483bf2011-08-26 20:40:28 -0400429
Jeff Layton2a4317c2012-03-21 16:42:43 -0400430static void
431nfsd4_legacy_tracking_exit(struct net *net)
432{
433 nfs4_release_reclaim();
434 nfsd4_shutdown_recdir();
435}
436
J. Bruce Fields48483bf2011-08-26 20:40:28 -0400437/*
438 * Change the NFSv4 recovery directory to recdir.
439 */
440int
441nfs4_reset_recoverydir(char *recdir)
442{
443 int status;
444 struct path path;
445
446 status = kern_path(recdir, LOOKUP_FOLLOW, &path);
447 if (status)
448 return status;
449 status = -ENOTDIR;
450 if (S_ISDIR(path.dentry->d_inode->i_mode)) {
451 strcpy(user_recovery_dirname, recdir);
452 status = 0;
453 }
454 path_put(&path);
455 return status;
456}
457
458char *
459nfs4_recoverydir(void)
460{
461 return user_recovery_dirname;
462}
Jeff Layton2a4317c2012-03-21 16:42:43 -0400463
464static int
465nfsd4_check_legacy_client(struct nfs4_client *clp)
466{
467 /* did we already find that this client is stable? */
468 if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
469 return 0;
470
471 /* look for it in the reclaim hashtable otherwise */
472 if (nfsd4_find_reclaim_client(clp)) {
473 set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
474 return 0;
475 }
476
477 return -ENOENT;
478}
479
480static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
481 .init = nfsd4_load_reboot_recovery_data,
482 .exit = nfsd4_legacy_tracking_exit,
483 .create = nfsd4_create_clid_dir,
484 .remove = nfsd4_remove_clid_dir,
485 .check = nfsd4_check_legacy_client,
486 .grace_done = nfsd4_recdir_purge_old,
487};
488
489int
490nfsd4_client_tracking_init(struct net *net)
491{
492 int status;
493
494 client_tracking_ops = &nfsd4_legacy_tracking_ops;
495
496 status = client_tracking_ops->init(net);
497 if (status) {
498 printk(KERN_WARNING "NFSD: Unable to initialize client "
499 "recovery tracking! (%d)\n", status);
500 client_tracking_ops = NULL;
501 }
502 return status;
503}
504
505void
506nfsd4_client_tracking_exit(struct net *net)
507{
508 if (client_tracking_ops) {
509 client_tracking_ops->exit(net);
510 client_tracking_ops = NULL;
511 }
512}
513
514void
515nfsd4_client_record_create(struct nfs4_client *clp)
516{
517 if (client_tracking_ops)
518 client_tracking_ops->create(clp);
519}
520
521void
522nfsd4_client_record_remove(struct nfs4_client *clp)
523{
524 if (client_tracking_ops)
525 client_tracking_ops->remove(clp);
526}
527
528int
529nfsd4_client_record_check(struct nfs4_client *clp)
530{
531 if (client_tracking_ops)
532 return client_tracking_ops->check(clp);
533
534 return -EOPNOTSUPP;
535}
536
537void
538nfsd4_record_grace_done(struct net *net, time_t boot_time)
539{
540 if (client_tracking_ops)
541 client_tracking_ops->grace_done(net, boot_time);
542}