xref: /dragonfly/sbin/hammer2/ondisk.c (revision e4adeac1)
1 /*
2  * Copyright (c) 2020 Tomohiro Kusumi <tkusumi@netbsd.org>
3  * Copyright (c) 2020 The DragonFly Project
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The DragonFly Project
7  * by Matthew Dillon <dillon@dragonflybsd.org>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  * 3. Neither the name of The DragonFly Project nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific, prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <fstab.h>
45 #include <assert.h>
46 #include <err.h>
47 
48 #include <vfs/hammer2/hammer2_disk.h>
49 
50 #include "hammer2_subs.h"
51 
52 static hammer2_ondisk_t fso;
53 static int hammer2_volumes_initialized;
54 
55 static void
56 hammer2_init_volume(hammer2_volume_t *vol)
57 {
58 	vol->fd = -1;
59 	vol->id = -1;
60 	vol->offset = (hammer2_off_t)-1;
61 	vol->size = (hammer2_off_t)-1;
62 }
63 
64 void
65 hammer2_init_ondisk(hammer2_ondisk_t *fsp)
66 {
67 	int i;
68 
69 	bzero(fsp, sizeof(*fsp));
70 	fsp->version = -1;
71 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i)
72 		hammer2_init_volume(&fsp->volumes[i]);
73 }
74 
75 void
76 hammer2_install_volume(hammer2_volume_t *vol, int fd, int id, const char *path,
77 		       hammer2_off_t offset, hammer2_off_t size)
78 {
79 	bzero(vol, sizeof(*vol));
80 	vol->fd = fd;
81 	vol->id = id;
82 	vol->path = strdup(path);
83 	vol->offset = offset;
84 	vol->size = size;
85 }
86 
87 void
88 hammer2_uninstall_volume(hammer2_volume_t *vol)
89 {
90 	fsync(vol->fd);
91 	close(vol->fd);
92 	free(vol->path);
93 	hammer2_init_volume(vol);
94 }
95 
96 static int
97 hammer2_read_volume_header(int fd, const char *path,
98 			   hammer2_volume_data_t *voldata)
99 {
100 	hammer2_volume_data_t vd;
101 	hammer2_tid_t mirror_tid = -1;
102 	hammer2_off_t size = check_volume(fd);
103 	hammer2_crc32_t crc0, crc1;
104 	const char *p;
105 	int i, zone = -1;
106 	ssize_t ret;
107 
108 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
109 		if (i * HAMMER2_ZONE_BYTES64 >= size)
110 			break;
111 		if (lseek(fd, i * HAMMER2_ZONE_BYTES64, SEEK_SET) == -1)
112 			break;
113 		ret = read(fd, &vd, HAMMER2_PBUFSIZE);
114 		if (ret == -1)
115 			err(1, "read");
116 		else if (ret != HAMMER2_PBUFSIZE)
117 			errx(1, "%s #%d: failed to read", path, i);
118 
119 		p = (const char*)&vd;
120 		/* verify volume header magic */
121 		if ((vd.magic != HAMMER2_VOLUME_ID_HBO) &&
122 		    (vd.magic != HAMMER2_VOLUME_ID_ABO))
123 			errx(1, "%s #%d: bad magic", path, i);
124 
125 		if (vd.magic == HAMMER2_VOLUME_ID_ABO) {
126 			/* XXX: Reversed-endianness filesystem */
127 			errx(1, "%s #%d: reverse-endian filesystem detected",
128 			     path, i);
129 		}
130 
131 		/* verify volume header CRC's */
132 		crc0 = vd.icrc_sects[HAMMER2_VOL_ICRC_SECT0];
133 		crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
134 				      HAMMER2_VOLUME_ICRC0_SIZE);
135 		if (crc0 != crc1)
136 			errx(1, "%s #%d: volume header crc mismatch sect0 %08x/%08x",
137 			     path, i, crc0, crc1);
138 
139 		crc0 = vd.icrc_sects[HAMMER2_VOL_ICRC_SECT1];
140 		crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
141 				      HAMMER2_VOLUME_ICRC1_SIZE);
142 		if (crc0 != crc1)
143 			errx(1, "%s #%d: volume header crc mismatch sect1 %08x/%08x",
144 			     path, i, crc0, crc1);
145 
146 		crc0 = vd.icrc_volheader;
147 		crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF,
148 				      HAMMER2_VOLUME_ICRCVH_SIZE);
149 		if (crc0 != crc1)
150 			errx(1, "%s #%d: volume header crc mismatch vh %08x/%08x",
151 			     path, i, crc0, crc1);
152 
153 		if (zone == -1 || mirror_tid < vd.mirror_tid) {
154 			bcopy(&vd, voldata, sizeof(vd));
155 			mirror_tid = vd.mirror_tid;
156 			zone = i;
157 		}
158 	}
159 
160 	if (zone == -1)
161 		errx(1, "%s has no valid volume headers", path);
162 	return(zone);
163 }
164 
165 static void
166 hammer2_err_uuid_mismatch(uuid_t *uuid1, uuid_t *uuid2, const char *id)
167 {
168 	char *p1 = NULL, *p2 = NULL;
169 
170 	hammer2_uuid_to_str(uuid1, &p1);
171 	hammer2_uuid_to_str(uuid2, &p2);
172 
173 	errx(1, "%s uuid mismatch %s vs %s", id, p1, p2);
174 
175 	free(p1);
176 	free(p2);
177 }
178 
179 static void
180 hammer2_add_volume(const char *path, int rdonly)
181 {
182 	hammer2_volume_data_t voldata;
183 	hammer2_volume_t *vol;
184 	struct stat st;
185 	int fd, i;
186 
187 	fd = open(path, rdonly ? O_RDONLY : O_RDWR);
188 	if (fd == -1)
189 		err(1, "open");
190 
191 	if (fstat(fd, &st) == -1)
192 		err(1, "fstat");
193 	if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode))
194 		errx(1, "Unsupported file type");
195 
196 	if (hammer2_read_volume_header(fd, path, &voldata) >= 0) {
197 		i = voldata.volu_id;
198 		if (i < 0 || i >= HAMMER2_MAX_VOLUMES)
199 			errx(1, "%s has bad volume id %d", path, i);
200 		vol = &fso.volumes[i];
201 		if (vol->id != -1)
202 			errx(1, "%s already initialized", path);
203 		/* all headers must have the same version, nvolumes and uuid */
204 		if (!fso.nvolumes) {
205 			fso.version = voldata.version;
206 			fso.nvolumes = voldata.nvolumes;
207 			fso.fsid = voldata.fsid;
208 			fso.fstype = voldata.fstype;
209 		} else {
210 			if (fso.version != (int)voldata.version)
211 				errx(1, "Volume version mismatch %d vs %d",
212 				     fso.version, (int)voldata.version);
213 			if (fso.nvolumes != voldata.nvolumes)
214 				errx(1, "Volume count mismatch %d vs %d",
215 				     fso.nvolumes, voldata.nvolumes);
216 			if (!uuid_equal(&fso.fsid, &voldata.fsid, NULL))
217 				hammer2_err_uuid_mismatch(&fso.fsid,
218 							  &voldata.fsid,
219 							  "fsid");
220 			if (!uuid_equal(&fso.fstype, &voldata.fstype, NULL))
221 				hammer2_err_uuid_mismatch(&fso.fstype,
222 							  &voldata.fstype,
223 							  "fstype");
224 		}
225 		/* all per-volume tests passed */
226 		hammer2_install_volume(vol, fd, i, path,
227 				       voldata.volu_loff[i], voldata.volu_size);
228 		fso.total_size += vol->size;
229 	} else {
230 		errx(1, "Failed to read volume header");
231 	}
232 }
233 
234 static void
235 hammer2_verify_volumes_common(const hammer2_ondisk_t *fsp)
236 {
237 	const hammer2_volume_t *vol;
238 	hammer2_off_t size;
239 	struct stat *st;
240 	const char *path;
241 	int i, j, nvolumes = 0;
242 
243 	if (fsp->version == -1)
244 		errx(1, "Bad volume version %d", fsp->version);
245 
246 	/* check initialized volume count */
247 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
248 		vol = &fsp->volumes[i];
249 		if (vol->id != -1)
250 			nvolumes++;
251 	}
252 
253 	/* fsp->nvolumes hasn't been verified yet, use nvolumes */
254 	st = calloc(nvolumes, sizeof(*st));
255 
256 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
257 		vol = &fsp->volumes[i];
258 		if (vol->id == -1)
259 			continue;
260 		path = vol->path;
261 		/* check volumes are unique */
262 		if (stat(path, &st[i]) != 0)
263 			errx(1, "Failed to stat %s", path);
264 		if (fstat(vol->fd, &st[i]) != 0)
265 			errx(1, "Failed to fstat %d", vol->fd);
266 		for (j = 0; j < i; ++j) {
267 			if ((st[i].st_ino == st[j].st_ino) &&
268 			    (st[i].st_dev == st[j].st_dev))
269 				errx(1, "%s specified more than once", path);
270 		}
271 		/* check volume fields are initialized */
272 		if (vol->fd == -1)
273 			errx(1, "%s has bad fd %d", path, vol->fd);
274 		if (vol->offset == (hammer2_off_t)-1)
275 			errx(1, "%s has bad offset 0x%016jx", path,
276 			     (intmax_t)vol->offset);
277 		if (vol->size == (hammer2_off_t)-1)
278 			errx(1, "%s has bad size 0x%016jx", path,
279 			     (intmax_t)vol->size);
280 		/* check volume size vs block device size */
281 		size = check_volume(vol->fd);
282 		if (vol->size > size)
283 			errx(1, "%s's size 0x%016jx exceeds device size 0x%016jx",
284 			     path, (intmax_t)vol->size, size);
285 	}
286 	free(st);
287 }
288 
289 static void
290 hammer2_verify_volumes_1(hammer2_ondisk_t *fsp,
291 			 const hammer2_volume_data_t *rootvoldata)
292 {
293 	const hammer2_volume_t *vol;
294 	hammer2_off_t off;
295 	const char *path;
296 	int i, nvolumes = 0;
297 
298 	/* check initialized volume count */
299 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
300 		vol = &fsp->volumes[i];
301 		if (vol->id != -1)
302 			nvolumes++;
303 	}
304 	if (nvolumes != 1)
305 		errx(1, "Only 1 volume supported");
306 	if (fsp->nvolumes)
307 		errx(1, "Volume count %d must be 0", fsp->nvolumes);
308 	fsp->nvolumes = nvolumes; /* adjust with actual count */
309 
310 	/* check volume header */
311 	if (rootvoldata) {
312 		if (rootvoldata->volu_id)
313 			errx(1, "Volume id %d must be 0", rootvoldata->volu_id);
314 		if (rootvoldata->nvolumes)
315 			errx(1, "Volume count %d must be 0",
316 			     rootvoldata->nvolumes);
317 		if (rootvoldata->total_size)
318 			errx(1, "Total size 0x%016jx must be 0",
319 			     (intmax_t)rootvoldata->total_size);
320 		for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
321 			off = rootvoldata->volu_loff[i];
322 			if (off)
323 				errx(1, "Volume offset[%d] 0x%016jx must be 0",
324 				     i, (intmax_t)off);
325 		}
326 	}
327 
328 	/* check volume */
329 	vol = &fsp->volumes[0];
330 	path = vol->path;
331 	if (vol->id)
332 		errx(1, "%s has non zero id %d", path, vol->id);
333 	if (vol->offset)
334 		errx(1, "%s has non zero offset 0x%016jx", path,
335 		     (intmax_t)vol->offset);
336 	if (vol->size & HAMMER2_VOLUME_ALIGNMASK64)
337 		errx(1, "%s's size is not 0x%016jx aligned", path,
338 		     (intmax_t)HAMMER2_VOLUME_ALIGN);
339 }
340 
341 static void
342 hammer2_verify_volumes_2(const hammer2_ondisk_t *fsp,
343 			 const hammer2_volume_data_t *rootvoldata)
344 {
345 	const hammer2_volume_t *vol;
346 	hammer2_off_t off;
347 	const char *path;
348 	int i, nvolumes = 0;
349 
350 	/* check initialized volume count */
351 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
352 		vol = &fsp->volumes[i];
353 		if (vol->id != -1)
354 			nvolumes++;
355 	}
356 	if (fsp->nvolumes != nvolumes)
357 		errx(1, "Volume count mismatch %d vs %d",
358 		     fsp->nvolumes, nvolumes);
359 
360 	/* check volume header */
361 	if (rootvoldata) {
362 		if (rootvoldata->volu_id != HAMMER2_ROOT_VOLUME)
363 			errx(1, "Volume id %d must be %d",
364 			     rootvoldata->volu_id, HAMMER2_ROOT_VOLUME);
365 		if (rootvoldata->nvolumes != fso.nvolumes)
366 			errx(1, "Volume header requires %d devices, %d specified",
367 			     rootvoldata->nvolumes, fso.nvolumes);
368 		if (rootvoldata->total_size != fso.total_size)
369 			errx(1, "Total size 0x%016jx does not equal sum of "
370 			     "volumes 0x%016jx",
371 			     rootvoldata->total_size, fso.total_size);
372 		for (i = 0; i < nvolumes; ++i) {
373 			off = rootvoldata->volu_loff[i];
374 			if (off == (hammer2_off_t)-1)
375 				errx(1, "Volume offset[%d] 0x%016jx must not be -1",
376 				     i, (intmax_t)off);
377 		}
378 		for (i = nvolumes; i < HAMMER2_MAX_VOLUMES; ++i) {
379 			off = rootvoldata->volu_loff[i];
380 			if (off != (hammer2_off_t)-1)
381 				errx(1, "Volume offset[%d] 0x%016jx must be -1",
382 				     i, (intmax_t)off);
383 		}
384 	}
385 
386 	/* check volumes */
387 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
388 		vol = &fsp->volumes[i];
389 		if (vol->id == -1)
390 			continue;
391 		path = vol->path;
392 		/* check offset */
393 		if (vol->offset & HAMMER2_FREEMAP_LEVEL1_MASK)
394 			errx(1, "%s's offset 0x%016jx not 0x%016jx aligned",
395 			     path, (intmax_t)vol->offset,
396 			     HAMMER2_FREEMAP_LEVEL1_SIZE);
397 		/* check vs previous volume */
398 		if (i) {
399 			if (vol->id != (vol-1)->id + 1)
400 				errx(1, "%s has inconsistent id %d", path,
401 				     vol->id);
402 			if (vol->offset != (vol-1)->offset + (vol-1)->size)
403 				errx(1, "%s has inconsistent offset 0x%016jx",
404 				     path, (intmax_t)vol->offset);
405 		} else { /* first */
406 			if (vol->offset)
407 				errx(1, "%s has non zero offset 0x%016jx", path,
408 				     (intmax_t)vol->offset);
409 		}
410 		/* check size for non-last and last volumes */
411 		if (i != fsp->nvolumes - 1) {
412 			if (vol->size < HAMMER2_FREEMAP_LEVEL1_SIZE)
413 				errx(1, "%s's size must be >= 0x%016jx", path,
414 				     (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
415 			if (vol->size & HAMMER2_FREEMAP_LEVEL1_MASK)
416 				errx(1, "%s's size is not 0x%016jx aligned",
417 				     path,
418 				     (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
419 		} else { /* last */
420 			if (vol->size & HAMMER2_VOLUME_ALIGNMASK64)
421 				errx(1, "%s's size is not 0x%016jx aligned",
422 				     path, (intmax_t)HAMMER2_VOLUME_ALIGN);
423 		}
424 	}
425 }
426 
427 void
428 hammer2_verify_volumes(hammer2_ondisk_t *fsp,
429 		       const hammer2_volume_data_t *rootvoldata)
430 {
431 	hammer2_verify_volumes_common(fsp);
432 	if (fsp->version >= HAMMER2_VOL_VERSION_MULTI_VOLUMES)
433 		hammer2_verify_volumes_2(fsp, rootvoldata);
434 	else
435 		hammer2_verify_volumes_1(fsp, rootvoldata);
436 	assert(fsp->nvolumes > 0);
437 }
438 
439 void
440 hammer2_print_volumes(const hammer2_ondisk_t *fsp)
441 {
442 	const hammer2_volume_t *vol;
443 	int i, n, w = 0;
444 
445 	for (i = 0; i < fsp->nvolumes; ++i) {
446 		vol = &fsp->volumes[i];
447 		n = (int)strlen(vol->path);
448 		if (n > w)
449 			w = n;
450 	}
451 
452 	printf("total    %-*.*s 0x%016jx 0x%016jx\n",
453 		w, w, "", (intmax_t)0, (intmax_t)fsp->total_size);
454 
455 	for (i = 0; i < fsp->nvolumes; ++i) {
456 		vol = &fsp->volumes[i];
457 		printf("volume%-2d %-*.*s 0x%016jx 0x%016jx%s\n",
458 		       vol->id, w, w, vol->path, (intmax_t)vol->offset,
459 		       (intmax_t)vol->size,
460 		       (vol->id == HAMMER2_ROOT_VOLUME ?
461 		       " (root volume)" : ""));
462 	}
463 }
464 
465 void
466 hammer2_init_volumes(const char *blkdevs, int rdonly)
467 {
468 	hammer2_volume_data_t *rootvoldata;
469 	char *p, *devpath;
470 
471 	if (hammer2_volumes_initialized)
472 		errx(1, "Already initialized");
473 	if (!blkdevs)
474 		errx(1, "NULL blkdevs");
475 
476 	hammer2_init_ondisk(&fso);
477 	p = strdup(blkdevs);
478 	while ((devpath = p) != NULL) {
479 		if ((p = strchr(p, ':')) != NULL)
480 			*p++ = 0;
481 		devpath = getdevpath(devpath, 0);
482 		if (strchr(devpath, ':'))
483 			hammer2_init_volumes(devpath, rdonly);
484 		else
485 			hammer2_add_volume(devpath, rdonly);
486 		free(devpath);
487 	}
488 	free(p);
489 	hammer2_volumes_initialized = 1;
490 
491 	rootvoldata = hammer2_read_root_volume_header();
492 	hammer2_verify_volumes(&fso, rootvoldata);
493 	free(rootvoldata);
494 }
495 
496 void
497 hammer2_cleanup_volumes(void)
498 {
499 	hammer2_volume_t *vol;
500 	int i;
501 
502 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
503 		vol = &fso.volumes[i];
504 		if (vol->id == -1)
505 			continue;
506 		hammer2_uninstall_volume(vol);
507 	}
508 	hammer2_volumes_initialized = 0;
509 }
510 
511 typedef void (*callback)(const hammer2_volume_t*, void *data);
512 
513 static int
514 hammer2_get_volume_attr(hammer2_off_t offset, callback fn, void *data)
515 {
516 	hammer2_volume_t *vol;
517 	int i;
518 
519 	assert(hammer2_volumes_initialized == 1);
520 	offset &= ~HAMMER2_OFF_MASK_RADIX;
521 
522 	/* do binary search if users really use this many supported volumes */
523 	for (i = 0; i < fso.nvolumes; ++i) {
524 		vol = &fso.volumes[i];
525 		if ((offset >= vol->offset) &&
526 		    (offset < vol->offset + vol->size)) {
527 			fn(vol, data);
528 			return(0);
529 		}
530 	}
531 
532 	return(-1);
533 }
534 
535 /* fd */
536 static void
537 hammer2_volume_fd_cb(const hammer2_volume_t *vol, void *data)
538 {
539 	*(int*)data = vol->fd;
540 }
541 
542 int
543 hammer2_get_volume_fd(hammer2_off_t offset)
544 {
545 	int ret = 0;
546 
547 	if (hammer2_get_volume_attr(offset, hammer2_volume_fd_cb, &ret) < 0)
548 		return(-1);
549 	return(ret);
550 }
551 
552 int
553 hammer2_get_root_volume_fd(void)
554 {
555 	return(hammer2_get_volume_fd(0));
556 }
557 
558 /* id */
559 static void
560 hammer2_volume_id_cb(const hammer2_volume_t *vol, void *data)
561 {
562 	*(int*)data = vol->id;
563 }
564 
565 int
566 hammer2_get_volume_id(hammer2_off_t offset)
567 {
568 	int ret = 0;
569 
570 	if (hammer2_get_volume_attr(offset, hammer2_volume_id_cb, &ret) < 0)
571 		return(-1);
572 	return(ret);
573 }
574 
575 int
576 hammer2_get_root_volume_id(void)
577 {
578 	return(hammer2_get_volume_id(0));
579 }
580 
581 /* path */
582 static void
583 hammer2_volume_path_cb(const hammer2_volume_t *vol, void *data)
584 {
585 	*(const char**)data = vol->path;
586 }
587 
588 const char *
589 hammer2_get_volume_path(hammer2_off_t offset)
590 {
591 	const char *ret = NULL;
592 
593 	if (hammer2_get_volume_attr(offset, hammer2_volume_path_cb, &ret) < 0)
594 		return(NULL);
595 	return(ret);
596 }
597 
598 const char *
599 hammer2_get_root_volume_path(void)
600 {
601 	return(hammer2_get_volume_path(0));
602 }
603 
604 /* offset */
605 static void
606 hammer2_volume_offset_cb(const hammer2_volume_t *vol, void *data)
607 {
608 	*(hammer2_off_t*)data = vol->offset;
609 }
610 
611 hammer2_off_t
612 hammer2_get_volume_offset(hammer2_off_t offset)
613 {
614 	hammer2_off_t ret = 0;
615 
616 	if (hammer2_get_volume_attr(offset, hammer2_volume_offset_cb, &ret) < 0)
617 		return(-1);
618 	return(ret);
619 }
620 
621 hammer2_off_t
622 hammer2_get_root_volume_offset(void)
623 {
624 	return(hammer2_get_volume_offset(0));
625 }
626 
627 /* size */
628 static void
629 hammer2_volume_size_cb(const hammer2_volume_t *vol, void *data)
630 {
631 	*(hammer2_off_t*)data = vol->size;
632 }
633 
634 hammer2_off_t
635 hammer2_get_volume_size(hammer2_off_t offset)
636 {
637 	hammer2_off_t ret = 0;
638 
639 	if (hammer2_get_volume_attr(offset, hammer2_volume_size_cb, &ret) < 0)
640 		return(-1);
641 	return(ret);
642 }
643 
644 hammer2_off_t
645 hammer2_get_root_volume_size(void)
646 {
647 	return(hammer2_get_volume_size(0));
648 }
649 
650 /* total size */
651 hammer2_off_t
652 hammer2_get_total_size(void)
653 {
654 	return(fso.total_size);
655 }
656 
657 hammer2_volume_data_t*
658 hammer2_read_root_volume_header(void)
659 {
660 	hammer2_volume_data_t *voldata;
661 	int fd = hammer2_get_root_volume_fd();
662 	const char *path = hammer2_get_root_volume_path();
663 
664 	if (fd == -1)
665 		return(NULL);
666 
667 	voldata = calloc(1, sizeof(*voldata));
668 	if (hammer2_read_volume_header(fd, path, voldata) >= 0)
669 		return(voldata);
670 	else
671 		errx(1, "Failed to read volume header");
672 }
673