aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@cse.unsw.edu.au>2005-06-24 01:04:25 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-24 03:06:35 -0400
commit190e4fbf96037e5e526ba3210f2bcc2a3b6fe964 (patch)
tree957fb35a6a2895622c4db0052160fd91d06153e8
parentcb36d6345752fa24827044c68e15f6708a40d9f6 (diff)
[PATCH] knfsd: nfsd4: initialize recovery directory
NFSv4 clients are required to know what state they have on the server so that they can reclaim it on server reboot. However, it is possible for pathalogical combinations of server reboots and network partitions to leave a client in a state where it cannot know whether it has lost its state on the server. For this reason, rfc3530 requires that we store some information about clients to stable storage. So we maintain a directory /var/lib/nfs/v4recovery with a subdirectory for each client with active state. We leave open the possibility of including files underneath each such subdirectory with information about the client, but for now the subdirectories are empty. We create a client subdirectory whenever a client makes its first non-reclaim open_confirm. We remove a client subdirectory whenever either a) its lease expires, or b) the grace period ends without it reclaiming anything. When handling reclaims, we allow the reclaim if and only if the client doing the reclaim has a subdirectory. This patch adds just the code to scan the recovery directory on nfsd startup. Signed-off-by: Andy Adamson <andros@citi.umich.edu> Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu> Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/nfsd/nfs4recover.c166
-rw-r--r--fs/nfsd/nfs4state.c18
-rw-r--r--include/linux/nfsd/state.h4
3 files changed, 186 insertions, 2 deletions
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index 841a305d7948..2dc9851a1d37 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -39,6 +39,9 @@
39#include <linux/nfs4.h> 39#include <linux/nfs4.h>
40#include <linux/nfsd/state.h> 40#include <linux/nfsd/state.h>
41#include <linux/nfsd/xdr4.h> 41#include <linux/nfsd/xdr4.h>
42#include <linux/param.h>
43#include <linux/file.h>
44#include <linux/namei.h>
42#include <asm/uaccess.h> 45#include <asm/uaccess.h>
43#include <asm/scatterlist.h> 46#include <asm/scatterlist.h>
44#include <linux/crypto.h> 47#include <linux/crypto.h>
@@ -46,6 +49,27 @@
46 49
47#define NFSDDBG_FACILITY NFSDDBG_PROC 50#define NFSDDBG_FACILITY NFSDDBG_PROC
48 51
52/* Globals */
53char recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
54static struct nameidata rec_dir;
55static int rec_dir_init = 0;
56
57static void
58nfs4_save_user(uid_t *saveuid, gid_t *savegid)
59{
60 *saveuid = current->fsuid;
61 *savegid = current->fsgid;
62 current->fsuid = 0;
63 current->fsgid = 0;
64}
65
66static void
67nfs4_reset_user(uid_t saveuid, gid_t savegid)
68{
69 current->fsuid = saveuid;
70 current->fsgid = savegid;
71}
72
49static void 73static void
50md5_to_hex(char *out, char *md5) 74md5_to_hex(char *out, char *md5)
51{ 75{
@@ -95,3 +119,145 @@ out:
95 crypto_free_tfm(tfm); 119 crypto_free_tfm(tfm);
96 return status; 120 return status;
97} 121}
122
123typedef int (recdir_func)(struct dentry *, struct dentry *);
124
125struct dentry_list {
126 struct dentry *dentry;
127 struct list_head list;
128};
129
130struct dentry_list_arg {
131 struct list_head dentries;
132 struct dentry *parent;
133};
134
135static int
136nfsd4_build_dentrylist(void *arg, const char *name, int namlen,
137 loff_t offset, ino_t ino, unsigned int d_type)
138{
139 struct dentry_list_arg *dla = arg;
140 struct list_head *dentries = &dla->dentries;
141 struct dentry *parent = dla->parent;
142 struct dentry *dentry;
143 struct dentry_list *child;
144
145 if (name && isdotent(name, namlen))
146 return nfs_ok;
147 dentry = lookup_one_len(name, parent, namlen);
148 if (IS_ERR(dentry))
149 return PTR_ERR(dentry);
150 child = kmalloc(sizeof(*child), GFP_KERNEL);
151 if (child == NULL)
152 return -ENOMEM;
153 child->dentry = dentry;
154 list_add(&child->list, dentries);
155 return 0;
156}
157
158static int
159nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
160{
161 struct file *filp;
162 struct dentry_list_arg dla = {
163 .parent = dir,
164 };
165 struct list_head *dentries = &dla.dentries;
166 struct dentry_list *child;
167 uid_t uid;
168 gid_t gid;
169 int status;
170
171 if (!rec_dir_init)
172 return 0;
173
174 nfs4_save_user(&uid, &gid);
175
176 filp = dentry_open(dget(dir), mntget(rec_dir.mnt),
177 O_RDWR);
178 status = PTR_ERR(filp);
179 if (IS_ERR(filp))
180 goto out;
181 INIT_LIST_HEAD(dentries);
182 status = vfs_readdir(filp, nfsd4_build_dentrylist, &dla);
183 fput(filp);
184 while (!list_empty(dentries)) {
185 child = list_entry(dentries->next, struct dentry_list, list);
186 status = f(dir, child->dentry);
187 if (status)
188 goto out;
189 list_del(&child->list);
190 dput(child->dentry);
191 kfree(child);
192 }
193out:
194 while (!list_empty(dentries)) {
195 child = list_entry(dentries->next, struct dentry_list, list);
196 list_del(&child->list);
197 dput(child->dentry);
198 kfree(child);
199 }
200 nfs4_reset_user(uid, gid);
201 return status;
202}
203
204static int
205load_recdir(struct dentry *parent, struct dentry *child)
206{
207 if (child->d_name.len != HEXDIR_LEN - 1) {
208 printk("nfsd4: illegal name %s in recovery directory\n",
209 child->d_name.name);
210 /* Keep trying; maybe the others are OK: */
211 return nfs_ok;
212 }
213 nfs4_client_to_reclaim(child->d_name.name);
214 return nfs_ok;
215}
216
217int
218nfsd4_recdir_load(void) {
219 int status;
220
221 status = nfsd4_list_rec_dir(rec_dir.dentry, load_recdir);
222 if (status)
223 printk("nfsd4: failed loading clients from recovery"
224 " directory %s\n", rec_dir.dentry->d_name.name);
225 return status;
226}
227
228/*
229 * Hold reference to the recovery directory.
230 */
231
232void
233nfsd4_init_recdir(char *rec_dirname)
234{
235 uid_t uid = 0;
236 gid_t gid = 0;
237 int status;
238
239 printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
240 rec_dirname);
241
242 BUG_ON(rec_dir_init);
243
244 nfs4_save_user(&uid, &gid);
245
246 status = path_lookup(rec_dirname, LOOKUP_FOLLOW, &rec_dir);
247 if (status == -ENOENT)
248 printk("NFSD: recovery directory %s doesn't exist\n",
249 rec_dirname);
250
251 if (!status)
252 rec_dir_init = 1;
253 nfs4_reset_user(uid, gid);
254}
255
256void
257nfsd4_shutdown_recdir(void)
258{
259 if (!rec_dir_init)
260 return;
261 rec_dir_init = 0;
262 path_release(&rec_dir);
263}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 0b47a97e953d..6b9d23c39afe 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -71,6 +71,7 @@ static stateid_t onestateid; /* bits all 1 */
71static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags); 71static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);
72static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid); 72static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid);
73static void release_stateid_lockowners(struct nfs4_stateid *open_stp); 73static void release_stateid_lockowners(struct nfs4_stateid *open_stp);
74extern char recovery_dirname[];
74 75
75/* Locking: 76/* Locking:
76 * 77 *
@@ -3091,8 +3092,8 @@ alloc_reclaim(void)
3091/* 3092/*
3092 * failure => all reset bets are off, nfserr_no_grace... 3093 * failure => all reset bets are off, nfserr_no_grace...
3093 */ 3094 */
3094static int 3095int
3095nfs4_client_to_reclaim(char *name) 3096nfs4_client_to_reclaim(const char *name)
3096{ 3097{
3097 unsigned int strhashval; 3098 unsigned int strhashval;
3098 struct nfs4_client_reclaim *crp = NULL; 3099 struct nfs4_client_reclaim *crp = NULL;
@@ -3202,6 +3203,17 @@ nfs4_state_init(void)
3202 reclaim_str_hashtbl_size = 0; 3203 reclaim_str_hashtbl_size = 0;
3203} 3204}
3204 3205
3206static void
3207nfsd4_load_reboot_recovery_data(void)
3208{
3209 int status;
3210
3211 nfsd4_init_recdir(recovery_dirname);
3212 status = nfsd4_recdir_load();
3213 if (status)
3214 printk("NFSD: Failure reading reboot recovery data\n");
3215}
3216
3205/* initialization to perform when the nfsd service is started: */ 3217/* initialization to perform when the nfsd service is started: */
3206 3218
3207static void 3219static void
@@ -3228,6 +3240,7 @@ nfs4_state_start(void)
3228 status = nfsd4_init_slabs(); 3240 status = nfsd4_init_slabs();
3229 if (status) 3241 if (status)
3230 return status; 3242 return status;
3243 nfsd4_load_reboot_recovery_data();
3231 __nfs4_state_start(); 3244 __nfs4_state_start();
3232 nfs4_init = 1; 3245 nfs4_init = 1;
3233 return 0; 3246 return 0;
@@ -3286,6 +3299,7 @@ __nfs4_state_shutdown(void)
3286 cancel_delayed_work(&laundromat_work); 3299 cancel_delayed_work(&laundromat_work);
3287 flush_workqueue(laundry_wq); 3300 flush_workqueue(laundry_wq);
3288 destroy_workqueue(laundry_wq); 3301 destroy_workqueue(laundry_wq);
3302 nfsd4_shutdown_recdir();
3289 nfs4_init = 0; 3303 nfs4_init = 0;
3290} 3304}
3291 3305
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h
index 83d29ec03a58..19481ab122df 100644
--- a/include/linux/nfsd/state.h
+++ b/include/linux/nfsd/state.h
@@ -278,6 +278,10 @@ extern void nfsd4_probe_callback(struct nfs4_client *clp);
278extern void nfsd4_cb_recall(struct nfs4_delegation *dp); 278extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
279extern void nfs4_put_delegation(struct nfs4_delegation *dp); 279extern void nfs4_put_delegation(struct nfs4_delegation *dp);
280extern int nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); 280extern int nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
281extern void nfsd4_init_recdir(char *recdir_name);
282extern int nfsd4_recdir_load(void);
283extern void nfsd4_shutdown_recdir(void);
284extern int nfs4_client_to_reclaim(const char *name);
281 285
282static inline void 286static inline void
283nfs4_put_stateowner(struct nfs4_stateowner *so) 287nfs4_put_stateowner(struct nfs4_stateowner *so)