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