OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [fs/] [nfsd/] [export.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
#define MSNFS   /* HACK HACK */
2
/*
3
 * linux/fs/nfsd/export.c
4
 *
5
 * NFS exporting and validation.
6
 *
7
 * We maintain a list of clients, each of which has a list of
8
 * exports. To export an fs to a given client, you first have
9
 * to create the client entry with NFSCTL_ADDCLIENT, which
10
 * creates a client control block and adds it to the hash
11
 * table. Then, you call NFSCTL_EXPORT for each fs.
12
 *
13
 *
14
 * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de>
15
 */
16
 
17
#include <linux/unistd.h>
18
#include <linux/slab.h>
19
#include <linux/stat.h>
20
#include <linux/in.h>
21
#include <linux/seq_file.h>
22
#include <linux/smp_lock.h>
23
 
24
#include <linux/sunrpc/svc.h>
25
#include <linux/nfsd/nfsd.h>
26
#include <linux/nfsd/nfsfh.h>
27
#include <linux/nfsd/syscall.h>
28
#include <linux/lockd/bind.h>
29
 
30
#define NFSDDBG_FACILITY        NFSDDBG_EXPORT
31
#define NFSD_PARANOIA 1
32
 
33
typedef struct svc_client       svc_client;
34
typedef struct svc_export       svc_export;
35
 
36
static svc_export *     exp_parent(svc_client *clp, kdev_t dev,
37
                                        struct dentry *dentry);
38
static svc_export *     exp_child(svc_client *clp, kdev_t dev,
39
                                        struct dentry *dentry);
40
static void             exp_unexport_all(svc_client *clp);
41
static void             exp_do_unexport(svc_export *unexp);
42
static svc_client *     exp_getclientbyname(char *name);
43
static void             exp_freeclient(svc_client *clp);
44
static void             exp_unhashclient(svc_client *clp);
45
static int              exp_verify_string(char *cp, int max);
46
 
47
#define CLIENT_HASHBITS         6
48
#define CLIENT_HASHMAX          (1 << CLIENT_HASHBITS)
49
#define CLIENT_HASHMASK         (CLIENT_HASHMAX - 1)
50
#define CLIENT_HASH(a) \
51
                ((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK)
52
/* XXX: is this adequate for 32bit kdev_t ? */
53
#define EXPORT_HASH(dev)        ((dev) & (NFSCLNT_EXPMAX - 1))
54
#define EXPORT_FSID_HASH(fsid)  ((fsid) & (NFSCLNT_EXPMAX - 1))
55
 
56
struct svc_clnthash {
57
        struct svc_clnthash *   h_next;
58
        struct in_addr          h_addr;
59
        struct svc_client *     h_client;
60
};
61
static struct svc_clnthash *    clnt_hash[CLIENT_HASHMAX];
62
static svc_client *             clients;
63
 
64
static int                      hash_lock;
65
static int                      want_lock;
66
static int                      hash_count;
67
static DECLARE_WAIT_QUEUE_HEAD( hash_wait );
68
 
69
/*
70
 * Find the client's export entry matching xdev/xino.
71
 */
72
svc_export *
73
exp_get(svc_client *clp, kdev_t dev, ino_t ino)
74
{
75
        struct list_head *head, *p;
76
 
77
        if (!clp)
78
                return NULL;
79
 
80
        head = &clp->cl_export[EXPORT_HASH(dev)];
81
        list_for_each(p, head) {
82
                svc_export *exp = list_entry(p, svc_export, ex_hash);
83
                if (exp->ex_ino == ino && exp->ex_dev == dev)
84
                        return exp;
85
        }
86
 
87
        return NULL;
88
}
89
 
90
/*
91
 * Find the client's export entry matching fsid
92
 */
93
svc_export *
94
exp_get_fsid(svc_client *clp, int fsid)
95
{
96
        struct list_head *head, *p;
97
 
98
        if (!clp)
99
                return NULL;
100
 
101
        head = &clp->cl_expfsid[EXPORT_FSID_HASH(fsid)];
102
        list_for_each(p, head) {
103
                svc_export *exp = list_entry(p, svc_export, ex_fsid_hash);
104
                if (exp->ex_fsid == fsid)
105
                        return exp;
106
        }
107
        return NULL;
108
}
109
 
110
/*
111
 * Find the export entry for a given dentry.  <gam3@acm.org>
112
 */
113
static svc_export *
114
exp_parent(svc_client *clp, kdev_t dev, struct dentry *dentry)
115
{
116
        struct list_head *head = &clp->cl_export[EXPORT_HASH(dev)];
117
        struct list_head *p;
118
 
119
        list_for_each(p,head) {
120
                svc_export *exp = list_entry(p, svc_export, ex_hash);
121
                if (is_subdir(dentry, exp->ex_dentry))
122
                        return exp;
123
        }
124
        return NULL;
125
}
126
 
127
/*
128
 * Find the child export entry for a given fs. This function is used
129
 * only by the export syscall to keep the export tree consistent.
130
 * <gam3@acm.org>
131
 */
132
static svc_export *
133
exp_child(svc_client *clp, kdev_t dev, struct dentry *dentry)
134
{
135
        struct list_head *head = &clp->cl_export[EXPORT_HASH(dev)];
136
        struct list_head *p;
137
 
138
 
139
        list_for_each(p, head) {
140
                svc_export *exp = list_entry(p, svc_export, ex_hash);
141
                struct dentry *ndentry = exp->ex_dentry;
142
 
143
                if (ndentry && is_subdir(ndentry->d_parent, dentry))
144
                        return exp;
145
        }
146
        return NULL;
147
}
148
 
149
/* Update parent pointers of all exports */
150
static void exp_change_parents(svc_client *clp, svc_export *old, svc_export *new)
151
{
152
        struct list_head *head = &clp->cl_list;
153
        struct list_head *p;
154
 
155
        list_for_each(p, head) {
156
                svc_export *exp = list_entry(p, svc_export, ex_list);
157
                if (exp->ex_parent == old)
158
                        exp->ex_parent = new;
159
        }
160
}
161
 
162
static void exp_fsid_unhash(struct svc_export *exp)
163
{
164
 
165
        if ((exp->ex_flags & NFSEXP_FSID) == 0)
166
                return;
167
 
168
        list_del_init(&exp->ex_fsid_hash);
169
}
170
 
171
static void exp_fsid_hash(struct svc_client *clp, struct svc_export *exp)
172
{
173
        struct list_head *head;
174
 
175
        if ((exp->ex_flags & NFSEXP_FSID) == 0)
176
                return;
177
        head = clp->cl_expfsid + EXPORT_FSID_HASH(exp->ex_fsid);
178
        list_add(&exp->ex_fsid_hash, head);
179
}
180
 
181
/*
182
 * Export a file system.
183
 */
184
int
185
exp_export(struct nfsctl_export *nxp)
186
{
187
        svc_client      *clp;
188
        svc_export      *exp = NULL, *parent;
189
        svc_export      *fsid_exp;
190
        struct nameidata nd;
191
        struct inode    *inode = NULL;
192
        int             err;
193
        kdev_t          dev;
194
        ino_t           ino;
195
 
196
        /* Consistency check */
197
        err = -EINVAL;
198
        if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) ||
199
            !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
200
                goto out;
201
 
202
        dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
203
                        nxp->ex_client, nxp->ex_path,
204
                        nxp->ex_dev, (long) nxp->ex_ino, nxp->ex_flags);
205
 
206
        /* Try to lock the export table for update */
207
        if ((err = exp_writelock()) < 0)
208
                goto out;
209
 
210
        /* Look up client info */
211
        err = -EINVAL;
212
        if (!(clp = exp_getclientbyname(nxp->ex_client)))
213
                goto out_unlock;
214
 
215
 
216
        /* Look up the dentry */
217
        err = 0;
218
        if (path_init(nxp->ex_path, LOOKUP_POSITIVE, &nd))
219
                err = path_walk(nxp->ex_path, &nd);
220
        if (err)
221
                goto out_unlock;
222
 
223
        inode = nd.dentry->d_inode;
224
        dev = inode->i_dev;
225
        ino = inode->i_ino;
226
        err = -EINVAL;
227
 
228
        exp = exp_get(clp, dev, ino);
229
 
230
        /* must make sure there wont be an ex_fsid clash */
231
        if ((nxp->ex_flags & NFSEXP_FSID) &&
232
            (fsid_exp = exp_get_fsid(clp, nxp->ex_dev)) &&
233
            fsid_exp != exp)
234
                goto finish;
235
 
236
        if (exp != NULL) {
237
                /* just a flags/id/fsid update */
238
 
239
                exp_fsid_unhash(exp);
240
                exp->ex_flags    = nxp->ex_flags;
241
                exp->ex_anon_uid = nxp->ex_anon_uid;
242
                exp->ex_anon_gid = nxp->ex_anon_gid;
243
                exp->ex_fsid     = nxp->ex_dev;
244
                exp_fsid_hash(clp, exp);
245
                err = 0;
246
                goto finish;
247
        }
248
 
249
        /* We currently export only dirs and regular files.
250
         * This is what umountd does.
251
         */
252
        err = -ENOTDIR;
253
        if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode))
254
                goto finish;
255
 
256
        err = -EINVAL;
257
        /* There are two requirements on a filesystem to be exportable.
258
         * 1:  We must be able to identify the filesystem from a number.
259
         *       either a device number (so FS_REQUIRES_DEV needed)
260
         *       or an FSID number (so NFSEXP_FSID needed).
261
         * 2:  We must be able to find an inode from a filehandle.
262
         *       either using fh_to_dentry (prefered)
263
         *       or using read_inode (the hack).
264
         */
265
        if (!((inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV)
266
              || (nxp->ex_flags & NFSEXP_FSID))
267
            ||
268
            (inode->i_sb->s_op->read_inode == NULL
269
             && inode->i_sb->s_op->fh_to_dentry == NULL)) {
270
                dprintk("exp_export: export of invalid fs type.\n");
271
                goto finish;
272
        }
273
 
274
        if ((parent = exp_child(clp, dev, nd.dentry)) != NULL) {
275
                dprintk("exp_export: export not valid (Rule 3).\n");
276
                goto finish;
277
        }
278
        /* Is this is a sub-export, must be a proper subset of FS */
279
        if ((parent = exp_parent(clp, dev, nd.dentry)) != NULL) {
280
                dprintk("exp_export: sub-export not valid (Rule 2).\n");
281
                goto finish;
282
        }
283
 
284
        err = -ENOMEM;
285
        if (!(exp = kmalloc(sizeof(*exp), GFP_USER)))
286
                goto finish;
287
        dprintk("nfsd: created export entry %p for client %p\n", exp, clp);
288
 
289
        strcpy(exp->ex_path, nxp->ex_path);
290
        exp->ex_client = clp;
291
        exp->ex_parent = parent;
292
        exp->ex_dentry = dget(nd.dentry);
293
        exp->ex_mnt = mntget(nd.mnt);
294
        exp->ex_flags = nxp->ex_flags;
295
        exp->ex_dev = dev;
296
        exp->ex_ino = ino;
297
        exp->ex_anon_uid = nxp->ex_anon_uid;
298
        exp->ex_anon_gid = nxp->ex_anon_gid;
299
        exp->ex_fsid = nxp->ex_dev;
300
 
301
 
302
        /* Update parent pointers of all exports */
303
        if (parent)
304
                exp_change_parents(clp, parent, exp);
305
 
306
        list_add(&exp->ex_hash, clp->cl_export + EXPORT_HASH(dev));
307
        list_add_tail(&exp->ex_list, &clp->cl_list);
308
 
309
        exp_fsid_hash(clp, exp);
310
 
311
        err = 0;
312
 
313
finish:
314
        path_release(&nd);
315
out_unlock:
316
        exp_unlock();
317
out:
318
        return err;
319
}
320
 
321
/*
322
 * Unexport a file system. The export entry has already
323
 * been removed from the client's list of exported fs's.
324
 */
325
static void
326
exp_do_unexport(svc_export *unexp)
327
{
328
        struct dentry   *dentry;
329
        struct vfsmount *mnt;
330
        struct inode    *inode;
331
 
332
        list_del(&unexp->ex_hash);
333
        list_del(&unexp->ex_list);
334
        exp_fsid_unhash(unexp);
335
 
336
        exp_change_parents(unexp->ex_client, unexp, unexp->ex_parent);
337
 
338
        dentry = unexp->ex_dentry;
339
        mnt = unexp->ex_mnt;
340
        inode = dentry->d_inode;
341
        if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
342
                printk(KERN_WARNING "nfsd: bad dentry in unexport!\n");
343
        dput(dentry);
344
        mntput(mnt);
345
 
346
        kfree(unexp);
347
}
348
 
349
/*
350
 * Revoke all exports for a given client.
351
 * This may look very awkward, but we have to do it this way in order
352
 * to avoid race conditions (aka mind the parent pointer).
353
 */
354
static void
355
exp_unexport_all(svc_client *clp)
356
{
357
        struct list_head *p = &clp->cl_list;
358
 
359
        dprintk("unexporting all fs's for clnt %p\n", clp);
360
 
361
        while (!list_empty(p)) {
362
                svc_export *exp = list_entry(p->next, svc_export, ex_list);
363
                exp_do_unexport(exp);
364
        }
365
}
366
 
367
/*
368
 * unexport syscall.
369
 */
370
int
371
exp_unexport(struct nfsctl_export *nxp)
372
{
373
        svc_client      *clp;
374
        int             err;
375
 
376
        /* Consistency check */
377
        if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
378
                return -EINVAL;
379
 
380
        if ((err = exp_writelock()) < 0)
381
                goto out;
382
 
383
        err = -EINVAL;
384
        clp = exp_getclientbyname(nxp->ex_client);
385
        if (clp) {
386
                svc_export *exp = exp_get(clp, nxp->ex_dev, nxp->ex_ino);
387
                if (exp) {
388
                        exp_do_unexport(exp);
389
                        err = 0;
390
                }
391
        }
392
 
393
        exp_unlock();
394
out:
395
        return err;
396
}
397
 
398
/*
399
 * Obtain the root fh on behalf of a client.
400
 * This could be done in user space, but I feel that it adds some safety
401
 * since its harder to fool a kernel module than a user space program.
402
 */
403
int
404
exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino,
405
           char *path, struct knfsd_fh *f, int maxsize)
406
{
407
        struct svc_export       *exp;
408
        struct nameidata        nd;
409
        struct inode            *inode;
410
        struct svc_fh           fh;
411
        int                     err;
412
 
413
        err = -EPERM;
414
        if (path) {
415
                if (path_init(path, LOOKUP_POSITIVE, &nd) &&
416
                    path_walk(path, &nd)) {
417
                        printk("nfsd: exp_rootfh path not found %s", path);
418
                        return err;
419
                }
420
                dev = nd.dentry->d_inode->i_dev;
421
                ino = nd.dentry->d_inode->i_ino;
422
 
423
                dprintk("nfsd: exp_rootfh(%s [%p] %s:%x/%ld)\n",
424
                         path, nd.dentry, clp->cl_ident, dev, (long) ino);
425
                exp = exp_parent(clp, dev, nd.dentry);
426
        } else {
427
                dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n",
428
                         clp->cl_ident, dev, (long) ino);
429
                if ((exp = exp_get(clp, dev, ino))) {
430
                        nd.mnt = mntget(exp->ex_mnt);
431
                        nd.dentry = dget(exp->ex_dentry);
432
                }
433
        }
434
        if (!exp) {
435
                dprintk("nfsd: exp_rootfh export not found.\n");
436
                goto out;
437
        }
438
 
439
        inode = nd.dentry->d_inode;
440
        if (!inode) {
441
                printk("exp_rootfh: Aieee, NULL d_inode\n");
442
                goto out;
443
        }
444
        if (inode->i_dev != dev || inode->i_ino != ino) {
445
                printk("exp_rootfh: Aieee, ino/dev mismatch\n");
446
                printk("exp_rootfh: arg[dev(%x):ino(%ld)]"
447
                       " inode[dev(%x):ino(%ld)]\n",
448
                       dev, (long) ino, inode->i_dev, (long) inode->i_ino);
449
        }
450
 
451
        /*
452
         * fh must be initialized before calling fh_compose
453
         */
454
        fh_init(&fh, maxsize);
455
        if (fh_compose(&fh, exp, dget(nd.dentry), NULL))
456
                err = -EINVAL;
457
        else
458
                err = 0;
459
        memcpy(f, &fh.fh_handle, sizeof(struct knfsd_fh));
460
        fh_put(&fh);
461
 
462
out:
463
        if (path)
464
                path_release(&nd);
465
        return err;
466
}
467
 
468
/*
469
 * Hashtable locking. Write locks are placed only by user processes
470
 * wanting to modify export information.
471
 */
472
void
473
exp_readlock(void)
474
{
475
        while (hash_lock || want_lock)
476
                sleep_on(&hash_wait);
477
        hash_count++;
478
}
479
 
480
int
481
exp_writelock(void)
482
{
483
        /* fast track */
484
        if (!hash_count && !hash_lock) {
485
        lock_it:
486
                hash_lock = 1;
487
                return 0;
488
        }
489
 
490
        current->sigpending = 0;
491
        want_lock++;
492
        while (hash_count || hash_lock) {
493
                interruptible_sleep_on(&hash_wait);
494
                if (signal_pending(current))
495
                        break;
496
        }
497
        want_lock--;
498
 
499
        /* restore the task's signals */
500
        spin_lock_irq(&current->sigmask_lock);
501
        recalc_sigpending(current);
502
        spin_unlock_irq(&current->sigmask_lock);
503
 
504
        if (!hash_count && !hash_lock)
505
                goto lock_it;
506
        return -EINTR;
507
}
508
 
509
void
510
exp_unlock(void)
511
{
512
        if (!hash_count && !hash_lock)
513
                printk(KERN_WARNING "exp_unlock: not locked!\n");
514
        if (hash_count)
515
                hash_count--;
516
        else
517
                hash_lock = 0;
518
        wake_up(&hash_wait);
519
}
520
 
521
/*
522
 * Find a valid client given an inet address. We always move the most
523
 * recently used client to the front of the hash chain to speed up
524
 * future lookups.
525
 * Locking against other processes is the responsibility of the caller.
526
 */
527
struct svc_client *
528
exp_getclient(struct sockaddr_in *sin)
529
{
530
        struct svc_clnthash     **hp, **head, *tmp;
531
        unsigned long           addr = sin->sin_addr.s_addr;
532
 
533
        head = &clnt_hash[CLIENT_HASH(addr)];
534
 
535
        for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
536
                if (tmp->h_addr.s_addr == addr) {
537
                        /* Move client to the front */
538
                        if (head != hp) {
539
                                *hp = tmp->h_next;
540
                                tmp->h_next = *head;
541
                                *head = tmp;
542
                        }
543
 
544
                        return tmp->h_client;
545
                }
546
        }
547
 
548
        return NULL;
549
}
550
 
551
/*
552
 * Find a client given its identifier.
553
 */
554
static svc_client *
555
exp_getclientbyname(char *ident)
556
{
557
        svc_client *    clp;
558
 
559
        for (clp = clients; clp; clp = clp->cl_next) {
560
                if (!strcmp(clp->cl_ident, ident))
561
                        return clp;
562
        }
563
        return NULL;
564
}
565
 
566
/* Iterator */
567
 
568
static void *e_start(struct seq_file *m, loff_t *pos)
569
{
570
        loff_t n = *pos;
571
        unsigned client, export;
572
        svc_client *clp;
573
        struct list_head *p;
574
 
575
        lock_kernel();
576
        exp_readlock();
577
        if (!n--)
578
                return (void *)1;
579
        client = n >> 32;
580
        export = n & ((1LL<<32) - 1);
581
        for (clp = clients; client && clp; clp = clp->cl_next, client--)
582
                ;
583
        if (!clp)
584
                return NULL;
585
        list_for_each(p, &clp->cl_list)
586
                if (!export--)
587
                        return list_entry(p, svc_export, ex_list);
588
        n &= ~((1LL<<32) - 1);
589
        do {
590
                clp = clp->cl_next;
591
                n += 1LL<<32;
592
        } while(clp && list_empty(&clp->cl_list));
593
        if (!clp)
594
                return NULL;
595
        *pos = n+1;
596
        return list_entry(clp->cl_list.next, svc_export, ex_list);
597
}
598
 
599
static void *e_next(struct seq_file *m, void *p, loff_t *pos)
600
{
601
        svc_export *exp = p;
602
        svc_client *clp;
603
 
604
        if (p == (void *)1)
605
                clp = clients;
606
        else if (exp->ex_list.next == &exp->ex_client->cl_list) {
607
                clp = exp->ex_client->cl_next;
608
                *pos += 1LL<<32;
609
        } else {
610
                ++*pos;
611
                return list_entry(exp->ex_list.next, svc_export, ex_list);
612
        }
613
        *pos &= ~((1LL<<32) - 1);
614
        while (clp && list_empty(&clp->cl_list)) {
615
                clp = clp->cl_next;
616
                *pos += 1LL<<32;
617
        }
618
        if (!clp)
619
                return NULL;
620
        ++*pos;
621
        return list_entry(clp->cl_list.next, svc_export, ex_list);
622
}
623
 
624
static void e_stop(struct seq_file *m, void *p)
625
{
626
        exp_unlock();
627
        unlock_kernel();
628
}
629
 
630
struct flags {
631
        int flag;
632
        char *name[2];
633
} expflags[] = {
634
        { NFSEXP_READONLY, {"ro", "rw"}},
635
        { NFSEXP_INSECURE_PORT, {"insecure", ""}},
636
        { NFSEXP_ROOTSQUASH, {"root_squash", "no_root_squash"}},
637
        { NFSEXP_ALLSQUASH, {"all_squash", ""}},
638
        { NFSEXP_ASYNC, {"async", "sync"}},
639
        { NFSEXP_GATHERED_WRITES, {"wdelay", "no_wdelay"}},
640
        { NFSEXP_UIDMAP, {"uidmap", ""}},
641
        { NFSEXP_KERBEROS, { "kerberos", ""}},
642
        { NFSEXP_SUNSECURE, { "sunsecure", ""}},
643
        { NFSEXP_NOHIDE, {"nohide", ""}},
644
        { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
645
        { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
646
#ifdef MSNFS
647
        { NFSEXP_MSNFS, {"msnfs", ""}},
648
#endif
649
        { 0, {"", ""}}
650
};
651
 
652
static void exp_flags(struct seq_file *m, int flag, int fsid)
653
{
654
        int first = 0;
655
        struct flags *flg;
656
 
657
        for (flg = expflags; flg->flag; flg++) {
658
                int state = (flg->flag & flag)?0:1;
659
                if (*flg->name[state])
660
                        seq_printf(m, "%s%s", first++?",":"", flg->name[state]);
661
        }
662
        if (flag & NFSEXP_FSID)
663
                seq_printf(m, "%sfsid=%d", first++?",":"", fsid);
664
}
665
 
666
static inline void mangle(struct seq_file *m, const char *s)
667
{
668
        seq_escape(m, s, " \t\n\\");
669
}
670
 
671
static int e_show(struct seq_file *m, void *p)
672
{
673
        struct svc_export *exp = p;
674
        struct svc_client *clp;
675
        int j, first = 0;
676
 
677
        if (p == (void *)1) {
678
                seq_puts(m, "# Version 1.1\n");
679
                seq_puts(m, "# Path Client(Flags) # IPs\n");
680
                return 0;
681
        }
682
 
683
        clp = exp->ex_client;
684
 
685
        mangle(m, exp->ex_path);
686
        seq_putc(m, '\t');
687
        mangle(m, clp->cl_ident);
688
        seq_putc(m, '(');
689
        exp_flags(m, exp->ex_flags, exp->ex_fsid);
690
        seq_puts(m, ") # ");
691
        for (j = 0; j < clp->cl_naddr; j++) {
692
                struct svc_clnthash **hp, **head, *tmp;
693
                struct in_addr addr = clp->cl_addr[j];
694
 
695
                head = &clnt_hash[CLIENT_HASH(addr.s_addr)];
696
                for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
697
                        if (tmp->h_addr.s_addr == addr.s_addr)
698
                                break;
699
                }
700
                if (tmp) {
701
                        if (first++)
702
                                seq_putc(m, ' ');
703
                        if (tmp->h_client != clp)
704
                                seq_putc(m, '(');
705
                        seq_printf(m, "%d.%d.%d.%d",
706
                                htonl(addr.s_addr) >> 24 & 0xff,
707
                                htonl(addr.s_addr) >> 16 & 0xff,
708
                                htonl(addr.s_addr) >>  8 & 0xff,
709
                                htonl(addr.s_addr) >>  0 & 0xff);
710
                        if (tmp->h_client != clp)
711
                                seq_putc(m, ')');
712
                }
713
        }
714
        seq_putc(m, '\n');
715
        return 0;
716
}
717
 
718
struct seq_operations nfs_exports_op = {
719
        start:  e_start,
720
        next:   e_next,
721
        stop:   e_stop,
722
        show:   e_show,
723
};
724
 
725
/*
726
 * Add or modify a client.
727
 * Change requests may involve the list of host addresses. The list of
728
 * exports and possibly existing uid maps are left untouched.
729
 */
730
int
731
exp_addclient(struct nfsctl_client *ncp)
732
{
733
        struct svc_clnthash *   ch[NFSCLNT_ADDRMAX];
734
        svc_client *            clp;
735
        int                     i, err, change = 0, ilen;
736
 
737
        /* First, consistency check. */
738
        err = -EINVAL;
739
        if (!(ilen = exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)))
740
                goto out;
741
        if (ncp->cl_naddr > NFSCLNT_ADDRMAX)
742
                goto out;
743
 
744
        /* Lock the hashtable */
745
        if ((err = exp_writelock()) < 0)
746
                goto out;
747
 
748
        /* First check if this is a change request for a client. */
749
        for (clp = clients; clp; clp = clp->cl_next)
750
                if (!strcmp(clp->cl_ident, ncp->cl_ident))
751
                        break;
752
 
753
        err = -ENOMEM;
754
        if (clp) {
755
                change = 1;
756
        } else {
757
                if (!(clp = kmalloc(sizeof(*clp), GFP_KERNEL)))
758
                        goto out_unlock;
759
                memset(clp, 0, sizeof(*clp));
760
                for (i = 0; i < NFSCLNT_EXPMAX; i++) {
761
                        INIT_LIST_HEAD(&clp->cl_export[i]);
762
                        INIT_LIST_HEAD(&clp->cl_expfsid[i]);
763
                }
764
                INIT_LIST_HEAD(&clp->cl_list);
765
 
766
                dprintk("created client %s (%p)\n", ncp->cl_ident, clp);
767
 
768
                strcpy(clp->cl_ident, ncp->cl_ident);
769
                clp->cl_idlen = ilen;
770
        }
771
 
772
        /* Allocate hash buckets */
773
        for (i = 0; i < ncp->cl_naddr; i++) {
774
                ch[i] = kmalloc(sizeof(struct svc_clnthash), GFP_KERNEL);
775
                if (!ch[i]) {
776
                        while (i--)
777
                                kfree(ch[i]);
778
                        if (!change)
779
                                kfree(clp);
780
                        goto out_unlock;
781
                }
782
        }
783
 
784
        /* Copy addresses. */
785
        for (i = 0; i < ncp->cl_naddr; i++) {
786
                clp->cl_addr[i] = ncp->cl_addrlist[i];
787
        }
788
        clp->cl_naddr = ncp->cl_naddr;
789
 
790
        /* Remove old client hash entries. */
791
        if (change)
792
                exp_unhashclient(clp);
793
 
794
        /* Insert client into hashtable. */
795
        for (i = 0; i < ncp->cl_naddr; i++) {
796
                struct in_addr  addr = clp->cl_addr[i];
797
                int             hash;
798
 
799
                hash = CLIENT_HASH(addr.s_addr);
800
                ch[i]->h_client = clp;
801
                ch[i]->h_addr = addr;
802
                ch[i]->h_next = clnt_hash[hash];
803
                clnt_hash[hash] = ch[i];
804
        }
805
 
806
        if (!change) {
807
                clp->cl_next = clients;
808
                clients = clp;
809
        }
810
        err = 0;
811
 
812
out_unlock:
813
        exp_unlock();
814
out:
815
        return err;
816
}
817
 
818
/*
819
 * Delete a client given an identifier.
820
 */
821
int
822
exp_delclient(struct nfsctl_client *ncp)
823
{
824
        svc_client      **clpp, *clp;
825
        int             err;
826
 
827
        err = -EINVAL;
828
        if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
829
                goto out;
830
 
831
        /* Lock the hashtable */
832
        if ((err = exp_writelock()) < 0)
833
                goto out;
834
 
835
        err = -EINVAL;
836
        for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next))
837
                if (!strcmp(ncp->cl_ident, clp->cl_ident))
838
                        break;
839
 
840
        if (clp) {
841
                *clpp = clp->cl_next;
842
                exp_freeclient(clp);
843
                err = 0;
844
        }
845
 
846
        exp_unlock();
847
out:
848
        return err;
849
}
850
 
851
/*
852
 * Free a client. The caller has already removed it from the client list.
853
 */
854
static void
855
exp_freeclient(svc_client *clp)
856
{
857
        exp_unhashclient(clp);
858
 
859
        /* umap_free(&(clp->cl_umap)); */
860
        exp_unexport_all(clp);
861
        nfsd_lockd_unexport(clp);
862
        kfree (clp);
863
}
864
 
865
/*
866
 * Remove client from hashtable. We first collect all hashtable
867
 * entries and free them in one go.
868
 * The hash table must be writelocked by the caller.
869
 */
870
static void
871
exp_unhashclient(svc_client *clp)
872
{
873
        struct svc_clnthash     **hpp, *hp, *ch[NFSCLNT_ADDRMAX];
874
        int                     i, count, err;
875
 
876
again:
877
        err = 0;
878
        for (i = 0, count = 0; i < CLIENT_HASHMAX && !err; i++) {
879
                hpp = clnt_hash + i;
880
                while ((hp = *hpp) && !err) {
881
                        if (hp->h_client == clp) {
882
                                *hpp = hp->h_next;
883
                                ch[count++] = hp;
884
                                err = (count >= NFSCLNT_ADDRMAX);
885
                        } else {
886
                                hpp = &(hp->h_next);
887
                        }
888
                }
889
        }
890
        if (count != clp->cl_naddr)
891
                printk(KERN_WARNING "nfsd: bad address count in freeclient!\n");
892
        if (err)
893
                goto again;
894
        for (i = 0; i < count; i++)
895
                kfree (ch[i]);
896
}
897
 
898
/*
899
 * Lockd is shutting down and tells us to unregister all clients
900
 */
901
void
902
exp_nlmdetach(void)
903
{
904
        struct svc_client       *clp;
905
 
906
        exp_readlock();
907
        for (clp = clients; clp; clp = clp->cl_next)
908
                nfsd_lockd_unexport(clp);
909
        exp_unlock();
910
}
911
 
912
/*
913
 * Verify that string is non-empty and does not exceed max length.
914
 */
915
static int
916
exp_verify_string(char *cp, int max)
917
{
918
        int     i;
919
 
920
        for (i = 0; i < max; i++)
921
                if (!cp[i])
922
                        return i;
923
        cp[i] = 0;
924
        printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp);
925
        return 0;
926
}
927
 
928
/*
929
 * Initialize the exports module.
930
 */
931
void
932
nfsd_export_init(void)
933
{
934
        int             i;
935
 
936
        dprintk("nfsd: initializing export module.\n");
937
 
938
        for (i = 0; i < CLIENT_HASHMAX; i++)
939
                clnt_hash[i] = NULL;
940
        clients = NULL;
941
 
942
}
943
 
944
/*
945
 * Shutdown the exports module.
946
 */
947
void
948
nfsd_export_shutdown(void)
949
{
950
        int     i;
951
 
952
        dprintk("nfsd: shutting down export module.\n");
953
 
954
        if (exp_writelock() < 0) {
955
                printk(KERN_WARNING "Weird: hashtable locked in exp_shutdown");
956
                return;
957
        }
958
        for (i = 0; i < CLIENT_HASHMAX; i++) {
959
                while (clnt_hash[i])
960
                        exp_freeclient(clnt_hash[i]->h_client);
961
        }
962
        clients = NULL; /* we may be restarted before the module unloads */
963
 
964
        exp_unlock();
965
        dprintk("nfsd: export shutdown complete.\n");
966
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.