hammer2 - cleanup, stabilization pass
[dragonfly.git] / sys / vfs / hammer2 / hammer2_vfsops.c
1 /*-
2  * Copyright (c) 2011, 2012 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
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
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/nlookup.h>
38 #include <sys/vnode.h>
39 #include <sys/mount.h>
40 #include <sys/fcntl.h>
41 #include <sys/buf.h>
42 #include <sys/uuid.h>
43 #include <sys/vfsops.h>
44 #include <sys/sysctl.h>
45
46 #include "hammer2.h"
47 #include "hammer2_disk.h"
48 #include "hammer2_mount.h"
49
50 struct hammer2_sync_info {
51         int error;
52         int waitfor;
53 };
54
55 int hammer2_debug;
56
57 SYSCTL_NODE(_vfs, OID_AUTO, hammer2, CTLFLAG_RW, 0, "HAMMER2 filesystem");
58
59 SYSCTL_INT(_vfs_hammer2, OID_AUTO, debug, CTLFLAG_RW,
60            &hammer2_debug, 0, "");
61
62 static int hammer2_vfs_init(struct vfsconf *conf);
63 static int hammer2_vfs_mount(struct mount *mp, char *path, caddr_t data,
64                                 struct ucred *cred);
65 static int hammer2_remount(struct mount *, char *, struct vnode *,
66                                 struct ucred *);
67 static int hammer2_vfs_unmount(struct mount *mp, int mntflags);
68 static int hammer2_vfs_root(struct mount *mp, struct vnode **vpp);
69 static int hammer2_vfs_statfs(struct mount *mp, struct statfs *sbp,
70                                 struct ucred *cred);
71 static int hammer2_vfs_statvfs(struct mount *mp, struct statvfs *sbp,
72                                 struct ucred *cred);
73 static int hammer2_vfs_sync(struct mount *mp, int waitfor);
74 static int hammer2_vfs_vget(struct mount *mp, struct vnode *dvp,
75                                 ino_t ino, struct vnode **vpp);
76 static int hammer2_vfs_fhtovp(struct mount *mp, struct vnode *rootvp,
77                                 struct fid *fhp, struct vnode **vpp);
78 static int hammer2_vfs_vptofh(struct vnode *vp, struct fid *fhp);
79 static int hammer2_vfs_checkexp(struct mount *mp, struct sockaddr *nam,
80                                 int *exflagsp, struct ucred **credanonp);
81
82 static int hammer2_install_volume_header(hammer2_mount_t *hmp);
83 static int hammer2_sync_scan1(struct mount *mp, struct vnode *vp, void *data);
84 static int hammer2_sync_scan2(struct mount *mp, struct vnode *vp, void *data);
85
86 /*
87  * HAMMER2 vfs operations.
88  */
89 static struct vfsops hammer2_vfsops = {
90         .vfs_init       = hammer2_vfs_init,
91         .vfs_sync       = hammer2_vfs_sync,
92         .vfs_mount      = hammer2_vfs_mount,
93         .vfs_unmount    = hammer2_vfs_unmount,
94         .vfs_root       = hammer2_vfs_root,
95         .vfs_statfs     = hammer2_vfs_statfs,
96         .vfs_statvfs    = hammer2_vfs_statvfs,
97         .vfs_vget       = hammer2_vfs_vget,
98         .vfs_vptofh     = hammer2_vfs_vptofh,
99         .vfs_fhtovp     = hammer2_vfs_fhtovp,
100         .vfs_checkexp   = hammer2_vfs_checkexp
101 };
102
103 MALLOC_DEFINE(M_HAMMER2, "HAMMER2-mount", "");
104
105 VFS_SET(hammer2_vfsops, hammer2, 0);
106 MODULE_VERSION(hammer2, 1);
107
108 static
109 int
110 hammer2_vfs_init(struct vfsconf *conf)
111 {
112         int error;
113
114         error = 0;
115
116         if (HAMMER2_BLOCKREF_BYTES != sizeof(struct hammer2_blockref))
117                 error = EINVAL;
118         if (HAMMER2_INODE_BYTES != sizeof(struct hammer2_inode_data))
119                 error = EINVAL;
120         if (HAMMER2_ALLOCREF_BYTES != sizeof(struct hammer2_allocref))
121                 error = EINVAL;
122         if (HAMMER2_VOLUME_BYTES != sizeof(struct hammer2_volume_data))
123                 error = EINVAL;
124
125         if (error)
126                 kprintf("HAMMER2 structure size mismatch; cannot continue.\n");
127
128         return (error);
129 }
130
131 /*
132  * Mount or remount HAMMER2 fileystem from physical media
133  *
134  *      mountroot
135  *              mp              mount point structure
136  *              path            NULL
137  *              data            <unused>
138  *              cred            <unused>
139  *
140  *      mount
141  *              mp              mount point structure
142  *              path            path to mount point
143  *              data            pointer to argument structure in user space
144  *                      volume  volume path (device@LABEL form)
145  *                      hflags  user mount flags
146  *              cred            user credentials
147  *
148  * RETURNS:     0       Success
149  *              !0      error number
150  */
151 static
152 int
153 hammer2_vfs_mount(struct mount *mp, char *path, caddr_t data,
154               struct ucred *cred)
155 {
156         struct hammer2_mount_info info;
157         hammer2_mount_t *hmp;
158         hammer2_key_t lhc;
159         struct vnode *devvp;
160         struct nlookupdata nd;
161         hammer2_chain_t *parent;
162         hammer2_chain_t *schain;
163         hammer2_chain_t *rchain;
164         char devstr[MNAMELEN];
165         size_t size;
166         size_t done;
167         char *dev;
168         char *label;
169         int ronly = 1;
170         int error;
171
172         hmp = NULL;
173         dev = NULL;
174         label = NULL;
175         devvp = NULL;
176
177         kprintf("hammer2_mount\n");
178
179         if (path == NULL) {
180                 /*
181                  * Root mount
182                  */
183                 return (EOPNOTSUPP);
184         } else {
185                 /*
186                  * Non-root mount or updating a mount
187                  */
188                 error = copyin(data, &info, sizeof(info));
189                 if (error)
190                         return (error);
191
192                 error = copyinstr(info.volume, devstr, MNAMELEN - 1, &done);
193                 if (error)
194                         return (error);
195
196                 /* Extract device and label */
197                 dev = devstr;
198                 label = strchr(devstr, '@');
199                 if (label == NULL ||
200                     ((label + 1) - dev) > done) {
201                         return (EINVAL);
202                 }
203                 *label = '\0';
204                 label++;
205                 if (*label == '\0')
206                         return (EINVAL);
207
208                 if (mp->mnt_flag & MNT_UPDATE) {
209                         /* Update mount */
210                         /* HAMMER2 implements NFS export via mountctl */
211                         hmp = MPTOH2(mp);
212                         devvp = hmp->devvp;
213                         error = hammer2_remount(mp, path, devvp, cred);
214                         return error;
215                 }
216         }
217
218         /*
219          * New non-root mount
220          */
221         /* Lookup name and verify it refers to a block device */
222         error = nlookup_init(&nd, dev, UIO_SYSSPACE, NLC_FOLLOW);
223         if (error == 0)
224                 error = nlookup(&nd);
225         if (error == 0)
226                 error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
227         nlookup_done(&nd);
228
229         if (error == 0) {
230                 if (vn_isdisk(devvp, &error))
231                         error = vfs_mountedon(devvp);
232         }
233         if (error == 0 && vcount(devvp) > 0)
234                 error = EBUSY;
235
236         /*
237          * Now open the device
238          */
239         if (error == 0) {
240                 ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
241                 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
242                 error = vinvalbuf(devvp, V_SAVE, 0, 0);
243                 if (error == 0) {
244                         error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE,
245                                          FSCRED, NULL);
246                 }
247                 vn_unlock(devvp);
248         }
249         if (error && devvp) {
250                 vrele(devvp);
251                 devvp = NULL;
252         }
253         if (error)
254                 return error;
255
256         /*
257          * Block device opened successfully, finish initializing the
258          * mount structure.
259          *
260          * From this point on we have to call hammer2_unmount() on failure.
261          */
262         hmp = kmalloc(sizeof(*hmp), M_HAMMER2, M_WAITOK | M_ZERO);
263         mp->mnt_data = (qaddr_t)hmp;
264         hmp->mp = mp;
265         hmp->ronly = ronly;
266         hmp->devvp = devvp;
267         kmalloc_create(&hmp->minode, "HAMMER2-inodes");
268         kmalloc_create(&hmp->mchain, "HAMMER2-chains");
269         
270         mp->mnt_flag = MNT_LOCAL;
271         mp->mnt_kern_flag |= MNTK_ALL_MPSAFE;   /* all entry pts are SMP */
272
273         /*
274          * vchain setup. vchain.data is special cased to NULL.  vchain.refs
275          * is initialized and will never drop to 0.
276          */
277         hmp->vchain.bref.type = HAMMER2_BREF_TYPE_VOLUME;
278         hmp->vchain.refs = 1;
279         hmp->vchain.data = (void *)&hmp->voldata;
280         hmp->vchain.bref.data_off = 0 | HAMMER2_PBUFRADIX;
281         /* hmp->vchain.u.xxx is left NULL */
282         lockinit(&hmp->vchain.lk, "volume", 0, LK_CANRECURSE);
283
284         /*
285          * Install the volume header
286          */
287         error = hammer2_install_volume_header(hmp);
288         if (error) {
289                 hammer2_vfs_unmount(mp, MNT_FORCE);
290                 return error;
291         }
292
293         /*
294          * required mount structure initializations
295          */
296         mp->mnt_stat.f_iosize = HAMMER2_PBUFSIZE;
297         mp->mnt_stat.f_bsize = HAMMER2_PBUFSIZE;
298
299         mp->mnt_vstat.f_frsize = HAMMER2_PBUFSIZE;
300         mp->mnt_vstat.f_bsize = HAMMER2_PBUFSIZE;
301
302         /*
303          * First locate the super-root inode, which is key 0 relative to the
304          * volume header's blockset.
305          *
306          * Then locate the root inode by scanning the directory keyspace
307          * represented by the label.
308          */
309         lhc = hammer2_dirhash(label, strlen(label));
310         parent = &hmp->vchain;
311         hammer2_chain_ref(hmp, parent);
312         hammer2_chain_lock(hmp, parent);
313         schain = hammer2_chain_lookup(hmp, &parent,
314                                       HAMMER2_SROOT_KEY, HAMMER2_SROOT_KEY, 0);
315         hammer2_chain_put(hmp, parent);
316         if (schain == NULL) {
317                 kprintf("hammer2_mount: invalid super-root\n");
318                 hammer2_vfs_unmount(mp, MNT_FORCE);
319                 return EINVAL;
320         }
321
322         parent = schain;
323         hammer2_chain_ref(hmp, parent); /* parent: lock+ref, schain: ref */
324         rchain = hammer2_chain_lookup(hmp, &parent,
325                                       lhc, lhc + HAMMER2_DIRHASH_LOMASK,
326                                       0);
327         while (rchain) {
328                 if (rchain->bref.type == HAMMER2_BREF_TYPE_INODE &&
329                     rchain->u.ip &&
330                     strcmp(label, rchain->data->ipdata.filename) == 0) {
331                         break;
332                 }
333                 rchain = hammer2_chain_next(hmp, &parent, rchain,
334                                             lhc, lhc + HAMMER2_DIRHASH_LOMASK,
335                                             0);
336         }
337         hammer2_chain_put(hmp, parent);
338         if (rchain == NULL) {
339                 kprintf("hammer2_mount: root label not found\n");
340                 hammer2_chain_drop(hmp, schain);
341                 hammer2_vfs_unmount(mp, MNT_FORCE);
342                 return EINVAL;
343         }
344         hammer2_chain_unlock(hmp, rchain); /* rchain: ref */
345
346         hmp->schain = schain;           /* left held & unlocked */
347         hmp->rchain = rchain;           /* left held & unlocked */
348         hmp->iroot = rchain->u.ip;      /* implied hold from rchain */
349         kprintf("iroot %p\n", rchain->u.ip);
350
351         vfs_getnewfsid(mp);
352         vfs_add_vnodeops(mp, &hammer2_vnode_vops, &mp->mnt_vn_norm_ops);
353         vfs_add_vnodeops(mp, &hammer2_spec_vops, &mp->mnt_vn_spec_ops);
354         vfs_add_vnodeops(mp, &hammer2_fifo_vops, &mp->mnt_vn_fifo_ops);
355
356         copyinstr(info.volume, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
357         bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
358         bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
359         copyinstr(path, mp->mnt_stat.f_mntonname,
360                   sizeof(mp->mnt_stat.f_mntonname) - 1,
361                   &size);
362
363         hammer2_vfs_statfs(mp, &mp->mnt_stat, cred);
364
365         return 0;
366 }
367
368 static
369 int
370 hammer2_remount(struct mount *mp, char *path, struct vnode *devvp,
371                 struct ucred *cred)
372 {
373         return (0);
374 }
375
376 static
377 int
378 hammer2_vfs_unmount(struct mount *mp, int mntflags)
379 {
380         hammer2_mount_t *hmp;
381         int flags;
382         int error = 0;
383         int ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
384         struct vnode *devvp;
385
386         kprintf("hammer2_unmount\n");
387
388         hmp = MPTOH2(mp);
389         flags = 0;
390
391         if (mntflags & MNT_FORCE)
392                 flags |= FORCECLOSE;
393
394         hammer2_mount_exlock(hmp);
395
396         /*
397          * If mount initialization proceeded far enough we must flush
398          * its vnodes.
399          */
400         if (hmp->iroot)
401                 error = vflush(mp, 0, flags);
402
403         if (error)
404                 return error;
405
406         /*
407          * Flush any left over chains
408          */
409         if (hmp->vchain.flags & (HAMMER2_CHAIN_MODIFIED1 |
410                                  HAMMER2_CHAIN_SUBMODIFIED)) {
411                 hammer2_vfs_sync(mp, MNT_WAIT);
412         }
413         if (hmp->vchain.flags & (HAMMER2_CHAIN_MODIFIED1 |
414                                  HAMMER2_CHAIN_SUBMODIFIED)) {
415                 kprintf("hammer2_unmount: chains left over after final sync\n");
416         }
417
418         /*
419          * Cleanup the root and super-root chain elements (which should be
420          * clean).
421          */
422         hmp->iroot = NULL;
423         if (hmp->rchain) {
424                 KKASSERT(hmp->rchain->refs == 1);
425                 hammer2_chain_drop(hmp, hmp->rchain);
426                 hmp->rchain = NULL;
427         }
428         if (hmp->schain) {
429                 KKASSERT(hmp->schain->refs == 1);
430                 hammer2_chain_drop(hmp, hmp->schain);
431                 hmp->schain = NULL;
432         }
433
434         /*
435          * Finish up with the device vnode
436          */
437         if ((devvp = hmp->devvp) != NULL) {
438                 vinvalbuf(devvp, (ronly ? 0 : V_SAVE), 0, 0);
439                 hmp->devvp = NULL;
440                 VOP_CLOSE(devvp, (ronly ? FREAD : FREAD|FWRITE));
441                 vrele(devvp);
442                 devvp = NULL;
443         }
444
445         kmalloc_destroy(&hmp->minode);
446         kmalloc_destroy(&hmp->mchain);
447
448         hammer2_mount_unlock(hmp);
449
450         mp->mnt_data = NULL;
451         hmp->mp = NULL;
452         kfree(hmp, M_HAMMER2);
453
454         return (error);
455 }
456
457 static
458 int
459 hammer2_vfs_vget(struct mount *mp, struct vnode *dvp,
460              ino_t ino, struct vnode **vpp)
461 {
462         kprintf("hammer2_vget\n");
463         return (EOPNOTSUPP);
464 }
465
466 static
467 int
468 hammer2_vfs_root(struct mount *mp, struct vnode **vpp)
469 {
470         hammer2_mount_t *hmp;
471         int error;
472         struct vnode *vp;
473
474         kprintf("hammer2_root\n");
475
476         hmp = MPTOH2(mp);
477         hammer2_mount_exlock(hmp);
478         if (hmp->iroot == NULL) {
479                 *vpp = NULL;
480                 error = EINVAL;
481         } else {
482                 vp = hammer2_igetv(hmp->iroot, &error);
483                 *vpp = vp;
484                 if (vp == NULL)
485                         kprintf("vnodefail\n");
486         }
487         hammer2_mount_unlock(hmp);
488
489         return (error);
490 }
491
492 static
493 int
494 hammer2_vfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
495 {
496         hammer2_mount_t *hmp;
497
498         hmp = MPTOH2(mp);
499
500         mp->mnt_stat.f_files = 10;
501         mp->mnt_stat.f_bfree = 10;
502         mp->mnt_stat.f_bavail = mp->mnt_stat.f_bfree;
503
504         *sbp = mp->mnt_stat;
505         return (0);
506 }
507
508 static
509 int
510 hammer2_vfs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
511 {
512         hammer2_mount_t *hmp;
513
514         hmp = MPTOH2(mp);
515
516         mp->mnt_vstat.f_bsize = HAMMER2_PBUFSIZE;
517         mp->mnt_vstat.f_files = 0;
518         mp->mnt_vstat.f_bavail = mp->mnt_stat.f_bfree;
519
520         *sbp = mp->mnt_vstat;
521         return (0);
522 }
523
524 /*
525  * Sync the entire filesystem; this is called from the filesystem syncer
526  * process periodically and whenever a user calls sync(1) on the hammer
527  * mountpoint.
528  *
529  * Currently is actually called from the syncer! \o/
530  *
531  * This task will have to snapshot the state of the dirty inode chain.
532  * From that, it will have to make sure all of the inodes on the dirty
533  * chain have IO initiated. We make sure that io is initiated for the root
534  * block.
535  *
536  * If waitfor is set, we wait for media to acknowledge the new rootblock.
537  *
538  * THINKS: side A vs side B, to have sync not stall all I/O?
539  */
540 static
541 int
542 hammer2_vfs_sync(struct mount *mp, int waitfor)
543 {
544         struct hammer2_sync_info info;
545         hammer2_mount_t *hmp;
546         int flags;
547         int error;
548         int haswork;
549
550         kprintf("hammer2_sync \n");
551         hmp = MPTOH2(mp);
552
553         flags = VMSC_GETVP;
554         if (waitfor & MNT_LAZY)
555                 flags |= VMSC_ONEPASS;
556
557         info.error = 0;
558         info.waitfor = MNT_NOWAIT;
559         vmntvnodescan(mp, flags | VMSC_NOWAIT,
560                       hammer2_sync_scan1,
561                       hammer2_sync_scan2, &info);
562         if (info.error == 0 && (waitfor & MNT_WAIT)) {
563                 info.waitfor = waitfor;
564                     vmntvnodescan(mp, flags,
565                                   hammer2_sync_scan1,
566                                   hammer2_sync_scan2, &info);
567
568         }
569 #if 0
570         if (waitfor == MNT_WAIT) {
571                 /* XXX */
572         } else {
573                 /* XXX */
574         }
575 #endif
576         hammer2_chain_lock(hmp, &hmp->vchain);
577         if (hmp->vchain.flags &
578             (HAMMER2_CHAIN_MODIFIED1 | HAMMER2_CHAIN_SUBMODIFIED)) {
579                 hammer2_chain_flush(hmp, &hmp->vchain);
580                 haswork = 1;
581         } else {
582                 haswork = 0;
583         }
584         hammer2_chain_unlock(hmp, &hmp->vchain);
585         error = vinvalbuf(hmp->devvp, V_SAVE, 0, 0);
586         if (error == 0 && haswork) {
587                 struct buf *bp;
588
589                 kprintf("synchronize disk\n");
590
591                 bp = getpbuf(NULL);
592                 bp->b_bio1.bio_offset = 0;
593                 bp->b_bufsize = 0;
594                 bp->b_bcount = 0;
595                 bp->b_cmd = BUF_CMD_FLUSH;
596                 bp->b_bio1.bio_done = biodone_sync;
597                 bp->b_bio1.bio_flags |= BIO_SYNC;
598                 vn_strategy(hmp->devvp, &bp->b_bio1);
599                 biowait(&bp->b_bio1, "h2vol");
600                 relpbuf(bp, NULL);
601
602                 kprintf("flush volume header\n");
603
604                 bp = getblk(hmp->devvp, 0, HAMMER2_PBUFSIZE, 0, 0);
605                 bcopy(&hmp->voldata, bp->b_data, HAMMER2_PBUFSIZE);
606                 bawrite(bp);
607         }
608         return (error);
609 }
610
611
612 static int
613 hammer2_sync_scan1(struct mount *mp, struct vnode *vp, void *data)
614 {
615         hammer2_inode_t *ip;
616
617         ip = VTOI(vp);
618         if (vp->v_type == VNON || ip == NULL ||
619             ((ip->chain.flags & HAMMER2_CHAIN_MODIFIED1) == 0 &&
620              RB_EMPTY(&vp->v_rbdirty_tree))) {
621                 return(-1);
622         }
623         return(0);
624 }
625
626 static int
627 hammer2_sync_scan2(struct mount *mp, struct vnode *vp, void *data)
628 {
629         struct hammer2_sync_info *info = data;
630         hammer2_inode_t *ip;
631         int error;
632
633         ip = VTOI(vp);
634         if (vp->v_type == VNON || vp->v_type == VBAD ||
635             ((ip->chain.flags & HAMMER2_CHAIN_MODIFIED1) == 0 &&
636              RB_EMPTY(&vp->v_rbdirty_tree))) {
637                 return(0);
638         }
639         error = VOP_FSYNC(vp, MNT_NOWAIT, 0);
640         if (error)
641                 info->error = error;
642         return(0);
643 }
644
645 static
646 int
647 hammer2_vfs_vptofh(struct vnode *vp, struct fid *fhp)
648 {
649         return (0);
650 }
651
652 static
653 int
654 hammer2_vfs_fhtovp(struct mount *mp, struct vnode *rootvp,
655                struct fid *fhp, struct vnode **vpp)
656 {
657         return (0);
658 }
659
660 static
661 int
662 hammer2_vfs_checkexp(struct mount *mp, struct sockaddr *nam,
663                  int *exflagsp, struct ucred **credanonp)
664 {
665         return (0);
666 }
667
668 /*
669  * Support code for hammer2_mount().  Read, verify, and install the volume
670  * header into the HMP
671  *
672  * XXX read four volhdrs and use the one with the highest TID whos CRC
673  *     matches.
674  *
675  * XXX check iCRCs.
676  *
677  * XXX For filesystems w/ less than 4 volhdrs, make sure to not write to
678  *     nonexistant locations.
679  *
680  * XXX Record selected volhdr and ring updates to each of 4 volhdrs
681  */
682 static
683 int
684 hammer2_install_volume_header(hammer2_mount_t *hmp)
685 {
686         hammer2_volume_data_t *vd;
687         struct buf *bp;
688         hammer2_crc32_t crc0, crc, bcrc0, bcrc;
689         int error_reported;
690         int error;
691         int valid;
692         int i;
693
694         error_reported = 0;
695         error = 0;
696         valid = 0;
697         bp = NULL;
698
699         /*
700          * There are up to 4 copies of the volume header (syncs iterate
701          * between them so there is no single master).  We don't trust the
702          * volu_size field so we don't know precisely how large the filesystem
703          * is, so depend on the OS to return an error if we go beyond the
704          * block device's EOF.
705          */
706         for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
707                 error = bread(hmp->devvp, i * HAMMER2_RESERVE_BYTES64, 
708                               HAMMER2_VOLUME_BYTES, &bp);
709                 if (error) {
710                         brelse(bp);
711                         bp = NULL;
712                         continue;
713                 }
714
715                 vd = (struct hammer2_volume_data *) bp->b_data;
716                 if ((vd->magic != HAMMER2_VOLUME_ID_HBO) &&
717                     (vd->magic != HAMMER2_VOLUME_ID_ABO)) {
718                         brelse(bp);
719                         bp = NULL;
720                         continue;
721                 }
722
723                 if (vd->magic == HAMMER2_VOLUME_ID_ABO) {
724                         /* XXX: Reversed-endianness filesystem */
725                         kprintf("hammer2: reverse-endian filesystem detected");
726                         brelse(bp);
727                         bp = NULL;
728                         continue;
729                 }
730
731                 crc = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
732                 crc0 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC0_OFF,
733                                       HAMMER2_VOLUME_ICRC0_SIZE);
734                 bcrc = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
735                 bcrc0 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC1_OFF,
736                                        HAMMER2_VOLUME_ICRC1_SIZE);
737                 if ((crc0 != crc) || (bcrc0 != bcrc)) {
738                         kprintf("hammer2 volume header crc "
739                                 "mismatch copy #%d\t%08x %08x",
740                                 i, crc0, crc);
741                         error_reported = 1;
742                         brelse(bp);
743                         bp = NULL;
744                         continue;
745                 }
746                 if (valid == 0 || hmp->voldata.last_tid < vd->last_tid) {
747                         valid = 1;
748                         hmp->voldata = *vd;
749                 }
750                 brelse(bp);
751                 bp = NULL;
752         }
753         if (valid) {
754                 error = 0;
755                 if (error_reported)
756                         kprintf("hammer2: a valid volume header was found\n");
757         } else {
758                 error = EINVAL;
759                 kprintf("hammer2: no valid volume headers found!\n");
760         }
761         return (error);
762 }
763