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

Subversion Repositories or1k

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 * linux/fs/nfsd/nfsfh.c
3
 *
4
 * NFS server file handle treatment.
5
 *
6
 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7
 * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org>
8
 * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999
9
 */
10
 
11
#include <linux/sched.h>
12
#include <linux/slab.h>
13
#include <linux/fs.h>
14
#include <linux/unistd.h>
15
#include <linux/string.h>
16
#include <linux/stat.h>
17
#include <linux/dcache.h>
18
#include <asm/pgtable.h>
19
 
20
#include <linux/sunrpc/svc.h>
21
#include <linux/nfsd/nfsd.h>
22
 
23
#define NFSDDBG_FACILITY                NFSDDBG_FH
24
#define NFSD_PARANOIA 1
25
/* #define NFSD_DEBUG_VERBOSE 1 */
26
 
27
 
28
static int nfsd_nr_verified;
29
static int nfsd_nr_put;
30
 
31
 
32
struct nfsd_getdents_callback {
33
        char *name;             /* name that was found. It already points to a buffer NAME_MAX+1 is size */
34
        unsigned long ino;      /* the inum we are looking for */
35
        int found;              /* inode matched? */
36
        int sequence;           /* sequence counter */
37
};
38
 
39
/*
40
 * A rather strange filldir function to capture
41
 * the name matching the specified inode number.
42
 */
43
static int filldir_one(void * __buf, const char * name, int len,
44
                        loff_t pos, ino_t ino, unsigned int d_type)
45
{
46
        struct nfsd_getdents_callback *buf = __buf;
47
        int result = 0;
48
 
49
        buf->sequence++;
50
#ifdef NFSD_DEBUG_VERBOSE
51
dprintk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
52
#endif
53
        if (buf->ino == ino) {
54
                memcpy(buf->name, name, len);
55
                buf->name[len] = '\0';
56
                buf->found = 1;
57
                result = -1;
58
        }
59
        return result;
60
}
61
 
62
/**
63
 * nfsd_get_name - default nfsd_operations->get_name function
64
 * @dentry: the directory in which to find a name
65
 * @name:   a pointer to a %NAME_MAX+1 char buffer to store the name
66
 * @child:  the dentry for the child directory.
67
 *
68
 * calls readdir on the parent until it finds an entry with
69
 * the same inode number as the child, and returns that.
70
 */
71
static int nfsd_get_name(struct dentry *dentry, char *name,
72
                        struct dentry *child)
73
{
74
        struct inode *dir = dentry->d_inode;
75
        int error;
76
        struct file file;
77
        struct nfsd_getdents_callback buffer;
78
 
79
        error = -ENOTDIR;
80
        if (!dir || !S_ISDIR(dir->i_mode))
81
                goto out;
82
        error = -EINVAL;
83
        if (!dir->i_fop)
84
                goto out;
85
        /*
86
         * Open the directory ...
87
         */
88
        error = init_private_file(&file, dentry, FMODE_READ);
89
        if (error)
90
                goto out;
91
        error = -EINVAL;
92
        if (!file.f_op->readdir)
93
                goto out_close;
94
 
95
        buffer.name = name;
96
        buffer.ino = child->d_inode->i_ino;
97
        buffer.found = 0;
98
        buffer.sequence = 0;
99
        while (1) {
100
                int old_seq = buffer.sequence;
101
 
102
                error = vfs_readdir(&file, filldir_one, &buffer);
103
 
104
                if (error < 0)
105
                        break;
106
 
107
                error = 0;
108
                if (buffer.found)
109
                        break;
110
                error = -ENOENT;
111
                if (old_seq == buffer.sequence)
112
                        break;
113
        }
114
 
115
out_close:
116
        if (file.f_op->release)
117
                file.f_op->release(dir, &file);
118
out:
119
        return error;
120
}
121
 
122
/* this should be provided by each filesystem in an nfsd_operations interface as
123
 * iget isn't really the right interface
124
 */
125
static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation)
126
{
127
 
128
        /* iget isn't really right if the inode is currently unallocated!!
129
         * This should really all be done inside each filesystem
130
         *
131
         * ext2fs' read_inode has been strengthed to return a bad_inode if the inode
132
         *   had been deleted.
133
         *
134
         * Currently we don't know the generation for parent directory, so a generation
135
         * of 0 means "accept any"
136
         */
137
        struct inode *inode;
138
        struct list_head *lp;
139
        struct dentry *result;
140
        if (ino == 0)
141
                return ERR_PTR(-ESTALE);
142
        inode = iget(sb, ino);
143
        if (inode == NULL)
144
                return ERR_PTR(-ENOMEM);
145
        if (is_bad_inode(inode)
146
            || (generation && inode->i_generation != generation)
147
                ) {
148
                /* we didn't find the right inode.. */
149
                dprintk("fh_verify: Inode %lu, Bad count: %d %d or version  %u %u\n",
150
                        inode->i_ino,
151
                        inode->i_nlink, atomic_read(&inode->i_count),
152
                        inode->i_generation,
153
                        generation);
154
 
155
                iput(inode);
156
                return ERR_PTR(-ESTALE);
157
        }
158
        /* now to find a dentry.
159
         * If possible, get a well-connected one
160
         */
161
        spin_lock(&dcache_lock);
162
        for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
163
                result = list_entry(lp,struct dentry, d_alias);
164
                if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
165
                        dget_locked(result);
166
                        result->d_vfs_flags |= DCACHE_REFERENCED;
167
                        spin_unlock(&dcache_lock);
168
                        iput(inode);
169
                        return result;
170
                }
171
        }
172
        spin_unlock(&dcache_lock);
173
        result = d_alloc_root(inode);
174
        if (result == NULL) {
175
                iput(inode);
176
                return ERR_PTR(-ENOMEM);
177
        }
178
        result->d_flags |= DCACHE_NFSD_DISCONNECTED;
179
        return result;
180
}
181
 
182
static struct dentry *nfsd_get_dentry(struct super_block *sb, __u32 *fh,
183
                                             int len, int fhtype, int parent)
184
{
185
        if (sb->s_op->fh_to_dentry)
186
                return sb->s_op->fh_to_dentry(sb, fh, len, fhtype, parent);
187
        switch (fhtype) {
188
        case 1:
189
                if (len < 2)
190
                        break;
191
                if (parent)
192
                        break;
193
                return nfsd_iget(sb, fh[0], fh[1]);
194
 
195
        case 2:
196
                if (len < 3)
197
                        break;
198
                if (parent)
199
                        return nfsd_iget(sb,fh[2],0);
200
                return nfsd_iget(sb,fh[0],fh[1]);
201
        default: break;
202
        }
203
        return ERR_PTR(-EINVAL);
204
}
205
 
206
 
207
/* this routine links an IS_ROOT dentry into the dcache tree.  It gains "parent"
208
 * as a parent and "name" as a name
209
 * It should possibly go in dcache.c
210
 */
211
int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name)
212
{
213
        struct dentry *tdentry;
214
#ifdef NFSD_PARANOIA
215
        if (!IS_ROOT(target))
216
                printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name);
217
        if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED))
218
                printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name);
219
#endif
220
        tdentry = d_alloc(parent, name);
221
        if (tdentry == NULL)
222
                return -ENOMEM;
223
        d_move(target, tdentry);
224
 
225
        /* tdentry will have been made a "child" of target (the parent of target)
226
         * make it an IS_ROOT instead
227
         */
228
        spin_lock(&dcache_lock);
229
        list_del_init(&tdentry->d_child);
230
        tdentry->d_parent = tdentry;
231
        spin_unlock(&dcache_lock);
232
        d_rehash(target);
233
        dput(tdentry);
234
 
235
        /* if parent is properly connected, then we can assert that
236
         * the children are connected, but it must be a singluar (non-forking)
237
         * branch
238
         */
239
        if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) {
240
                while (target) {
241
                        target->d_flags &= ~DCACHE_NFSD_DISCONNECTED;
242
                        parent = target;
243
                        spin_lock(&dcache_lock);
244
                        if (list_empty(&parent->d_subdirs))
245
                                target = NULL;
246
                        else {
247
                                target = list_entry(parent->d_subdirs.next, struct dentry, d_child);
248
#ifdef NFSD_PARANOIA
249
                                /* must be only child */
250
                                if (target->d_child.next != &parent->d_subdirs
251
                                    || target->d_child.prev != &parent->d_subdirs)
252
                                        printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n",
253
                                               parent->d_name.name, target->d_name.name);
254
#endif
255
                        }
256
                        spin_unlock(&dcache_lock);
257
                }
258
        }
259
        return 0;
260
}
261
 
262
/* this routine finds the dentry of the parent of a given directory
263
 * it should be in the filesystem accessed by nfsd_operations
264
 * it assumes lookup("..") works.
265
 */
266
struct dentry *nfsd_findparent(struct dentry *child)
267
{
268
        struct dentry *tdentry, *pdentry;
269
        tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0});
270
        if (!tdentry)
271
                return ERR_PTR(-ENOMEM);
272
 
273
        /* I'm going to assume that if the returned dentry is different, then
274
         * it is well connected.  But nobody returns different dentrys do they?
275
         */
276
        down(&child->d_inode->i_sem);
277
        pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry);
278
        up(&child->d_inode->i_sem);
279
        d_drop(tdentry); /* we never want ".." hashed */
280
        if (!pdentry && tdentry->d_inode == NULL) {
281
                /* File system cannot find ".." ... sad but possible */
282
                pdentry = ERR_PTR(-EINVAL);
283
        }
284
        if (!pdentry) {
285
                /* I don't want to return a ".." dentry.
286
                 * I would prefer to return an unconnected "IS_ROOT" dentry,
287
                 * though a properly connected dentry is even better
288
                 */
289
                /* if first or last of alias list is not tdentry, use that
290
                 * else make a root dentry
291
                 */
292
                struct list_head *aliases = &tdentry->d_inode->i_dentry;
293
                spin_lock(&dcache_lock);
294
                if (aliases->next != aliases) {
295
                        pdentry = list_entry(aliases->next, struct dentry, d_alias);
296
                        if (pdentry == tdentry)
297
                                pdentry = list_entry(aliases->prev, struct dentry, d_alias);
298
                        if (pdentry == tdentry)
299
                                pdentry = NULL;
300
                        if (pdentry) dget_locked(pdentry);
301
                }
302
                spin_unlock(&dcache_lock);
303
                if (pdentry == NULL) {
304
                        pdentry = d_alloc_root(tdentry->d_inode);
305
                        if (pdentry) {
306
                                igrab(tdentry->d_inode);
307
                                pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
308
                                pdentry->d_op = child->d_op;
309
                        }
310
                }
311
                if (pdentry == NULL)
312
                        pdentry = ERR_PTR(-ENOMEM);
313
        }
314
        dput(tdentry); /* it is not hashed, it will be discarded */
315
        return pdentry;
316
}
317
 
318
static struct dentry *splice(struct dentry *child, struct dentry *parent)
319
{
320
        int err = 0, nerr;
321
        struct qstr qs;
322
        char namebuf[256];
323
        struct list_head *lp;
324
        /* child is an IS_ROOT (anonymous) dentry, but it is hypothesised that
325
         * it should be a child of parent.
326
         * We see if we can find a name and, if we can - splice it in.
327
         * We lookup the name before locking (i_sem) the directory as namelookup
328
         * also claims i_sem.  If the name gets changed then we will loop around
329
         * and try again in find_fh_dentry.
330
         */
331
 
332
        nerr = nfsd_get_name(parent, namebuf, child);
333
 
334
        /*
335
         * We now claim the parent i_sem so that no-one else tries to create
336
         * a dentry in the parent while we are.
337
         */
338
 
339
        down(&parent->d_inode->i_sem);
340
 
341
        /* Now, things might have changed while we waited.
342
         * Possibly a friendly filesystem found child and spliced it in in response
343
         * to a lookup (though nobody does this yet).  In this case, just succeed.
344
         */
345
        if (child->d_parent == parent) goto out;
346
 
347
        /* Possibly a new dentry has been made for this child->d_inode in
348
         * parent by a lookup.  In this case return that dentry. Caller must
349
         * notice and act accordingly
350
         */
351
        spin_lock(&dcache_lock);
352
        list_for_each(lp, &child->d_inode->i_dentry) {
353
                struct dentry *tmp = list_entry(lp,struct dentry, d_alias);
354
                if (!list_empty(&tmp->d_hash) &&
355
                    tmp->d_parent == parent) {
356
                        child = dget_locked(tmp);
357
                        spin_unlock(&dcache_lock);
358
                        goto out;
359
                }
360
        }
361
        spin_unlock(&dcache_lock);
362
 
363
        /* now we need that name.  If there was an error getting it, now is th
364
         * time to bail out.
365
         */
366
        if ((err = nerr))
367
                goto out;
368
        qs.name = namebuf;
369
        qs.len = strlen(namebuf);
370
        if (find_inode_number(parent, &qs) != 0) {
371
                /* Now that IS odd.  I wonder what it means... */
372
                err = -EEXIST;
373
                printk("nfsd-fh: found a name that I didn't expect: %s/%s\n", parent->d_name.name, qs.name);
374
                goto out;
375
        }
376
        err = d_splice(child, parent, &qs);
377
        dprintk("nfsd_fh: found name %s for ino %ld\n", child->d_name.name, child->d_inode->i_ino);
378
 out:
379
        up(&parent->d_inode->i_sem);
380
        if (err)
381
                return ERR_PTR(err);
382
        else
383
                return child;
384
}
385
 
386
/*
387
 * This is the basic lookup mechanism for turning an NFS file handle
388
 * into a dentry.
389
 * We use nfsd_iget and if that doesn't return a suitably connected dentry,
390
 * we try to find the parent, and the parent of that and so-on until a
391
 * connection if made.
392
 */
393
static struct dentry *
394
find_fh_dentry(struct super_block *sb, __u32 *datap, int len, int fhtype, int needpath)
395
{
396
        struct dentry *dentry, *result = NULL;
397
        struct dentry *tmp;
398
        int err = -ESTALE;
399
        /* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free)
400
         * dcache path ever exists, as otherwise two partial paths might get
401
         * joined together, which would be very confusing.
402
         * If there is ever an unconnected non-root directory, then this lock
403
         * must be held.
404
         */
405
 
406
 
407
        nfsdstats.fh_lookup++;
408
        /*
409
         * Attempt to find the inode.
410
         */
411
 retry:
412
        down(&sb->s_nfsd_free_path_sem);
413
        result = nfsd_get_dentry(sb, datap, len, fhtype, 0);
414
        if (IS_ERR(result)
415
            || !(result->d_flags & DCACHE_NFSD_DISCONNECTED)
416
            || (!S_ISDIR(result->d_inode->i_mode) && ! needpath)) {
417
                up(&sb->s_nfsd_free_path_sem);
418
 
419
                err = PTR_ERR(result);
420
                if (IS_ERR(result))
421
                        goto err_out;
422
                if ((result->d_flags & DCACHE_NFSD_DISCONNECTED))
423
                        nfsdstats.fh_anon++;
424
                return result;
425
        }
426
 
427
        /* It's a directory, or we are required to confirm the file's
428
         * location in the tree.
429
         */
430
        dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,datap[0]);
431
 
432
        if (!S_ISDIR(result->d_inode->i_mode)) {
433
                nfsdstats.fh_nocache_nondir++;
434
                        /* need to iget dirino and make sure this inode is in that directory */
435
                        dentry = nfsd_get_dentry(sb, datap, len, fhtype, 1);
436
                        err = PTR_ERR(dentry);
437
                        if (IS_ERR(dentry))
438
                                goto err_result;
439
                        err = -ESTALE;
440
                        if (!dentry->d_inode
441
                            || !S_ISDIR(dentry->d_inode->i_mode)) {
442
                                goto err_dentry;
443
                        }
444
                        tmp = splice(result, dentry);
445
                        err = PTR_ERR(tmp);
446
                        if (IS_ERR(tmp))
447
                                goto err_dentry;
448
                        if (tmp != result) {
449
                                /* it is safe to just use tmp instead, but we must discard result first */
450
                                d_drop(result);
451
                                dput(result);
452
                                result = tmp;
453
                        }
454
        } else {
455
                nfsdstats.fh_nocache_dir++;
456
                dentry = dget(result);
457
        }
458
 
459
        while(dentry->d_flags & DCACHE_NFSD_DISCONNECTED) {
460
                /* LOOP INVARIANT */
461
                /* haven't found a place in the tree yet, but we do have a free path
462
                 * from dentry down to result, and dentry is a directory.
463
                 * Have a hold on dentry and result */
464
                struct dentry *pdentry;
465
                struct inode *parent;
466
 
467
                pdentry = nfsd_findparent(dentry);
468
                err = PTR_ERR(pdentry);
469
                if (IS_ERR(pdentry))
470
                        goto err_dentry;
471
                parent = pdentry->d_inode;
472
                err = -EACCES;
473
                if (!parent) {
474
                        dput(pdentry);
475
                        goto err_dentry;
476
                }
477
 
478
                tmp = splice(dentry, pdentry);
479
                if (tmp != dentry) {
480
                        /* Something wrong.  We need to drop the whole dentry->result path
481
                         * whatever it was
482
                         */
483
                        struct dentry *d;
484
                        for (d=result ; d ; d=(d->d_parent == d)?NULL:d->d_parent)
485
                                d_drop(d);
486
                }
487
                if (IS_ERR(tmp)) {
488
                        err = PTR_ERR(tmp);
489
                        dput(pdentry);
490
                        goto err_dentry;
491
                }
492
                if (tmp != dentry) {
493
                        /* we lost a race,  try again
494
                         */
495
                        dput(pdentry);
496
                        dput(tmp);
497
                        dput(dentry);
498
                        dput(result);   /* this will discard the whole free path, so we can up the semaphore */
499
                        up(&sb->s_nfsd_free_path_sem);
500
                        goto retry;
501
                }
502
                dput(dentry);
503
                dentry = pdentry;
504
        }
505
        dput(dentry);
506
        up(&sb->s_nfsd_free_path_sem);
507
        return result;
508
 
509
err_dentry:
510
        dput(dentry);
511
err_result:
512
        dput(result);
513
        up(&sb->s_nfsd_free_path_sem);
514
err_out:
515
        if (err == -ESTALE)
516
                nfsdstats.fh_stale++;
517
        return ERR_PTR(err);
518
}
519
 
520
/*
521
 * Perform sanity checks on the dentry in a client's file handle.
522
 *
523
 * Note that the file handle dentry may need to be freed even after
524
 * an error return.
525
 *
526
 * This is only called at the start of an nfsproc call, so fhp points to
527
 * a svc_fh which is all 0 except for the over-the-wire file handle.
528
 */
529
u32
530
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
531
{
532
        struct knfsd_fh *fh = &fhp->fh_handle;
533
        struct svc_export *exp;
534
        struct dentry   *dentry;
535
        struct inode    *inode;
536
        u32             error = 0;
537
 
538
        dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp));
539
 
540
        /* keep this filehandle for possible reference  when encoding attributes */
541
        rqstp->rq_reffh = fh;
542
 
543
        if (!fhp->fh_dentry) {
544
                kdev_t xdev = NODEV;
545
                ino_t xino = 0;
546
                __u32 *datap=NULL;
547
                int data_left = fh->fh_size/4;
548
                int nfsdev;
549
                int fsid = 0;
550
 
551
                error = nfserr_stale;
552
                if (rqstp->rq_vers == 3)
553
                        error = nfserr_badhandle;
554
                if (fh->fh_version == 1) {
555
 
556
                        datap = fh->fh_auth;
557
                        if (--data_left<0) goto out;
558
                        switch (fh->fh_auth_type) {
559
                        case 0: break;
560
                        default: goto out;
561
                        }
562
 
563
                        switch (fh->fh_fsid_type) {
564
                        case 0:
565
                                if ((data_left-=2)<0) goto out;
566
                                nfsdev = ntohl(*datap++);
567
                                xdev = MKDEV(nfsdev>>16, nfsdev&0xFFFF);
568
                                xino = *datap++;
569
                                break;
570
                        case 1:
571
                                if ((data_left-=1)<0) goto out;
572
                                fsid = *datap++;
573
                                break;
574
                        default:
575
                                goto out;
576
                        }
577
                } else {
578
                        if (fh->fh_size != NFS_FHSIZE)
579
                                goto out;
580
                        /* assume old filehandle format */
581
                        xdev = u32_to_kdev_t(fh->ofh_xdev);
582
                        xino = u32_to_ino_t(fh->ofh_xino);
583
                }
584
 
585
                /*
586
                 * Look up the export entry.
587
                 */
588
                error = nfserr_stale;
589
                if (fh->fh_version == 1 && fh->fh_fsid_type == 1)
590
                        exp = exp_get_fsid(rqstp->rq_client, fsid);
591
                else
592
                        exp = exp_get(rqstp->rq_client, xdev, xino);
593
 
594
                if (!exp)
595
                        /* export entry revoked */
596
                        goto out;
597
 
598
                /* Check if the request originated from a secure port. */
599
                error = nfserr_perm;
600
                if (!rqstp->rq_secure && EX_SECURE(exp)) {
601
                        printk(KERN_WARNING
602
                               "nfsd: request from insecure port (%08x:%d)!\n",
603
                               ntohl(rqstp->rq_addr.sin_addr.s_addr),
604
                               ntohs(rqstp->rq_addr.sin_port));
605
                        goto out;
606
                }
607
 
608
                /* Set user creds if we haven't done so already. */
609
                nfsd_setuser(rqstp, exp);
610
 
611
                /*
612
                 * Look up the dentry using the NFS file handle.
613
                 */
614
                error = nfserr_stale;
615
                if (rqstp->rq_vers == 3)
616
                        error = nfserr_badhandle;
617
 
618
                if (fh->fh_version == 1) {
619
                        /* if fileid_type != 0, and super_operations provide fh_to_dentry lookup,
620
                         *  then should use that */
621
                        switch (fh->fh_fileid_type) {
622
                        case 0:
623
                                dentry = dget(exp->ex_dentry);
624
                                /* need to revalidate the inode */
625
                                inode = dentry->d_inode;
626
                                if (inode->i_op && inode->i_op->revalidate)
627
                                        if (inode->i_op->revalidate(dentry)) {
628
                                                dput(dentry);
629
                                                dentry = ERR_PTR(-ESTALE);
630
                                        }
631
                                break;
632
                        default:
633
                                dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
634
                                                        datap, data_left, fh->fh_fileid_type,
635
                                                        !(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
636
                        }
637
                } else {
638
                        __u32 tfh[3];
639
                        tfh[0] = fh->ofh_ino;
640
                        tfh[1] = fh->ofh_generation;
641
                        tfh[2] = fh->ofh_dirino;
642
                        dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
643
                                                tfh, 3, fh->ofh_dirino?2:1,
644
                                                !(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
645
                }
646
                if (IS_ERR(dentry)) {
647
                        if (PTR_ERR(dentry) != -EINVAL)
648
                                error = nfserrno(PTR_ERR(dentry));
649
                        goto out;
650
                }
651
#ifdef NFSD_PARANOIA
652
                if (S_ISDIR(dentry->d_inode->i_mode) &&
653
                    (dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) {
654
                        printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
655
                               dentry->d_parent->d_name.name, dentry->d_name.name);
656
                }
657
#endif
658
 
659
                fhp->fh_dentry = dentry;
660
                fhp->fh_export = exp;
661
                nfsd_nr_verified++;
662
        } else {
663
                /* just rechecking permissions
664
                 * (e.g. nfsproc_create calls fh_verify, then nfsd_create does as well)
665
                 */
666
                dprintk("nfsd: fh_verify - just checking\n");
667
                dentry = fhp->fh_dentry;
668
                exp = fhp->fh_export;
669
        }
670
 
671
        inode = dentry->d_inode;
672
 
673
        /* Type check. The correct error return for type mismatches
674
         * does not seem to be generally agreed upon. SunOS seems to
675
         * use EISDIR if file isn't S_IFREG; a comment in the NFSv3
676
         * spec says this is incorrect (implementation notes for the
677
         * write call).
678
         */
679
 
680
        /* Type can be negative to e.g. exclude directories from linking */
681
        if (type > 0 && (inode->i_mode & S_IFMT) != type) {
682
                if (type == S_IFDIR)
683
                        error = nfserr_notdir;
684
                else if ((inode->i_mode & S_IFMT) == S_IFDIR)
685
                        error = nfserr_isdir;
686
                else
687
                        error = nfserr_inval;
688
                goto out;
689
        }
690
        if (type < 0 && (inode->i_mode & S_IFMT) == -type) {
691
                error = (type == -S_IFDIR)? nfserr_isdir : nfserr_notdir;
692
                goto out;
693
        }
694
 
695
        /*
696
         * Security: Check that the export is valid for dentry <gam3@acm.org>
697
         */
698
        error = 0;
699
 
700
        if (!(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
701
                struct dentry *tdentry = dentry;
702
 
703
                while (tdentry != exp->ex_dentry && !IS_ROOT(tdentry)) {
704
                        struct dentry *parent = tdentry->d_parent;
705
 
706
                        /* make sure parents give x permission to user */
707
                        error = permission(parent->d_inode, MAY_EXEC);
708
                        if (error)
709
                                break;
710
                        tdentry = parent;
711
                }
712
                if (exp->ex_dentry != tdentry) {
713
                        error = nfserr_stale;
714
                        printk("fh_verify: no root_squashed access at %s/%s.\n",
715
                               dentry->d_parent->d_name.name,
716
                               dentry->d_name.name);
717
                        goto out;
718
                }
719
        }
720
 
721
        /* Finally, check access permissions. */
722
        if (!error) {
723
                error = nfsd_permission(exp, dentry, access);
724
        }
725
#ifdef NFSD_PARANOIA_EXTREME
726
        if (error) {
727
                printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
728
                       dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24));
729
        }
730
#endif
731
out:
732
        if (error == nfserr_stale)
733
                nfsdstats.fh_stale++;
734
        return error;
735
}
736
 
737
/*
738
 * Compose a file handle for an NFS reply.
739
 *
740
 * Note that when first composed, the dentry may not yet have
741
 * an inode.  In this case a call to fh_update should be made
742
 * before the fh goes out on the wire ...
743
 */
744
inline int _fh_update(struct dentry *dentry, struct svc_export *exp,
745
                      __u32 *datap, int *maxsize)
746
{
747
        struct super_block *sb = dentry->d_inode->i_sb;
748
 
749
        if (dentry == exp->ex_dentry) {
750
                *maxsize = 0;
751
                return 0;
752
        }
753
 
754
        if (sb->s_op->dentry_to_fh) {
755
                int need_parent = !S_ISDIR(dentry->d_inode->i_mode) &&
756
                        !(exp->ex_flags & NFSEXP_NOSUBTREECHECK);
757
 
758
                int type = sb->s_op->dentry_to_fh(dentry, datap, maxsize, need_parent);
759
                return type;
760
        }
761
 
762
        if (*maxsize < 2)
763
                return 255;
764
        *datap++ = ino_t_to_u32(dentry->d_inode->i_ino);
765
        *datap++ = dentry->d_inode->i_generation;
766
        if (*maxsize ==2 ||
767
            S_ISDIR(dentry->d_inode->i_mode) ||
768
            (exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
769
                *maxsize = 2;
770
                return 1;
771
        }
772
        *datap++ = ino_t_to_u32(dentry->d_parent->d_inode->i_ino);
773
        *maxsize = 3;
774
        return 2;
775
}
776
 
777
/*
778
 * for composing old style file handles
779
 */
780
inline void _fh_update_old(struct dentry *dentry, struct svc_export *exp,
781
                           struct knfsd_fh *fh)
782
{
783
        fh->ofh_ino = ino_t_to_u32(dentry->d_inode->i_ino);
784
        fh->ofh_generation = dentry->d_inode->i_generation;
785
        if (S_ISDIR(dentry->d_inode->i_mode) ||
786
            (exp->ex_flags & NFSEXP_NOSUBTREECHECK))
787
                fh->ofh_dirino = 0;
788
}
789
 
790
int
791
fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, struct svc_fh *ref_fh)
792
{
793
        /* ref_fh is a reference file handle.
794
         * if it is non-null, then we should compose a filehandle which is
795
         * of the same version, where possible.
796
         * Currently, that means that if ref_fh->fh_handle.fh_version == 0xca
797
         * Then create a 32byte filehandle using nfs_fhbase_old
798
         * But only do this if dentry_to_fh is not available
799
         *
800
         */
801
 
802
        struct inode * inode = dentry->d_inode;
803
        struct dentry *parent = dentry->d_parent;
804
        __u32 *datap;
805
 
806
        dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
807
                exp->ex_dev, (long) exp->ex_ino,
808
                parent->d_name.name, dentry->d_name.name,
809
                (inode ? inode->i_ino : 0));
810
 
811
        if (fhp->fh_locked || fhp->fh_dentry) {
812
                printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
813
                        parent->d_name.name, dentry->d_name.name);
814
        }
815
        if (fhp->fh_maxsize < NFS_FHSIZE)
816
                printk(KERN_ERR "fh_compose: called with maxsize %d! %s/%s\n",
817
                       fhp->fh_maxsize, parent->d_name.name, dentry->d_name.name);
818
 
819
        fhp->fh_dentry = dentry; /* our internal copy */
820
        fhp->fh_export = exp;
821
 
822
        if (ref_fh &&
823
            ref_fh->fh_handle.fh_version == 0xca &&
824
            parent->d_inode->i_sb->s_op->dentry_to_fh == NULL) {
825
                /* old style filehandle please */
826
                memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE);
827
                fhp->fh_handle.fh_size = NFS_FHSIZE;
828
                fhp->fh_handle.ofh_dcookie = 0xfeebbaca;
829
                fhp->fh_handle.ofh_dev =  htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
830
                fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
831
                fhp->fh_handle.ofh_xino = ino_t_to_u32(exp->ex_ino);
832
                fhp->fh_handle.ofh_dirino = ino_t_to_u32(dentry->d_parent->d_inode->i_ino);
833
                if (inode)
834
                        _fh_update_old(dentry, exp, &fhp->fh_handle);
835
        } else {
836
                fhp->fh_handle.fh_version = 1;
837
                fhp->fh_handle.fh_auth_type = 0;
838
                datap = fhp->fh_handle.fh_auth+0;
839
                if ((exp->ex_flags & NFSEXP_FSID) &&
840
                    (!ref_fh || ref_fh->fh_handle.fh_fsid_type == 1)) {
841
                        fhp->fh_handle.fh_fsid_type = 1;
842
                        /* fsid_type 1 == 4 bytes filesystem id */
843
                        *datap++ = exp->ex_fsid;
844
                        fhp->fh_handle.fh_size = 2*4;
845
                } else {
846
                        fhp->fh_handle.fh_fsid_type = 0;
847
                        /* fsid_type 0 == 2byte major, 2byte minor, 4byte inode */
848
                        *datap++ = htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
849
                        *datap++ = ino_t_to_u32(exp->ex_ino);
850
                        fhp->fh_handle.fh_size = 3*4;
851
                }
852
                if (inode) {
853
                        int size = fhp->fh_maxsize/4 - 3;
854
                        fhp->fh_handle.fh_fileid_type =
855
                                _fh_update(dentry, exp, datap, &size);
856
                        fhp->fh_handle.fh_size += size*4;
857
                }
858
        }
859
 
860
        nfsd_nr_verified++;
861
        if (fhp->fh_handle.fh_fileid_type == 255)
862
                return nfserr_opnotsupp;
863
        return 0;
864
}
865
 
866
/*
867
 * Update file handle information after changing a dentry.
868
 * This is only called by nfsd_create, nfsd_create_v3 and nfsd_proc_create
869
 */
870
int
871
fh_update(struct svc_fh *fhp)
872
{
873
        struct dentry *dentry;
874
        __u32 *datap;
875
 
876
        if (!fhp->fh_dentry)
877
                goto out_bad;
878
 
879
        dentry = fhp->fh_dentry;
880
        if (!dentry->d_inode)
881
                goto out_negative;
882
        if (fhp->fh_handle.fh_version != 1) {
883
                _fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle);
884
        } else {
885
                int size;
886
                if (fhp->fh_handle.fh_fileid_type != 0)
887
                        goto out_uptodate;
888
                datap = fhp->fh_handle.fh_auth+
889
                        fhp->fh_handle.fh_size/4 -1;
890
                size = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4;
891
                fhp->fh_handle.fh_fileid_type =
892
                        _fh_update(dentry, fhp->fh_export, datap, &size);
893
                fhp->fh_handle.fh_size += size*4;
894
        }
895
out:
896
        return 0;
897
 
898
out_bad:
899
        printk(KERN_ERR "fh_update: fh not verified!\n");
900
        goto out;
901
out_negative:
902
        printk(KERN_ERR "fh_update: %s/%s still negative!\n",
903
                dentry->d_parent->d_name.name, dentry->d_name.name);
904
        goto out;
905
out_uptodate:
906
        printk(KERN_ERR "fh_update: %s/%s already up-to-date!\n",
907
                dentry->d_parent->d_name.name, dentry->d_name.name);
908
        goto out;
909
}
910
 
911
/*
912
 * Release a file handle.
913
 */
914
void
915
fh_put(struct svc_fh *fhp)
916
{
917
        struct dentry * dentry = fhp->fh_dentry;
918
        if (dentry) {
919
                fh_unlock(fhp);
920
                fhp->fh_dentry = NULL;
921
                dput(dentry);
922
                nfsd_nr_put++;
923
        }
924
        return;
925
}

powered by: WebSVN 2.1.0

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