Root/fs/nfsd/nfs4recover.c

1/*
2* 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
34#include <linux/file.h>
35#include <linux/slab.h>
36#include <linux/namei.h>
37#include <linux/crypto.h>
38#include <linux/sched.h>
39
40#include "nfsd.h"
41#include "state.h"
42#include "vfs.h"
43
44#define NFSDDBG_FACILITY NFSDDBG_PROC
45
46/* Globals */
47static struct file *rec_file;
48
49static int
50nfs4_save_creds(const struct cred **original_creds)
51{
52    struct cred *new;
53
54    new = prepare_creds();
55    if (!new)
56        return -ENOMEM;
57
58    new->fsuid = 0;
59    new->fsgid = 0;
60    *original_creds = override_creds(new);
61    put_cred(new);
62    return 0;
63}
64
65static void
66nfs4_reset_creds(const struct cred *original)
67{
68    revert_creds(original);
69}
70
71static void
72md5_to_hex(char *out, char *md5)
73{
74    int i;
75
76    for (i=0; i<16; i++) {
77        unsigned char c = md5[i];
78
79        *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
80        *out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
81    }
82    *out = '\0';
83}
84
85__be32
86nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname)
87{
88    struct xdr_netobj cksum;
89    struct hash_desc desc;
90    struct scatterlist sg;
91    __be32 status = nfserr_resource;
92
93    dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
94            clname->len, clname->data);
95    desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
96    desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
97    if (IS_ERR(desc.tfm))
98        goto out_no_tfm;
99    cksum.len = crypto_hash_digestsize(desc.tfm);
100    cksum.data = kmalloc(cksum.len, GFP_KERNEL);
101    if (cksum.data == NULL)
102         goto out;
103
104    sg_init_one(&sg, clname->data, clname->len);
105
106    if (crypto_hash_digest(&desc, &sg, sg.length, cksum.data))
107        goto out;
108
109    md5_to_hex(dname, cksum.data);
110
111    status = nfs_ok;
112out:
113    kfree(cksum.data);
114    crypto_free_hash(desc.tfm);
115out_no_tfm:
116    return status;
117}
118
119int
120nfsd4_create_clid_dir(struct nfs4_client *clp)
121{
122    const struct cred *original_cred;
123    char *dname = clp->cl_recdir;
124    struct dentry *dir, *dentry;
125    int status;
126
127    dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname);
128
129    if (!rec_file || clp->cl_firststate)
130        return 0;
131
132    status = nfs4_save_creds(&original_cred);
133    if (status < 0)
134        return status;
135
136    dir = rec_file->f_path.dentry;
137    /* lock the parent */
138    mutex_lock(&dir->d_inode->i_mutex);
139
140    dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1);
141    if (IS_ERR(dentry)) {
142        status = PTR_ERR(dentry);
143        goto out_unlock;
144    }
145    status = -EEXIST;
146    if (dentry->d_inode) {
147        dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n");
148        goto out_put;
149    }
150    status = mnt_want_write(rec_file->f_path.mnt);
151    if (status)
152        goto out_put;
153    status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU);
154    mnt_drop_write(rec_file->f_path.mnt);
155out_put:
156    dput(dentry);
157out_unlock:
158    mutex_unlock(&dir->d_inode->i_mutex);
159    if (status == 0) {
160        clp->cl_firststate = 1;
161        vfs_fsync(rec_file, 0);
162    }
163    nfs4_reset_creds(original_cred);
164    dprintk("NFSD: nfsd4_create_clid_dir returns %d\n", status);
165    return status;
166}
167
168typedef int (recdir_func)(struct dentry *, struct dentry *);
169
170struct name_list {
171    char name[HEXDIR_LEN];
172    struct list_head list;
173};
174
175static int
176nfsd4_build_namelist(void *arg, const char *name, int namlen,
177        loff_t offset, u64 ino, unsigned int d_type)
178{
179    struct list_head *names = arg;
180    struct name_list *entry;
181
182    if (namlen != HEXDIR_LEN - 1)
183        return 0;
184    entry = kmalloc(sizeof(struct name_list), GFP_KERNEL);
185    if (entry == NULL)
186        return -ENOMEM;
187    memcpy(entry->name, name, HEXDIR_LEN - 1);
188    entry->name[HEXDIR_LEN - 1] = '\0';
189    list_add(&entry->list, names);
190    return 0;
191}
192
193static int
194nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
195{
196    const struct cred *original_cred;
197    struct file *filp;
198    LIST_HEAD(names);
199    struct name_list *entry;
200    struct dentry *dentry;
201    int status;
202
203    if (!rec_file)
204        return 0;
205
206    status = nfs4_save_creds(&original_cred);
207    if (status < 0)
208        return status;
209
210    filp = dentry_open(dget(dir), mntget(rec_file->f_path.mnt), O_RDONLY,
211               current_cred());
212    status = PTR_ERR(filp);
213    if (IS_ERR(filp))
214        goto out;
215    status = vfs_readdir(filp, nfsd4_build_namelist, &names);
216    fput(filp);
217    mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
218    while (!list_empty(&names)) {
219        entry = list_entry(names.next, struct name_list, list);
220
221        dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
222        if (IS_ERR(dentry)) {
223            status = PTR_ERR(dentry);
224            break;
225        }
226        status = f(dir, dentry);
227        dput(dentry);
228        if (status)
229            break;
230        list_del(&entry->list);
231        kfree(entry);
232    }
233    mutex_unlock(&dir->d_inode->i_mutex);
234out:
235    while (!list_empty(&names)) {
236        entry = list_entry(names.next, struct name_list, list);
237        list_del(&entry->list);
238        kfree(entry);
239    }
240    nfs4_reset_creds(original_cred);
241    return status;
242}
243
244static int
245nfsd4_unlink_clid_dir(char *name, int namlen)
246{
247    struct dentry *dir, *dentry;
248    int status;
249
250    dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name);
251
252    dir = rec_file->f_path.dentry;
253    mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
254    dentry = lookup_one_len(name, dir, namlen);
255    if (IS_ERR(dentry)) {
256        status = PTR_ERR(dentry);
257        goto out_unlock;
258    }
259    status = -ENOENT;
260    if (!dentry->d_inode)
261        goto out;
262    status = vfs_rmdir(dir->d_inode, dentry);
263out:
264    dput(dentry);
265out_unlock:
266    mutex_unlock(&dir->d_inode->i_mutex);
267    return status;
268}
269
270void
271nfsd4_remove_clid_dir(struct nfs4_client *clp)
272{
273    const struct cred *original_cred;
274    int status;
275
276    if (!rec_file || !clp->cl_firststate)
277        return;
278
279    status = mnt_want_write(rec_file->f_path.mnt);
280    if (status)
281        goto out;
282    clp->cl_firststate = 0;
283
284    status = nfs4_save_creds(&original_cred);
285    if (status < 0)
286        goto out;
287
288    status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1);
289    nfs4_reset_creds(original_cred);
290    if (status == 0)
291        vfs_fsync(rec_file, 0);
292    mnt_drop_write(rec_file->f_path.mnt);
293out:
294    if (status)
295        printk("NFSD: Failed to remove expired client state directory"
296                " %.*s\n", HEXDIR_LEN, clp->cl_recdir);
297    return;
298}
299
300static int
301purge_old(struct dentry *parent, struct dentry *child)
302{
303    int status;
304
305    if (nfs4_has_reclaimed_state(child->d_name.name, false))
306        return 0;
307
308    status = vfs_rmdir(parent->d_inode, child);
309    if (status)
310        printk("failed to remove client recovery directory %s\n",
311                child->d_name.name);
312    /* Keep trying, success or failure: */
313    return 0;
314}
315
316void
317nfsd4_recdir_purge_old(void) {
318    int status;
319
320    if (!rec_file)
321        return;
322    status = mnt_want_write(rec_file->f_path.mnt);
323    if (status)
324        goto out;
325    status = nfsd4_list_rec_dir(rec_file->f_path.dentry, purge_old);
326    if (status == 0)
327        vfs_fsync(rec_file, 0);
328    mnt_drop_write(rec_file->f_path.mnt);
329out:
330    if (status)
331        printk("nfsd4: failed to purge old clients from recovery"
332            " directory %s\n", rec_file->f_path.dentry->d_name.name);
333}
334
335static int
336load_recdir(struct dentry *parent, struct dentry *child)
337{
338    if (child->d_name.len != HEXDIR_LEN - 1) {
339        printk("nfsd4: illegal name %s in recovery directory\n",
340                child->d_name.name);
341        /* Keep trying; maybe the others are OK: */
342        return 0;
343    }
344    nfs4_client_to_reclaim(child->d_name.name);
345    return 0;
346}
347
348int
349nfsd4_recdir_load(void) {
350    int status;
351
352    if (!rec_file)
353        return 0;
354
355    status = nfsd4_list_rec_dir(rec_file->f_path.dentry, load_recdir);
356    if (status)
357        printk("nfsd4: failed loading clients from recovery"
358            " directory %s\n", rec_file->f_path.dentry->d_name.name);
359    return status;
360}
361
362/*
363 * Hold reference to the recovery directory.
364 */
365
366void
367nfsd4_init_recdir(char *rec_dirname)
368{
369    const struct cred *original_cred;
370    int status;
371
372    printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
373            rec_dirname);
374
375    BUG_ON(rec_file);
376
377    status = nfs4_save_creds(&original_cred);
378    if (status < 0) {
379        printk("NFSD: Unable to change credentials to find recovery"
380               " directory: error %d\n",
381               status);
382        return;
383    }
384
385    rec_file = filp_open(rec_dirname, O_RDONLY | O_DIRECTORY, 0);
386    if (IS_ERR(rec_file)) {
387        printk("NFSD: unable to find recovery directory %s\n",
388                rec_dirname);
389        rec_file = NULL;
390    }
391
392    nfs4_reset_creds(original_cred);
393}
394
395void
396nfsd4_shutdown_recdir(void)
397{
398    if (!rec_file)
399        return;
400    fput(rec_file);
401    rec_file = NULL;
402}
403

Archive Download this file



interactive