xref: /dragonfly/sbin/hammer2/ondisk.c (revision c825a6c5bc23e72617bbb2bede9bfb99724bada0)
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 <stdint.h>
44 #include <string.h>
45 #include <fstab.h>
46 #include <assert.h>
47 #include <errno.h>
48 #include <err.h>
49 
50 #include <vfs/hammer2/hammer2_disk.h>
51 
52 #include "hammer2_subs.h"
53 
54 static hammer2_ondisk_t fso;
55 static int hammer2_volumes_initialized;
56 
57 static void
hammer2_init_volume(hammer2_volume_t * vol)58 hammer2_init_volume(hammer2_volume_t *vol)
59 {
60           vol->fd = -1;
61           vol->id = -1;
62           vol->offset = (hammer2_off_t)-1;
63           vol->size = (hammer2_off_t)-1;
64 }
65 
66 void
hammer2_init_ondisk(hammer2_ondisk_t * fsp)67 hammer2_init_ondisk(hammer2_ondisk_t *fsp)
68 {
69           int i;
70 
71           bzero(fsp, sizeof(*fsp));
72           fsp->version = -1;
73           for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i)
74                     hammer2_init_volume(&fsp->volumes[i]);
75 }
76 
77 void
hammer2_install_volume(hammer2_volume_t * vol,int fd,int id,const char * path,hammer2_off_t offset,hammer2_off_t size)78 hammer2_install_volume(hammer2_volume_t *vol, int fd, int id, const char *path,
79                            hammer2_off_t offset, hammer2_off_t size)
80 {
81           bzero(vol, sizeof(*vol));
82           vol->fd = fd;
83           vol->id = id;
84           vol->path = strdup(path);
85           vol->offset = offset;
86           vol->size = size;
87 }
88 
89 void
hammer2_uninstall_volume(hammer2_volume_t * vol)90 hammer2_uninstall_volume(hammer2_volume_t *vol)
91 {
92           fsync(vol->fd);
93           close(vol->fd);
94           free(vol->path);
95           hammer2_init_volume(vol);
96 }
97 
98 /*
99  * Locate a valid volume header.  If any of the four volume headers is good,
100  * we have a valid volume header and choose the best one based on mirror_tid.
101  */
102 static int
hammer2_read_volume_header(int fd,const char * path,hammer2_volume_data_t * voldata)103 hammer2_read_volume_header(int fd, const char *path,
104                                  hammer2_volume_data_t *voldata)
105 {
106           hammer2_volume_data_t vd;
107           hammer2_tid_t mirror_tid = -1;
108           hammer2_off_t size = check_volume(fd);
109           hammer2_crc32_t crc0, crc1;
110           const char *p;
111           int i, zone = -1;
112           ssize_t ret;
113 
114           for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
115                     if (i * HAMMER2_ZONE_BYTES64 >= size)
116                               break;
117                     if (lseek(fd, i * HAMMER2_ZONE_BYTES64, SEEK_SET) == -1)
118                               break;
119                     ret = read(fd, &vd, HAMMER2_VOLUME_BYTES);
120                     if (ret == -1) {
121                               fprintf(stderr, "%s #%d: read %s\n",
122                                         path, i, strerror(errno));
123                               continue;
124                     }
125                     if (ret != HAMMER2_VOLUME_BYTES) {
126                               fprintf(stderr, "%s #%d: read %s\n",
127                                         path, i, strerror(errno));
128                               continue;
129                     }
130 
131                     p = (const char*)&vd;
132                     /* verify volume header magic */
133                     if ((vd.magic != HAMMER2_VOLUME_ID_HBO) &&
134                         (vd.magic != HAMMER2_VOLUME_ID_ABO)) {
135                               fprintf(stderr, "%s #%d: bad magic\n", path, i);
136                               continue;
137                     }
138 
139                     if (vd.magic == HAMMER2_VOLUME_ID_ABO) {
140                               /* XXX: Reversed-endianness filesystem */
141                               fprintf(stderr,
142                                         "%s #%d: reverse-endian filesystem detected",
143                                         path, i);
144                               continue;
145                     }
146 
147                     /* verify volume header CRC's */
148                     crc0 = vd.icrc_sects[HAMMER2_VOL_ICRC_SECT0];
149                     crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
150                                               HAMMER2_VOLUME_ICRC0_SIZE);
151                     if (crc0 != crc1) {
152                               fprintf(stderr,
153                                         "%s #%d: volume header crc mismatch "
154                                         "sect0 %08x/%08x\n",
155                                         path, i, crc0, crc1);
156                               continue;
157                     }
158 
159                     crc0 = vd.icrc_sects[HAMMER2_VOL_ICRC_SECT1];
160                     crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
161                                               HAMMER2_VOLUME_ICRC1_SIZE);
162                     if (crc0 != crc1) {
163                               fprintf(stderr,
164                                         "%s #%d: volume header crc mismatch "
165                                         "sect1 %08x/%08x",
166                                         path, i, crc0, crc1);
167                               continue;
168                     }
169 
170                     crc0 = vd.icrc_volheader;
171                     crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF,
172                                               HAMMER2_VOLUME_ICRCVH_SIZE);
173                     if (crc0 != crc1) {
174                               fprintf(stderr,
175                                         "%s #%d: volume header crc mismatch "
176                                         "vh %08x/%08x",
177                                         path, i, crc0, crc1);
178                               continue;
179                     }
180                     if (zone == -1 || mirror_tid < vd.mirror_tid) {
181                               bcopy(&vd, voldata, sizeof(vd));
182                               mirror_tid = vd.mirror_tid;
183                               zone = i;
184                     }
185           }
186           return(zone);
187 }
188 
189 static void
hammer2_err_uuid_mismatch(uuid_t * uuid1,uuid_t * uuid2,const char * id)190 hammer2_err_uuid_mismatch(uuid_t *uuid1, uuid_t *uuid2, const char *id)
191 {
192           char *p1 = NULL, *p2 = NULL;
193 
194           hammer2_uuid_to_str(uuid1, &p1);
195           hammer2_uuid_to_str(uuid2, &p2);
196 
197           errx(1, "Volume %s uuid mismatch %s vs %s", id, p1, p2);
198 
199           free(p1);
200           free(p2);
201 }
202 
203 static void
hammer2_add_volume(const char * path,int rdonly)204 hammer2_add_volume(const char *path, int rdonly)
205 {
206           hammer2_volume_data_t voldata;
207           hammer2_volume_t *vol;
208           struct stat st;
209           int fd, i;
210           uuid_t uuid;
211 
212           fd = open(path, rdonly ? O_RDONLY : O_RDWR);
213           if (fd == -1)
214                     err(1, "open");
215 
216           if (fstat(fd, &st) == -1)
217                     err(1, "fstat");
218           if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode))
219                     errx(1, "Unsupported file type");
220 
221           if (hammer2_read_volume_header(fd, path, &voldata) >= 0) {
222                     i = voldata.volu_id;
223                     if (i < 0 || i >= HAMMER2_MAX_VOLUMES)
224                               errx(1, "%s has bad volume id %d", path, i);
225                     vol = &fso.volumes[i];
226                     if (vol->id != -1)
227                               errx(1, "volume id %d already initialized", i);
228                     /* all headers must have the same version, nvolumes and uuid */
229                     if (!fso.nvolumes) {
230                               fso.version = voldata.version;
231                               fso.nvolumes = voldata.nvolumes;
232                               fso.fsid = voldata.fsid;
233                               fso.fstype = voldata.fstype;
234                     } else {
235                               if (fso.version != (int)voldata.version)
236                                         errx(1, "Volume version mismatch %d vs %d",
237                                              fso.version, (int)voldata.version);
238                               if (fso.nvolumes != voldata.nvolumes)
239                                         errx(1, "Volume count mismatch %d vs %d",
240                                              fso.nvolumes, voldata.nvolumes);
241                               uuid = voldata.fsid;
242                               if (!uuid_equal(&fso.fsid, &uuid, NULL))
243                                         hammer2_err_uuid_mismatch(&fso.fsid,
244                                                                         &uuid,
245                                                                         "fsid");
246                               uuid = voldata.fstype;
247                               if (!uuid_equal(&fso.fstype, &uuid, NULL))
248                                         hammer2_err_uuid_mismatch(&fso.fstype,
249                                                                         &uuid,
250                                                                         "fstype");
251                     }
252                     /* all per-volume tests passed */
253                     hammer2_install_volume(vol, fd, i, path,
254                                                voldata.volu_loff[i], voldata.volu_size);
255                     fso.total_size += vol->size;
256           } else {
257                     errx(1, "No valid volume headers found!");
258           }
259 }
260 
261 static void
hammer2_verify_volumes_common(const hammer2_ondisk_t * fsp,const hammer2_volume_data_t * rootvoldata)262 hammer2_verify_volumes_common(const hammer2_ondisk_t *fsp,
263                                     const hammer2_volume_data_t *rootvoldata)
264 {
265           const hammer2_volume_t *vol;
266           hammer2_off_t size;
267           struct stat *st;
268           const char *path;
269           char *str;
270           int i, j, nvolumes = 0;
271           uuid_t uuid;
272 
273           if (fsp->version == -1)
274                     errx(1, "Bad volume version %d", fsp->version);
275 
276           /* check initialized volume count */
277           for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
278                     vol = &fsp->volumes[i];
279                     if (vol->id != -1)
280                               nvolumes++;
281           }
282 
283           /* check volume header */
284           if (rootvoldata) {
285                     if (rootvoldata->volu_id != HAMMER2_ROOT_VOLUME)
286                               errx(1, "Volume id %d must be %d",
287                                    rootvoldata->volu_id, HAMMER2_ROOT_VOLUME);
288                     str = NULL;
289                     uuid = rootvoldata->fstype;
290                     hammer2_uuid_to_str(&uuid, &str);
291                     if (strcmp(str, HAMMER2_UUID_STRING))
292                               errx(1, "Volume fstype uuid %s must be %s", str,
293                                    HAMMER2_UUID_STRING);
294           }
295 
296           /* fsp->nvolumes hasn't been verified yet, use nvolumes */
297           st = calloc(nvolumes, sizeof(*st));
298 
299           for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
300                     vol = &fsp->volumes[i];
301                     if (vol->id == -1)
302                               continue;
303                     path = vol->path;
304                     /* check volumes are unique */
305                     if (stat(path, &st[i]) != 0)
306                               errx(1, "Failed to stat %s", path);
307                     if (fstat(vol->fd, &st[i]) != 0)
308                               errx(1, "Failed to fstat %d", vol->fd);
309                     for (j = 0; j < i; ++j) {
310                               if ((st[i].st_ino == st[j].st_ino) &&
311                                   (st[i].st_dev == st[j].st_dev))
312                                         errx(1, "%s specified more than once", path);
313                     }
314                     /* check volume fields are initialized */
315                     if (vol->fd == -1)
316                               errx(1, "%s has bad fd %d", path, vol->fd);
317                     if (vol->offset == (hammer2_off_t)-1)
318                               errx(1, "%s has bad offset 0x%016jx", path,
319                                    (intmax_t)vol->offset);
320                     if (vol->size == (hammer2_off_t)-1)
321                               errx(1, "%s has bad size 0x%016jx", path,
322                                    (intmax_t)vol->size);
323                     /* check volume size vs block device size */
324                     size = check_volume(vol->fd);
325                     printf("checkvolu header %d %016jx/%016jx\n", i, vol->size, size);
326                     if (vol->size > size)
327                               errx(1, "%s's size 0x%016jx exceeds device size 0x%016jx",
328                                    path, (intmax_t)vol->size, size);
329                     if (vol->size == 0)
330                               errx(1, "%s has size of 0", path);
331           }
332           free(st);
333 }
334 
335 static void
hammer2_verify_volumes_1(hammer2_ondisk_t * fsp,const hammer2_volume_data_t * rootvoldata)336 hammer2_verify_volumes_1(hammer2_ondisk_t *fsp,
337                                const hammer2_volume_data_t *rootvoldata)
338 {
339           const hammer2_volume_t *vol;
340           hammer2_off_t off;
341           const char *path;
342           int i, nvolumes = 0;
343 
344           /* check initialized volume count */
345           for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
346                     vol = &fsp->volumes[i];
347                     if (vol->id != -1)
348                               nvolumes++;
349           }
350           if (nvolumes != 1)
351                     errx(1, "Only 1 volume supported");
352           fsp->nvolumes = nvolumes; /* adjust with actual count */
353 
354           /* check volume header */
355           if (rootvoldata) {
356                     if (rootvoldata->nvolumes)
357                               errx(1, "Volume count %d must be 0",
358                                    rootvoldata->nvolumes);
359                     if (rootvoldata->total_size)
360                               errx(1, "Total size 0x%016jx must be 0",
361                                    (intmax_t)rootvoldata->total_size);
362                     for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
363                               off = rootvoldata->volu_loff[i];
364                               if (off)
365                                         errx(1, "Volume offset[%d] 0x%016jx must be 0",
366                                              i, (intmax_t)off);
367                     }
368           }
369 
370           /* check volume */
371           vol = &fsp->volumes[HAMMER2_ROOT_VOLUME];
372           path = vol->path;
373           if (vol->id)
374                     errx(1, "%s has non zero id %d", path, vol->id);
375           if (vol->offset)
376                     errx(1, "%s has non zero offset 0x%016jx", path,
377                          (intmax_t)vol->offset);
378           if (vol->size & HAMMER2_VOLUME_ALIGNMASK64)
379                     errx(1, "%s's size is not 0x%016jx aligned", path,
380                          (intmax_t)HAMMER2_VOLUME_ALIGN);
381 }
382 
383 static void
hammer2_verify_volumes_2(const hammer2_ondisk_t * fsp,const hammer2_volume_data_t * rootvoldata)384 hammer2_verify_volumes_2(const hammer2_ondisk_t *fsp,
385                                const hammer2_volume_data_t *rootvoldata)
386 {
387           const hammer2_volume_t *vol;
388           hammer2_off_t off;
389           const char *path;
390           int i, nvolumes = 0;
391 
392           /* check initialized volume count */
393           for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
394                     vol = &fsp->volumes[i];
395                     if (vol->id != -1)
396                               nvolumes++;
397           }
398           if (fsp->nvolumes != nvolumes)
399                     errx(1, "Volume count mismatch %d vs %d",
400                          fsp->nvolumes, nvolumes);
401 
402           /* check volume header */
403           if (rootvoldata) {
404                     if (rootvoldata->nvolumes != fso.nvolumes)
405                               errx(1, "Volume header requires %d devices, %d specified",
406                                    rootvoldata->nvolumes, fso.nvolumes);
407                     if (rootvoldata->total_size != fso.total_size)
408                               errx(1, "Total size 0x%016jx does not equal sum of "
409                                    "volumes 0x%016jx",
410                                    rootvoldata->total_size, fso.total_size);
411                     for (i = 0; i < nvolumes; ++i) {
412                               off = rootvoldata->volu_loff[i];
413                               if (off == (hammer2_off_t)-1)
414                                         errx(1, "Volume offset[%d] 0x%016jx must not be -1",
415                                              i, (intmax_t)off);
416                     }
417                     for (i = nvolumes; i < HAMMER2_MAX_VOLUMES; ++i) {
418                               off = rootvoldata->volu_loff[i];
419                               if (off != (hammer2_off_t)-1)
420                                         errx(1, "Volume offset[%d] 0x%016jx must be -1",
421                                              i, (intmax_t)off);
422                     }
423           }
424 
425           /* check volumes */
426           for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
427                     vol = &fsp->volumes[i];
428                     if (vol->id == -1)
429                               continue;
430                     path = vol->path;
431                     /* check offset */
432                     if (vol->offset & HAMMER2_FREEMAP_LEVEL1_MASK)
433                               errx(1, "%s's offset 0x%016jx not 0x%016jx aligned",
434                                    path, (intmax_t)vol->offset,
435                                    HAMMER2_FREEMAP_LEVEL1_SIZE);
436                     /* check vs previous volume */
437                     if (i) {
438                               if (vol->id != (vol-1)->id + 1)
439                                         errx(1, "%s has inconsistent id %d", path,
440                                              vol->id);
441                               if (vol->offset != (vol-1)->offset + (vol-1)->size)
442                                         errx(1, "%s has inconsistent offset 0x%016jx",
443                                              path, (intmax_t)vol->offset);
444                     } else { /* first */
445                               if (vol->offset)
446                                         errx(1, "%s has non zero offset 0x%016jx", path,
447                                              (intmax_t)vol->offset);
448                     }
449                     /* check size for non-last and last volumes */
450                     if (i != fsp->nvolumes - 1) {
451                               if (vol->size < HAMMER2_FREEMAP_LEVEL1_SIZE)
452                                         errx(1, "%s's size must be >= 0x%016jx", path,
453                                              (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
454                               if (vol->size & HAMMER2_FREEMAP_LEVEL1_MASK)
455                                         errx(1, "%s's size is not 0x%016jx aligned",
456                                              path,
457                                              (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
458                     } else { /* last */
459                               if (vol->size & HAMMER2_VOLUME_ALIGNMASK64)
460                                         errx(1, "%s's size is not 0x%016jx aligned",
461                                              path, (intmax_t)HAMMER2_VOLUME_ALIGN);
462                     }
463           }
464 }
465 
466 void
hammer2_verify_volumes(hammer2_ondisk_t * fsp,const hammer2_volume_data_t * rootvoldata)467 hammer2_verify_volumes(hammer2_ondisk_t *fsp,
468                            const hammer2_volume_data_t *rootvoldata)
469 {
470           hammer2_verify_volumes_common(fsp, rootvoldata);
471           if (fsp->version >= HAMMER2_VOL_VERSION_MULTI_VOLUMES)
472                     hammer2_verify_volumes_2(fsp, rootvoldata);
473           else
474                     hammer2_verify_volumes_1(fsp, rootvoldata);
475           assert(fsp->nvolumes > 0);
476 }
477 
478 void
hammer2_print_volumes(const hammer2_ondisk_t * fsp)479 hammer2_print_volumes(const hammer2_ondisk_t *fsp)
480 {
481           const hammer2_volume_t *vol;
482           int i, n, w = 0;
483 
484           for (i = 0; i < fsp->nvolumes; ++i) {
485                     vol = &fsp->volumes[i];
486                     n = (int)strlen(vol->path);
487                     if (n > w)
488                               w = n;
489           }
490 
491           printf("total    %-*.*s 0x%016jx 0x%016jx\n",
492                     w, w, "", (intmax_t)0, (intmax_t)fsp->total_size);
493 
494           for (i = 0; i < fsp->nvolumes; ++i) {
495                     vol = &fsp->volumes[i];
496                     printf("volume%-2d %-*.*s 0x%016jx 0x%016jx%s\n",
497                            vol->id, w, w, vol->path, (intmax_t)vol->offset,
498                            (intmax_t)vol->size,
499                            (vol->id == HAMMER2_ROOT_VOLUME ?
500                            " (root volume)" : ""));
501           }
502 }
503 
504 void
hammer2_init_volumes(const char * blkdevs,int rdonly)505 hammer2_init_volumes(const char *blkdevs, int rdonly)
506 {
507           hammer2_volume_data_t *rootvoldata;
508           char *o, *p, *devpath;
509 
510           if (hammer2_volumes_initialized)
511                     errx(1, "Already initialized");
512           if (!blkdevs)
513                     errx(1, "NULL blkdevs");
514 
515           hammer2_init_ondisk(&fso);
516           o = p = strdup(blkdevs);
517           while ((devpath = p) != NULL) {
518                     if ((p = strchr(p, ':')) != NULL)
519                               *p++ = 0;
520                     devpath = getdevpath(devpath, 0);
521                     if (strchr(devpath, ':'))
522                               hammer2_init_volumes(devpath, rdonly);
523                     else
524                               hammer2_add_volume(devpath, rdonly);
525                     free(devpath);
526           }
527           free(o);
528           hammer2_volumes_initialized = 1;
529 
530           rootvoldata = hammer2_read_root_volume_header();
531           hammer2_verify_volumes(&fso, rootvoldata);
532           free(rootvoldata);
533 }
534 
535 void
hammer2_cleanup_volumes(void)536 hammer2_cleanup_volumes(void)
537 {
538           hammer2_volume_t *vol;
539           int i;
540 
541           for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
542                     vol = &fso.volumes[i];
543                     if (vol->id == -1)
544                               continue;
545                     hammer2_uninstall_volume(vol);
546           }
547           hammer2_volumes_initialized = 0;
548 }
549 
550 typedef void (*callback)(const hammer2_volume_t*, void *data);
551 
552 hammer2_volume_t *
hammer2_get_volume(hammer2_off_t offset)553 hammer2_get_volume(hammer2_off_t offset)
554 {
555           hammer2_volume_t *vol;
556           int i;
557 
558           assert(hammer2_volumes_initialized == 1);
559           offset &= ~HAMMER2_OFF_MASK_RADIX;
560 
561           /* do binary search if users really use this many supported volumes */
562           for (i = 0; i < fso.nvolumes; ++i) {
563                     vol = &fso.volumes[i];
564                     if ((offset >= vol->offset) &&
565                         (offset < vol->offset + vol->size))
566                     {
567                               return vol;
568                     }
569           }
570           return NULL;
571 }
572 
573 static int
hammer2_get_volume_attr(hammer2_off_t offset,callback fn,void * data)574 hammer2_get_volume_attr(hammer2_off_t offset, callback fn, void *data)
575 {
576           hammer2_volume_t *vol;
577           int i;
578 
579           assert(hammer2_volumes_initialized == 1);
580           offset &= ~HAMMER2_OFF_MASK_RADIX;
581 
582           /* do binary search if users really use this many supported volumes */
583           for (i = 0; i < fso.nvolumes; ++i) {
584                     vol = &fso.volumes[i];
585                     if ((offset >= vol->offset) &&
586                         (offset < vol->offset + vol->size)) {
587                               fn(vol, data);
588                               return(0);
589                     }
590           }
591 
592           return(-1);
593 }
594 
595 /* fd */
596 static void
hammer2_volume_fd_cb(const hammer2_volume_t * vol,void * data)597 hammer2_volume_fd_cb(const hammer2_volume_t *vol, void *data)
598 {
599           *(int*)data = vol->fd;
600 }
601 
602 int
hammer2_get_volume_fd(hammer2_off_t offset)603 hammer2_get_volume_fd(hammer2_off_t offset)
604 {
605           int ret = 0;
606 
607           if (hammer2_get_volume_attr(offset, hammer2_volume_fd_cb, &ret) < 0)
608                     return(-1);
609           return(ret);
610 }
611 
612 int
hammer2_get_root_volume_fd(void)613 hammer2_get_root_volume_fd(void)
614 {
615           return(hammer2_get_volume_fd(0));
616 }
617 
618 /* id */
619 static void
hammer2_volume_id_cb(const hammer2_volume_t * vol,void * data)620 hammer2_volume_id_cb(const hammer2_volume_t *vol, void *data)
621 {
622           *(int*)data = vol->id;
623 }
624 
625 int
hammer2_get_volume_id(hammer2_off_t offset)626 hammer2_get_volume_id(hammer2_off_t offset)
627 {
628           int ret = 0;
629 
630           if (hammer2_get_volume_attr(offset, hammer2_volume_id_cb, &ret) < 0)
631                     return(-1);
632           return(ret);
633 }
634 
635 int
hammer2_get_root_volume_id(void)636 hammer2_get_root_volume_id(void)
637 {
638           return(hammer2_get_volume_id(0));
639 }
640 
641 /* path */
642 static void
hammer2_volume_path_cb(const hammer2_volume_t * vol,void * data)643 hammer2_volume_path_cb(const hammer2_volume_t *vol, void *data)
644 {
645           *(const char**)data = vol->path;
646 }
647 
648 const char *
hammer2_get_volume_path(hammer2_off_t offset)649 hammer2_get_volume_path(hammer2_off_t offset)
650 {
651           const char *ret = NULL;
652 
653           if (hammer2_get_volume_attr(offset, hammer2_volume_path_cb, &ret) < 0)
654                     return(NULL);
655           return(ret);
656 }
657 
658 const char *
hammer2_get_root_volume_path(void)659 hammer2_get_root_volume_path(void)
660 {
661           return(hammer2_get_volume_path(0));
662 }
663 
664 /* offset */
665 static void
hammer2_volume_offset_cb(const hammer2_volume_t * vol,void * data)666 hammer2_volume_offset_cb(const hammer2_volume_t *vol, void *data)
667 {
668           *(hammer2_off_t*)data = vol->offset;
669 }
670 
671 hammer2_off_t
hammer2_get_volume_offset(hammer2_off_t offset)672 hammer2_get_volume_offset(hammer2_off_t offset)
673 {
674           hammer2_off_t ret = 0;
675 
676           if (hammer2_get_volume_attr(offset, hammer2_volume_offset_cb, &ret) < 0)
677                     return(-1);
678           return(ret);
679 }
680 
681 hammer2_off_t
hammer2_get_root_volume_offset(void)682 hammer2_get_root_volume_offset(void)
683 {
684           return(hammer2_get_volume_offset(0));
685 }
686 
687 /* size */
688 static void
hammer2_volume_size_cb(const hammer2_volume_t * vol,void * data)689 hammer2_volume_size_cb(const hammer2_volume_t *vol, void *data)
690 {
691           *(hammer2_off_t*)data = vol->size;
692 }
693 
694 hammer2_off_t
hammer2_get_volume_size(hammer2_off_t offset)695 hammer2_get_volume_size(hammer2_off_t offset)
696 {
697           hammer2_off_t ret = 0;
698 
699           if (hammer2_get_volume_attr(offset, hammer2_volume_size_cb, &ret) < 0)
700                     return(-1);
701           return(ret);
702 }
703 
704 hammer2_off_t
hammer2_get_root_volume_size(void)705 hammer2_get_root_volume_size(void)
706 {
707           return(hammer2_get_volume_size(0));
708 }
709 
710 /* total size */
711 hammer2_off_t
hammer2_get_total_size(void)712 hammer2_get_total_size(void)
713 {
714           return(fso.total_size);
715 }
716 
717 hammer2_volume_data_t*
hammer2_read_root_volume_header(void)718 hammer2_read_root_volume_header(void)
719 {
720           hammer2_volume_data_t *voldata;
721           int fd = hammer2_get_root_volume_fd();
722           const char *path = hammer2_get_root_volume_path();
723 
724           if (fd == -1)
725                     return(NULL);
726 
727           voldata = calloc(1, sizeof(*voldata));
728           if (hammer2_read_volume_header(fd, path, voldata) >= 0)
729                     return(voldata);
730           else
731                     errx(1, "Failed to read volume header");
732 }
733