1 /*-
2 * Copyright (c) 2017-2019 The DragonFly Project
3 * Copyright (c) 2017-2019 Tomohiro Kusumi <tkusumi@netbsd.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdbool.h>
31 #include <string.h>
32 #include <err.h>
33 #include <assert.h>
34 #include <uuid.h>
35 #include <vfs/hammer2/hammer2_disk.h>
36
37 #include "fstyp.h"
38
39 static ssize_t
get_file_size(FILE * fp)40 get_file_size(FILE *fp)
41 {
42 ssize_t siz;
43
44 if (fseek(fp, 0, SEEK_END) == -1) {
45 warnx("hammer2: failed to seek media end");
46 return (-1);
47 }
48
49 siz = ftell(fp);
50 if (siz == -1) {
51 warnx("hammer2: failed to tell media end");
52 return (-1);
53 }
54
55 return (siz);
56 }
57
58 static hammer2_volume_data_t *
read_voldata(FILE * fp,int i)59 read_voldata(FILE *fp, int i)
60 {
61 if (i < 0 || i >= HAMMER2_NUM_VOLHDRS)
62 return (NULL);
63
64 if (i * HAMMER2_ZONE_BYTES64 >= get_file_size(fp))
65 return (NULL);
66
67 return (read_buf(fp, i * HAMMER2_ZONE_BYTES64,
68 sizeof(hammer2_volume_data_t)));
69 }
70
71 static int
test_voldata(FILE * fp)72 test_voldata(FILE *fp)
73 {
74 hammer2_volume_data_t *voldata;
75 int i;
76 static int count = 0;
77 static uuid_t fsid, fstype;
78
79 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
80 if (i * HAMMER2_ZONE_BYTES64 >= get_file_size(fp))
81 break;
82 voldata = read_voldata(fp, i);
83 if (voldata == NULL) {
84 warnx("hammer2: failed to read volume data");
85 return (1);
86 }
87 if (voldata->magic != HAMMER2_VOLUME_ID_HBO &&
88 voldata->magic != HAMMER2_VOLUME_ID_ABO) {
89 free(voldata);
90 return (1);
91 }
92 if (voldata->volu_id > HAMMER2_MAX_VOLUMES - 1) {
93 free(voldata);
94 return (1);
95 }
96 if (voldata->nvolumes > HAMMER2_MAX_VOLUMES) {
97 free(voldata);
98 return (1);
99 }
100
101 if (count == 0) {
102 count = voldata->nvolumes;
103 memcpy(&fsid, &voldata->fsid, sizeof(fsid));
104 memcpy(&fstype, &voldata->fstype, sizeof(fstype));
105 } else {
106 if (voldata->nvolumes != count) {
107 free(voldata);
108 return (1);
109 }
110 if (!uuid_equal(&fsid, &voldata->fsid, NULL)) {
111 free(voldata);
112 return (1);
113 }
114 if (!uuid_equal(&fstype, &voldata->fstype, NULL)) {
115 free(voldata);
116 return (1);
117 }
118 }
119 free(voldata);
120 }
121
122 return (0);
123 }
124
125 static hammer2_media_data_t*
read_media(FILE * fp,const hammer2_blockref_t * bref,size_t * media_bytes)126 read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes)
127 {
128 hammer2_media_data_t *media;
129 hammer2_off_t io_off, io_base;
130 size_t bytes, io_bytes, boff, fbytes;
131
132 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
133 if (bytes)
134 bytes = (size_t)1 << bytes;
135 *media_bytes = bytes;
136
137 if (!bytes) {
138 warnx("hammer2: blockref has no data");
139 return (NULL);
140 }
141
142 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
143 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
144 boff = io_off - io_base;
145
146 io_bytes = HAMMER2_LBUFSIZE;
147 while (io_bytes + boff < bytes)
148 io_bytes <<= 1;
149
150 if (io_bytes > sizeof(hammer2_media_data_t)) {
151 warnx("hammer2: invalid I/O bytes");
152 return (NULL);
153 }
154
155 /*
156 * XXX fp is currently always root volume, so read fails if io_base is
157 * beyond root volume limit. Fail with a message before read_buf() then.
158 */
159 fbytes = get_file_size(fp);
160 if (fbytes == -1) {
161 warnx("hammer2: failed to get media size");
162 return (NULL);
163 }
164 if (io_base >= fbytes) {
165 warnx("hammer2: XXX read beyond HAMMER2 root volume limit unsupported");
166 return (NULL);
167 }
168
169 if (fseek(fp, io_base, SEEK_SET) == -1) {
170 warnx("hammer2: failed to seek media");
171 return (NULL);
172 }
173 media = read_buf(fp, io_base, io_bytes);
174 if (media == NULL) {
175 warnx("hammer2: failed to read media");
176 return (NULL);
177 }
178 if (boff)
179 memcpy(media, (char *)media + boff, bytes);
180
181 return (media);
182 }
183
184 static int
find_pfs(FILE * fp,const hammer2_blockref_t * bref,const char * pfs,bool * res)185 find_pfs(FILE *fp, const hammer2_blockref_t *bref, const char *pfs, bool *res)
186 {
187 hammer2_media_data_t *media;
188 hammer2_inode_data_t ipdata;
189 hammer2_blockref_t *bscan;
190 size_t bytes;
191 int i, bcount;
192
193 media = read_media(fp, bref, &bytes);
194 if (media == NULL)
195 return (-1);
196
197 switch (bref->type) {
198 case HAMMER2_BREF_TYPE_INODE:
199 ipdata = media->ipdata;
200 if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
201 bscan = &ipdata.u.blockset.blockref[0];
202 bcount = HAMMER2_SET_COUNT;
203 } else {
204 bscan = NULL;
205 bcount = 0;
206 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
207 if (memchr(ipdata.filename, 0,
208 sizeof(ipdata.filename))) {
209 if (!strcmp(
210 (const char*)ipdata.filename, pfs))
211 *res = true;
212 } else {
213 if (strlen(pfs) > 0 &&
214 !memcmp(ipdata.filename, pfs,
215 strlen(pfs)))
216 *res = true;
217 }
218 } else {
219 free(media);
220 return (-1);
221 }
222 }
223 break;
224 case HAMMER2_BREF_TYPE_INDIRECT:
225 bscan = &media->npdata[0];
226 bcount = bytes / sizeof(hammer2_blockref_t);
227 break;
228 default:
229 bscan = NULL;
230 bcount = 0;
231 break;
232 }
233
234 for (i = 0; i < bcount; ++i) {
235 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) {
236 if (find_pfs(fp, &bscan[i], pfs, res) == -1) {
237 free(media);
238 return (-1);
239 }
240 }
241 }
242 free(media);
243
244 return (0);
245 }
246
247 static char*
extract_device_name(const char * devpath)248 extract_device_name(const char *devpath)
249 {
250 char *p, *head;
251
252 if (!devpath)
253 return (NULL);
254
255 p = strdup(devpath);
256 head = p;
257
258 p = strchr(p, '@');
259 if (p)
260 *p = 0;
261
262 p = strrchr(head, '/');
263 if (p) {
264 p++;
265 if (*p == 0) {
266 free(head);
267 return (NULL);
268 }
269 p = strdup(p);
270 free(head);
271 return (p);
272 }
273
274 return (head);
275 }
276
277 static int
read_label(FILE * fp,char * label,size_t size,const char * devpath)278 read_label(FILE *fp, char *label, size_t size, const char *devpath)
279 {
280 hammer2_blockref_t broot, best, *bref;
281 hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media;
282 size_t bytes;
283 bool res = false;
284 int i, best_i, error = 1;
285 const char *pfs;
286 char *devname;
287
288 best_i = -1;
289 memset(vols, 0, sizeof(vols));
290 memset(&best, 0, sizeof(best));
291
292 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
293 if (i * HAMMER2_ZONE_BYTES64 >= get_file_size(fp))
294 break;
295 memset(&broot, 0, sizeof(broot));
296 broot.type = HAMMER2_BREF_TYPE_VOLUME;
297 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
298 vols[i] = (void*)read_voldata(fp, i);
299 if (vols[i] == NULL) {
300 warnx("hammer2: failed to read volume data");
301 goto fail;
302 }
303 broot.mirror_tid = vols[i]->voldata.mirror_tid;
304 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
305 best_i = i;
306 best = broot;
307 }
308 }
309
310 bref = &vols[best_i]->voldata.sroot_blockset.blockref[0];
311 if (bref->type != HAMMER2_BREF_TYPE_INODE) {
312 /* Don't print error as devpath could be non-root volume. */
313 goto fail;
314 }
315
316 media = read_media(fp, bref, &bytes);
317 if (media == NULL) {
318 goto fail;
319 }
320
321 /*
322 * fstyp_function in DragonFly takes an additional devpath argument
323 * which doesn't exist in FreeBSD and NetBSD.
324 */
325 #ifdef HAS_DEVPATH
326 pfs = strchr(devpath, '@');
327 if (!pfs) {
328 assert(strlen(devpath));
329 switch (devpath[strlen(devpath) - 1]) {
330 case 'a':
331 pfs = "BOOT";
332 break;
333 case 'd':
334 pfs = "ROOT";
335 break;
336 default:
337 pfs = "DATA";
338 break;
339 }
340 } else
341 pfs++;
342
343 if (strlen(pfs) > HAMMER2_INODE_MAXNAME) {
344 goto fail;
345 }
346 devname = extract_device_name(devpath);
347 #else
348 pfs = "";
349 devname = extract_device_name(NULL);
350 assert(!devname);
351 #endif
352
353 /* Add device name to help support multiple autofs -media mounts. */
354 if (find_pfs(fp, bref, pfs, &res) == 0 && res) {
355 if (devname)
356 snprintf(label, size, "%s_%s", pfs, devname);
357 else
358 strlcpy(label, pfs, size);
359 } else {
360 memset(label, 0, size);
361 memcpy(label, media->ipdata.filename,
362 sizeof(media->ipdata.filename));
363 if (devname) {
364 strlcat(label, "_", size);
365 strlcat(label, devname, size);
366 }
367 }
368 if (devname)
369 free(devname);
370 free(media);
371 error = 0;
372 fail:
373 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++)
374 free(vols[i]);
375
376 return (error);
377 }
378
379 int
fstyp_hammer2(FILE * fp,char * label,size_t size,const char * devpath)380 fstyp_hammer2(FILE *fp, char *label, size_t size, const char *devpath)
381 {
382 hammer2_volume_data_t *voldata = read_voldata(fp, 0);
383 int error = 1;
384
385 if (voldata->volu_id != HAMMER2_ROOT_VOLUME)
386 goto fail;
387 if (voldata->nvolumes != 0)
388 goto fail;
389 if (test_voldata(fp))
390 goto fail;
391
392 error = read_label(fp, label, size, devpath);
393 fail:
394 free(voldata);
395 return (error);
396 }
397
398 static int
__fsvtyp_hammer2(const char * blkdevs,char * label,size_t size,int partial)399 __fsvtyp_hammer2(const char *blkdevs, char *label, size_t size, int partial)
400 {
401 hammer2_volume_data_t *voldata = NULL;
402 FILE *fp = NULL;
403 char *dup = NULL, *target_label = NULL, *p, *volpath, *rootvolpath;
404 char x[HAMMER2_MAX_VOLUMES];
405 int i, volid, error = 1;
406
407 if (!blkdevs)
408 goto fail;
409
410 memset(x, 0, sizeof(x));
411 p = dup = strdup(blkdevs);
412 if ((p = strchr(p, '@')) != NULL) {
413 *p++ = '\0';
414 target_label = p;
415 }
416 p = dup;
417
418 volpath = NULL;
419 rootvolpath = NULL;
420 volid = -1;
421 while (p) {
422 volpath = p;
423 if ((p = strchr(p, ':')) != NULL)
424 *p++ = '\0';
425 if ((fp = fopen(volpath, "r")) == NULL) {
426 warnx("hammer2: failed to open %s", volpath);
427 goto fail;
428 }
429 if (test_voldata(fp))
430 break;
431 voldata = read_voldata(fp, 0);
432 fclose(fp);
433 if (voldata == NULL) {
434 warnx("hammer2: failed to read volume data");
435 goto fail;
436 }
437 volid = voldata->volu_id;
438 free(voldata);
439 voldata = NULL;
440 if (volid < 0 || volid >= HAMMER2_MAX_VOLUMES)
441 goto fail;
442 x[volid]++;
443 if (volid == HAMMER2_ROOT_VOLUME)
444 rootvolpath = volpath;
445 }
446
447 /* If no rootvolpath, proceed only if partial mode with volpath. */
448 if (rootvolpath)
449 volpath = rootvolpath;
450 else if (!partial || !volpath)
451 goto fail;
452 if ((fp = fopen(volpath, "r")) == NULL) {
453 warnx("hammer2: failed to open %s", volpath);
454 goto fail;
455 }
456 voldata = read_voldata(fp, 0);
457 if (voldata == NULL) {
458 warnx("hammer2: failed to read volume data");
459 goto fail;
460 }
461
462 if (volid == -1)
463 goto fail;
464 if (partial)
465 goto success;
466
467 for (i = 0; i < HAMMER2_MAX_VOLUMES; i++)
468 if (x[i] > 1)
469 goto fail;
470 for (i = 0; i < HAMMER2_MAX_VOLUMES; i++)
471 if (x[i] == 0)
472 break;
473 if (voldata->nvolumes != i)
474 goto fail;
475 for (; i < HAMMER2_MAX_VOLUMES; i++)
476 if (x[i] != 0)
477 goto fail;
478 success:
479 /* Reconstruct @label format path using only root volume. */
480 if (target_label) {
481 size_t siz = strlen(volpath) + strlen(target_label) + 2;
482 p = calloc(1, siz);
483 snprintf(p, siz, "%s@%s", volpath, target_label);
484 volpath = p;
485 }
486 error = read_label(fp, label, size, volpath);
487 if (target_label)
488 free(p);
489 /* If in partial mode, read label but ignore error. */
490 if (partial)
491 error = 0;
492 fail:
493 if (fp)
494 fclose(fp);
495 free(voldata);
496 free(dup);
497 return (error);
498 }
499
500 int
fsvtyp_hammer2(const char * blkdevs,char * label,size_t size)501 fsvtyp_hammer2(const char *blkdevs, char *label, size_t size)
502 {
503 return (__fsvtyp_hammer2(blkdevs, label, size, 0));
504 }
505
506 int
fsvtyp_hammer2_partial(const char * blkdevs,char * label,size_t size)507 fsvtyp_hammer2_partial(const char *blkdevs, char *label, size_t size)
508 {
509 return (__fsvtyp_hammer2(blkdevs, label, size, 1));
510 }
511