hammer2 - more indirect block work, add advlock
[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         kprintf("iroot %p\n", hmp->iroot);
401         if (hmp->iroot)
402                 error = vflush(mp, 0, flags);
403
404         if (error)
405                 return error;
406
407         /*
408          * Work to do:
409          *      1) Wait on the flusher having no work; heat up if needed
410          *      2) Scan inode RB tree till all the inodes are free
411          *      3) Destroy the kmalloc inode zone
412          *      4) Free the mount point
413          */
414         hmp->iroot = NULL;
415         if (hmp->rchain) {
416                 KKASSERT(hmp->rchain->refs == 1);
417                 hammer2_chain_drop(hmp, hmp->rchain);
418                 hmp->rchain = NULL;
419         }
420         if (hmp->schain) {
421                 KKASSERT(hmp->schain->refs == 1);
422                 hammer2_chain_drop(hmp, hmp->schain);
423                 hmp->schain = NULL;
424         }
425         if ((devvp = hmp->devvp) != NULL) {
426                 vinvalbuf(devvp, (ronly ? 0 : V_SAVE), 0, 0);
427                 hmp->devvp = NULL;
428                 VOP_CLOSE(devvp, (ronly ? FREAD : FREAD|FWRITE));
429                 vrele(devvp);
430                 devvp = NULL;
431         }
432
433         kmalloc_destroy(&hmp->minode);
434         kmalloc_destroy(&hmp->mchain);
435
436         hammer2_mount_unlock(hmp);
437
438         mp->mnt_data = NULL;
439         hmp->mp = NULL;
440         kfree(hmp, M_HAMMER2);
441
442         return (error);
443 }
444
445 static
446 int
447 hammer2_vfs_vget(struct mount *mp, struct vnode *dvp,
448              ino_t ino, struct vnode **vpp)
449 {
450         kprintf("hammer2_vget\n");
451         return (EOPNOTSUPP);
452 }
453
454 static
455 int
456 hammer2_vfs_root(struct mount *mp, struct vnode **vpp)
457 {
458         hammer2_mount_t *hmp;
459         int error;
460         struct vnode *vp;
461
462         kprintf("hammer2_root\n");
463
464         hmp = MPTOH2(mp);
465         hammer2_mount_exlock(hmp);
466         if (hmp->iroot == NULL) {
467                 *vpp = NULL;
468                 error = EINVAL;
469         } else {
470                 vp = hammer2_igetv(hmp->iroot, &error);
471                 *vpp = vp;
472                 if (vp == NULL)
473                         kprintf("vnodefail\n");
474         }
475         hammer2_mount_unlock(hmp);
476
477         return (error);
478 }
479
480 static
481 int
482 hammer2_vfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
483 {
484         hammer2_mount_t *hmp;
485
486         hmp = MPTOH2(mp);
487
488         mp->mnt_stat.f_files = 10;
489         mp->mnt_stat.f_bfree = 10;
490         mp->mnt_stat.f_bavail = mp->mnt_stat.f_bfree;
491
492         *sbp = mp->mnt_stat;
493         return (0);
494 }
495
496 static
497 int
498 hammer2_vfs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
499 {
500         hammer2_mount_t *hmp;
501
502         hmp = MPTOH2(mp);
503
504         mp->mnt_vstat.f_bsize = HAMMER2_PBUFSIZE;
505         mp->mnt_vstat.f_files = 0;
506         mp->mnt_vstat.f_bavail = mp->mnt_stat.f_bfree;
507
508         *sbp = mp->mnt_vstat;
509         return (0);
510 }
511
512 /*
513  * Sync the entire filesystem; this is called from the filesystem syncer
514  * process periodically and whenever a user calls sync(1) on the hammer
515  * mountpoint.
516  *
517  * Currently is actually called from the syncer! \o/
518  *
519  * This task will have to snapshot the state of the dirty inode chain.
520  * From that, it will have to make sure all of the inodes on the dirty
521  * chain have IO initiated. We make sure that io is initiated for the root
522  * block.
523  *
524  * If waitfor is set, we wait for media to acknowledge the new rootblock.
525  *
526  * THINKS: side A vs side B, to have sync not stall all I/O?
527  */
528 static
529 int
530 hammer2_vfs_sync(struct mount *mp, int waitfor)
531 {
532         struct hammer2_sync_info info;
533         hammer2_mount_t *hmp;
534         int flags;
535         int error;
536
537         kprintf("hammer2_sync \n");
538         hmp = MPTOH2(mp);
539
540         flags = VMSC_GETVP;
541         if (waitfor & MNT_LAZY)
542                 flags |= VMSC_ONEPASS;
543
544         info.error = 0;
545         info.waitfor = MNT_NOWAIT;
546         vmntvnodescan(mp, flags | VMSC_NOWAIT,
547                       hammer2_sync_scan1,
548                       hammer2_sync_scan2, &info);
549         if (info.error == 0 && (waitfor & MNT_WAIT)) {
550                 info.waitfor = waitfor;
551                     vmntvnodescan(mp, flags,
552                                   hammer2_sync_scan1,
553                                   hammer2_sync_scan2, &info);
554
555         }
556 #if 0
557         if (waitfor == MNT_WAIT) {
558                 /* XXX */
559         } else {
560                 /* XXX */
561         }
562 #endif
563         hammer2_chain_lock(hmp, &hmp->vchain);
564         hammer2_chain_flush(hmp, &hmp->vchain, NULL);
565         hammer2_chain_unlock(hmp, &hmp->vchain);
566         error = vinvalbuf(hmp->devvp, V_SAVE, 0, 0);
567         if (error == 0) {
568                 struct buf *bp;
569
570                 bp = getpbuf(NULL);
571                 bp->b_bio1.bio_offset = 0;
572                 bp->b_bufsize = 0;
573                 bp->b_bcount = 0;
574                 bp->b_cmd = BUF_CMD_FLUSH;
575                 bp->b_bio1.bio_done = biodone_sync;
576                 bp->b_bio1.bio_flags |= BIO_SYNC;
577                 vn_strategy(hmp->devvp, &bp->b_bio1);
578                 biowait(&bp->b_bio1, "h2vol");
579                 relpbuf(bp, NULL);
580
581                 kprintf("flush volume header\n");
582
583                 bp = getblk(hmp->devvp, 0, HAMMER2_PBUFSIZE, 0, 0);
584                 bcopy(&hmp->voldata, bp->b_data, HAMMER2_PBUFSIZE);
585                 bawrite(bp);
586         }
587         return (error);
588 }
589
590
591 static int
592 hammer2_sync_scan1(struct mount *mp, struct vnode *vp, void *data)
593 {
594         hammer2_inode_t *ip;
595
596         ip = VTOI(vp);
597         if (vp->v_type == VNON || ip == NULL ||
598             ((ip->chain.flags & HAMMER2_CHAIN_MODIFIED) == 0 &&
599              RB_EMPTY(&vp->v_rbdirty_tree))) {
600                 return(-1);
601         }
602         return(0);
603 }
604
605 static int
606 hammer2_sync_scan2(struct mount *mp, struct vnode *vp, void *data)
607 {
608         struct hammer2_sync_info *info = data;
609         hammer2_inode_t *ip;
610         int error;
611
612         ip = VTOI(vp);
613         if (vp->v_type == VNON || vp->v_type == VBAD ||
614             ((ip->chain.flags & HAMMER2_CHAIN_MODIFIED) == 0 &&
615              RB_EMPTY(&vp->v_rbdirty_tree))) {
616                 return(0);
617         }
618         error = VOP_FSYNC(vp, MNT_NOWAIT, 0);
619         if (error)
620                 info->error = error;
621         return(0);
622 }
623
624 static
625 int
626 hammer2_vfs_vptofh(struct vnode *vp, struct fid *fhp)
627 {
628         return (0);
629 }
630
631 static
632 int
633 hammer2_vfs_fhtovp(struct mount *mp, struct vnode *rootvp,
634                struct fid *fhp, struct vnode **vpp)
635 {
636         return (0);
637 }
638
639 static
640 int
641 hammer2_vfs_checkexp(struct mount *mp, struct sockaddr *nam,
642                  int *exflagsp, struct ucred **credanonp)
643 {
644         return (0);
645 }
646
647 /*
648  * Support code for hammer2_mount().  Read, verify, and install the volume
649  * header into the HMP
650  *
651  * XXX read four volhdrs and use the one with the highest TID whos CRC
652  *     matches.
653  *
654  * XXX check iCRCs.
655  *
656  * XXX For filesystems w/ less than 4 volhdrs, make sure to not write to
657  *     nonexistant locations.
658  *
659  * XXX Record selected volhdr and ring updates to each of 4 volhdrs
660  */
661 static
662 int
663 hammer2_install_volume_header(hammer2_mount_t *hmp)
664 {
665         hammer2_volume_data_t *vd;
666         struct buf *bp;
667         hammer2_crc32_t crc0, crc, bcrc0, bcrc;
668         int error_reported;
669         int error;
670         int valid;
671         int i;
672
673         error_reported = 0;
674         error = 0;
675         valid = 0;
676         bp = NULL;
677
678         /*
679          * There are up to 4 copies of the volume header (syncs iterate
680          * between them so there is no single master).  We don't trust the
681          * volu_size field so we don't know precisely how large the filesystem
682          * is, so depend on the OS to return an error if we go beyond the
683          * block device's EOF.
684          */
685         for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
686                 error = bread(hmp->devvp, i * HAMMER2_RESERVE_BYTES64, 
687                               HAMMER2_VOLUME_BYTES, &bp);
688                 if (error) {
689                         brelse(bp);
690                         bp = NULL;
691                         continue;
692                 }
693
694                 vd = (struct hammer2_volume_data *) bp->b_data;
695                 if ((vd->magic != HAMMER2_VOLUME_ID_HBO) &&
696                     (vd->magic != HAMMER2_VOLUME_ID_ABO)) {
697                         brelse(bp);
698                         bp = NULL;
699                         continue;
700                 }
701
702                 if (vd->magic == HAMMER2_VOLUME_ID_ABO) {
703                         /* XXX: Reversed-endianness filesystem */
704                         kprintf("hammer2: reverse-endian filesystem detected");
705                         brelse(bp);
706                         bp = NULL;
707                         continue;
708                 }
709
710                 crc = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
711                 crc0 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC0_OFF,
712                                       HAMMER2_VOLUME_ICRC0_SIZE);
713                 bcrc = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
714                 bcrc0 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC1_OFF,
715                                        HAMMER2_VOLUME_ICRC1_SIZE);
716                 if ((crc0 != crc) || (bcrc0 != bcrc)) {
717                         kprintf("hammer2 volume header crc "
718                                 "mismatch copy #%d\t%08x %08x",
719                                 i, crc0, crc);
720                         error_reported = 1;
721                         brelse(bp);
722                         bp = NULL;
723                         continue;
724                 }
725                 if (valid == 0 || hmp->voldata.last_tid < vd->last_tid) {
726                         valid = 1;
727                         hmp->voldata = *vd;
728                 }
729                 brelse(bp);
730                 bp = NULL;
731         }
732         if (valid) {
733                 error = 0;
734                 if (error_reported)
735                         kprintf("hammer2: a valid volume header was found\n");
736         } else {
737                 error = EINVAL;
738                 kprintf("hammer2: no valid volume headers found!\n");
739         }
740         return (error);
741 }
742