xref: /dragonfly/usr.sbin/fstyp/hammer2.c (revision 029e6489)
1030fdd8aSTomohiro Kusumi /*-
226cca7e2STomohiro Kusumi  * Copyright (c) 2017-2019 The DragonFly Project
396fbde7aSTomohiro Kusumi  * Copyright (c) 2017-2019 Tomohiro Kusumi <tkusumi@netbsd.org>
4030fdd8aSTomohiro Kusumi  * All rights reserved.
5030fdd8aSTomohiro Kusumi  *
6030fdd8aSTomohiro Kusumi  * Redistribution and use in source and binary forms, with or without
7030fdd8aSTomohiro Kusumi  * modification, are permitted provided that the following conditions
8030fdd8aSTomohiro Kusumi  * are met:
9030fdd8aSTomohiro Kusumi  * 1. Redistributions of source code must retain the above copyright
10030fdd8aSTomohiro Kusumi  *    notice, this list of conditions and the following disclaimer.
11030fdd8aSTomohiro Kusumi  * 2. Redistributions in binary form must reproduce the above copyright
12030fdd8aSTomohiro Kusumi  *    notice, this list of conditions and the following disclaimer in the
13030fdd8aSTomohiro Kusumi  *    documentation and/or other materials provided with the distribution.
14030fdd8aSTomohiro Kusumi  *
15030fdd8aSTomohiro Kusumi  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16030fdd8aSTomohiro Kusumi  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17030fdd8aSTomohiro Kusumi  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18030fdd8aSTomohiro Kusumi  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19030fdd8aSTomohiro Kusumi  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20030fdd8aSTomohiro Kusumi  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21030fdd8aSTomohiro Kusumi  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22030fdd8aSTomohiro Kusumi  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23030fdd8aSTomohiro Kusumi  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24030fdd8aSTomohiro Kusumi  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25030fdd8aSTomohiro Kusumi  * SUCH DAMAGE.
26030fdd8aSTomohiro Kusumi  */
27030fdd8aSTomohiro Kusumi 
28030fdd8aSTomohiro Kusumi #include <stdio.h>
29030fdd8aSTomohiro Kusumi #include <stdlib.h>
3065d969b4STomohiro Kusumi #include <stdbool.h>
31030fdd8aSTomohiro Kusumi #include <string.h>
32030fdd8aSTomohiro Kusumi #include <err.h>
3365d969b4STomohiro Kusumi #include <assert.h>
34030fdd8aSTomohiro Kusumi #include <vfs/hammer2/hammer2_disk.h>
35030fdd8aSTomohiro Kusumi 
36030fdd8aSTomohiro Kusumi #include "fstyp.h"
37030fdd8aSTomohiro Kusumi 
38030fdd8aSTomohiro Kusumi static hammer2_volume_data_t*
39c8efd889STomohiro Kusumi read_voldata(FILE *fp)
40030fdd8aSTomohiro Kusumi {
41030fdd8aSTomohiro Kusumi 	hammer2_volume_data_t *voldata;
42030fdd8aSTomohiro Kusumi 
43030fdd8aSTomohiro Kusumi 	voldata = read_buf(fp, 0, sizeof(*voldata));
44030fdd8aSTomohiro Kusumi 	if (voldata == NULL)
45030fdd8aSTomohiro Kusumi 		err(1, "failed to read volume data");
46030fdd8aSTomohiro Kusumi 
47030fdd8aSTomohiro Kusumi 	return (voldata);
48030fdd8aSTomohiro Kusumi }
49030fdd8aSTomohiro Kusumi 
50030fdd8aSTomohiro Kusumi static int
51c8efd889STomohiro Kusumi test_voldata(const hammer2_volume_data_t *voldata)
52030fdd8aSTomohiro Kusumi {
53030fdd8aSTomohiro Kusumi 	if (voldata->magic != HAMMER2_VOLUME_ID_HBO &&
54030fdd8aSTomohiro Kusumi 	    voldata->magic != HAMMER2_VOLUME_ID_ABO)
55030fdd8aSTomohiro Kusumi 		return (1);
56030fdd8aSTomohiro Kusumi 
57030fdd8aSTomohiro Kusumi 	return (0);
58030fdd8aSTomohiro Kusumi }
59030fdd8aSTomohiro Kusumi 
6065d969b4STomohiro Kusumi static hammer2_media_data_t*
61c8efd889STomohiro Kusumi read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes)
6265d969b4STomohiro Kusumi {
6365d969b4STomohiro Kusumi 	hammer2_media_data_t *media;
6465d969b4STomohiro Kusumi 	hammer2_off_t io_off, io_base;
6565d969b4STomohiro Kusumi 	size_t bytes, io_bytes, boff;
6665d969b4STomohiro Kusumi 
6765d969b4STomohiro Kusumi 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
6865d969b4STomohiro Kusumi 	if (bytes)
6965d969b4STomohiro Kusumi 		bytes = (size_t)1 << bytes;
7065d969b4STomohiro Kusumi 	*media_bytes = bytes;
7165d969b4STomohiro Kusumi 
7265d969b4STomohiro Kusumi 	if (!bytes) {
73841ef9e9STomohiro Kusumi 		warnx("blockref has no data");
7465d969b4STomohiro Kusumi 		return (NULL);
7565d969b4STomohiro Kusumi 	}
7665d969b4STomohiro Kusumi 
7765d969b4STomohiro Kusumi 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
7865d969b4STomohiro Kusumi 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
7965d969b4STomohiro Kusumi 	boff = io_off - io_base;
8065d969b4STomohiro Kusumi 
8165d969b4STomohiro Kusumi 	io_bytes = HAMMER2_MINIOSIZE;
8265d969b4STomohiro Kusumi 	while (io_bytes + boff < bytes)
8365d969b4STomohiro Kusumi 		io_bytes <<= 1;
8465d969b4STomohiro Kusumi 
8565d969b4STomohiro Kusumi 	if (io_bytes > sizeof(hammer2_media_data_t)) {
86841ef9e9STomohiro Kusumi 		warnx("invalid I/O bytes");
8765d969b4STomohiro Kusumi 		return (NULL);
8865d969b4STomohiro Kusumi 	}
8965d969b4STomohiro Kusumi 
9065d969b4STomohiro Kusumi 	if (fseek(fp, io_base, SEEK_SET) == -1) {
91841ef9e9STomohiro Kusumi 		warnx("failed to seek media");
9265d969b4STomohiro Kusumi 		return (NULL);
9365d969b4STomohiro Kusumi 	}
9465d969b4STomohiro Kusumi 	media = read_buf(fp, io_base, io_bytes);
9565d969b4STomohiro Kusumi 	if (media == NULL) {
96841ef9e9STomohiro Kusumi 		warnx("failed to read media");
9765d969b4STomohiro Kusumi 		return (NULL);
9865d969b4STomohiro Kusumi 	}
9965d969b4STomohiro Kusumi 	if (boff)
10065d969b4STomohiro Kusumi 		memcpy(media, (char *)media + boff, bytes);
10165d969b4STomohiro Kusumi 
10265d969b4STomohiro Kusumi 	return (media);
10365d969b4STomohiro Kusumi }
10465d969b4STomohiro Kusumi 
10526cca7e2STomohiro Kusumi static int
106c8efd889STomohiro Kusumi find_pfs(FILE *fp, const hammer2_blockref_t *bref, const char *pfs, bool *res)
10765d969b4STomohiro Kusumi {
10865d969b4STomohiro Kusumi 	hammer2_media_data_t *media;
10965d969b4STomohiro Kusumi 	hammer2_inode_data_t ipdata;
11065d969b4STomohiro Kusumi 	hammer2_blockref_t *bscan;
11165d969b4STomohiro Kusumi 	size_t bytes;
11265d969b4STomohiro Kusumi 	int i, bcount;
11365d969b4STomohiro Kusumi 
114c8efd889STomohiro Kusumi 	media = read_media(fp, bref, &bytes);
11565d969b4STomohiro Kusumi 	if (media == NULL)
11665d969b4STomohiro Kusumi 		return (-1);
11765d969b4STomohiro Kusumi 
11865d969b4STomohiro Kusumi 	switch (bref->type) {
11965d969b4STomohiro Kusumi 	case HAMMER2_BREF_TYPE_INODE:
12065d969b4STomohiro Kusumi 		ipdata = media->ipdata;
121*029e6489STomohiro Kusumi 		if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
12265d969b4STomohiro Kusumi 			bscan = &ipdata.u.blockset.blockref[0];
12365d969b4STomohiro Kusumi 			bcount = HAMMER2_SET_COUNT;
12465d969b4STomohiro Kusumi 		} else {
12565d969b4STomohiro Kusumi 			bscan = NULL;
12665d969b4STomohiro Kusumi 			bcount = 0;
12765d969b4STomohiro Kusumi 			if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
12863dc3af7STomohiro Kusumi 				if (memchr(ipdata.filename, 0,
12963dc3af7STomohiro Kusumi 				    sizeof(ipdata.filename))) {
1304a4aa420STomohiro Kusumi 					if (!strcmp(
1314a4aa420STomohiro Kusumi 					    (const char*)ipdata.filename, pfs))
13265d969b4STomohiro Kusumi 						*res = true;
13363dc3af7STomohiro Kusumi 				} else {
134e3e33cadSTomohiro Kusumi 					if (strlen(pfs) > 0 &&
135e3e33cadSTomohiro Kusumi 					    !memcmp(ipdata.filename, pfs,
13663dc3af7STomohiro Kusumi 					    strlen(pfs)))
13763dc3af7STomohiro Kusumi 						*res = true;
13863dc3af7STomohiro Kusumi 				}
13965d969b4STomohiro Kusumi 			} else
14065d969b4STomohiro Kusumi 				assert(0);
14165d969b4STomohiro Kusumi 		}
14265d969b4STomohiro Kusumi 		break;
14365d969b4STomohiro Kusumi 	case HAMMER2_BREF_TYPE_INDIRECT:
14465d969b4STomohiro Kusumi 		bscan = &media->npdata[0];
14565d969b4STomohiro Kusumi 		bcount = bytes / sizeof(hammer2_blockref_t);
14665d969b4STomohiro Kusumi 		break;
14765d969b4STomohiro Kusumi 	default:
14865d969b4STomohiro Kusumi 		bscan = NULL;
14965d969b4STomohiro Kusumi 		bcount = 0;
15065d969b4STomohiro Kusumi 		break;
15165d969b4STomohiro Kusumi 	}
15265d969b4STomohiro Kusumi 
15365d969b4STomohiro Kusumi 	for (i = 0; i < bcount; ++i) {
15465d969b4STomohiro Kusumi 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) {
155c8efd889STomohiro Kusumi 			if (find_pfs(fp, &bscan[i], pfs, res) == -1) {
15665d969b4STomohiro Kusumi 				free(media);
15765d969b4STomohiro Kusumi 				return (-1);
15865d969b4STomohiro Kusumi 			}
15965d969b4STomohiro Kusumi 		}
16065d969b4STomohiro Kusumi 	}
16165d969b4STomohiro Kusumi 	free(media);
16265d969b4STomohiro Kusumi 
16365d969b4STomohiro Kusumi 	return (0);
16465d969b4STomohiro Kusumi }
16565d969b4STomohiro Kusumi 
1662f9f168aSTomohiro Kusumi static char*
1672f9f168aSTomohiro Kusumi extract_device_name(const char *devpath)
1682f9f168aSTomohiro Kusumi {
169e3e33cadSTomohiro Kusumi 	char *p, *head;
170e3e33cadSTomohiro Kusumi 
171e3e33cadSTomohiro Kusumi 	if (!devpath)
172e3e33cadSTomohiro Kusumi 		return NULL;
173e3e33cadSTomohiro Kusumi 
174e3e33cadSTomohiro Kusumi 	p = strdup(devpath);
175e3e33cadSTomohiro Kusumi 	head = p;
1762f9f168aSTomohiro Kusumi 
1772f9f168aSTomohiro Kusumi 	p = strchr(p, '@');
1782f9f168aSTomohiro Kusumi 	if (p)
1792f9f168aSTomohiro Kusumi 		*p = 0;
1802f9f168aSTomohiro Kusumi 
1812f9f168aSTomohiro Kusumi 	p = strrchr(head, '/');
1822f9f168aSTomohiro Kusumi 	if (p) {
1832f9f168aSTomohiro Kusumi 		p++;
1842f9f168aSTomohiro Kusumi 		if (*p == 0) {
1852f9f168aSTomohiro Kusumi 			free(head);
1862f9f168aSTomohiro Kusumi 			return NULL;
1872f9f168aSTomohiro Kusumi 		}
1882f9f168aSTomohiro Kusumi 		p = strdup(p);
1892f9f168aSTomohiro Kusumi 		free(head);
1902f9f168aSTomohiro Kusumi 		return p;
1912f9f168aSTomohiro Kusumi 	}
1922f9f168aSTomohiro Kusumi 
1932f9f168aSTomohiro Kusumi 	return head;
1942f9f168aSTomohiro Kusumi }
1952f9f168aSTomohiro Kusumi 
19665d969b4STomohiro Kusumi static int
197c8efd889STomohiro Kusumi read_label(FILE *fp, char *label, size_t size, const char *devpath)
19826cca7e2STomohiro Kusumi {
19926cca7e2STomohiro Kusumi 	hammer2_blockref_t broot, best, *bref;
2009ed43ec3STomohiro Kusumi 	hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media;
20165d969b4STomohiro Kusumi 	size_t bytes;
20265d969b4STomohiro Kusumi 	bool res = false;
203841ef9e9STomohiro Kusumi 	int i, best_i, error = 1;
20465d969b4STomohiro Kusumi 	const char *pfs;
2052f9f168aSTomohiro Kusumi 	char *devname;
20626cca7e2STomohiro Kusumi 
20726cca7e2STomohiro Kusumi 	best_i = -1;
20826cca7e2STomohiro Kusumi 	memset(&best, 0, sizeof(best));
20926cca7e2STomohiro Kusumi 
21026cca7e2STomohiro Kusumi 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
21126cca7e2STomohiro Kusumi 		memset(&broot, 0, sizeof(broot));
21226cca7e2STomohiro Kusumi 		broot.type = HAMMER2_BREF_TYPE_VOLUME;
21326cca7e2STomohiro Kusumi 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
21426cca7e2STomohiro Kusumi 		vols[i] = read_buf(fp, broot.data_off & ~HAMMER2_OFF_MASK_RADIX,
21526cca7e2STomohiro Kusumi 		    sizeof(*vols[i]));
21626cca7e2STomohiro Kusumi 		broot.mirror_tid = vols[i]->voldata.mirror_tid;
21726cca7e2STomohiro Kusumi 		if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
21826cca7e2STomohiro Kusumi 			best_i = i;
21926cca7e2STomohiro Kusumi 			best = broot;
22026cca7e2STomohiro Kusumi 		}
22126cca7e2STomohiro Kusumi 	}
22226cca7e2STomohiro Kusumi 
22326cca7e2STomohiro Kusumi 	bref = &vols[best_i]->voldata.sroot_blockset.blockref[0];
22426cca7e2STomohiro Kusumi 	if (bref->type != HAMMER2_BREF_TYPE_INODE) {
225841ef9e9STomohiro Kusumi 		warnx("blockref type is not inode");
226841ef9e9STomohiro Kusumi 		goto fail;
22726cca7e2STomohiro Kusumi 	}
22826cca7e2STomohiro Kusumi 
229c8efd889STomohiro Kusumi 	media = read_media(fp, bref, &bytes);
23065d969b4STomohiro Kusumi 	if (media == NULL) {
231841ef9e9STomohiro Kusumi 		goto fail;
23226cca7e2STomohiro Kusumi 	}
23326cca7e2STomohiro Kusumi 
234e3e33cadSTomohiro Kusumi 	/*
235e3e33cadSTomohiro Kusumi 	 * fstyp_function in DragonFly takes an additional devpath argument
236e3e33cadSTomohiro Kusumi 	 * which doesn't exist in FreeBSD and NetBSD.
237e3e33cadSTomohiro Kusumi 	 */
238e3e33cadSTomohiro Kusumi #ifdef HAS_DEVPATH
23965d969b4STomohiro Kusumi 	pfs = strchr(devpath, '@');
24065d969b4STomohiro Kusumi 	if (!pfs) {
24165d969b4STomohiro Kusumi 		assert(strlen(devpath));
24265d969b4STomohiro Kusumi 		switch (devpath[strlen(devpath) - 1]) {
24365d969b4STomohiro Kusumi 		case 'a':
24465d969b4STomohiro Kusumi 			pfs = "BOOT";
24565d969b4STomohiro Kusumi 			break;
24665d969b4STomohiro Kusumi 		case 'd':
24765d969b4STomohiro Kusumi 			pfs = "ROOT";
24865d969b4STomohiro Kusumi 			break;
24965d969b4STomohiro Kusumi 		default:
25065d969b4STomohiro Kusumi 			pfs = "DATA";
25165d969b4STomohiro Kusumi 			break;
25226cca7e2STomohiro Kusumi 		}
25365d969b4STomohiro Kusumi 	} else
25465d969b4STomohiro Kusumi 		pfs++;
25526cca7e2STomohiro Kusumi 
25663dc3af7STomohiro Kusumi 	if (strlen(pfs) > HAMMER2_INODE_MAXNAME) {
257841ef9e9STomohiro Kusumi 		goto fail;
25863dc3af7STomohiro Kusumi 	}
2592f9f168aSTomohiro Kusumi 	devname = extract_device_name(devpath);
260e3e33cadSTomohiro Kusumi #else
261e3e33cadSTomohiro Kusumi 	pfs = "";
262e3e33cadSTomohiro Kusumi 	devname = extract_device_name(NULL);
263e3e33cadSTomohiro Kusumi 	assert(!devname);
264e3e33cadSTomohiro Kusumi #endif
2652f9f168aSTomohiro Kusumi 
2662f9f168aSTomohiro Kusumi 	/* Add device name to help support multiple autofs -media mounts. */
2672f9f168aSTomohiro Kusumi 	if (find_pfs(fp, bref, pfs, &res) == 0 && res) {
2682f9f168aSTomohiro Kusumi 		if (devname)
2692f9f168aSTomohiro Kusumi 			snprintf(label, size, "%s_%s", pfs, devname);
2702f9f168aSTomohiro Kusumi 		else
2713b3b0bfeSTomohiro Kusumi 			strlcpy(label, pfs, size);
2722f9f168aSTomohiro Kusumi 	} else {
27396b636c4STomohiro Kusumi 		memset(label, 0, size);
27496b636c4STomohiro Kusumi 		memcpy(label, media->ipdata.filename,
27596b636c4STomohiro Kusumi 		    sizeof(media->ipdata.filename));
27696b636c4STomohiro Kusumi 		if (devname) {
27796b636c4STomohiro Kusumi 			strlcat(label, "_", size);
27896b636c4STomohiro Kusumi 			strlcat(label, devname, size);
27996b636c4STomohiro Kusumi 		}
2802f9f168aSTomohiro Kusumi 	}
2812f9f168aSTomohiro Kusumi 	if (devname)
2822f9f168aSTomohiro Kusumi 		free(devname);
28326cca7e2STomohiro Kusumi 	free(media);
284841ef9e9STomohiro Kusumi 	error = 0;
285841ef9e9STomohiro Kusumi fail:
28626cca7e2STomohiro Kusumi 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++)
28726cca7e2STomohiro Kusumi 		free(vols[i]);
28826cca7e2STomohiro Kusumi 
28926cca7e2STomohiro Kusumi 	return (error);
29026cca7e2STomohiro Kusumi }
29126cca7e2STomohiro Kusumi 
292030fdd8aSTomohiro Kusumi int
29365d969b4STomohiro Kusumi fstyp_hammer2(FILE *fp, char *label, size_t size, const char *devpath)
294030fdd8aSTomohiro Kusumi {
295030fdd8aSTomohiro Kusumi 	hammer2_volume_data_t *voldata;
296030fdd8aSTomohiro Kusumi 	int error = 1;
297030fdd8aSTomohiro Kusumi 
298c8efd889STomohiro Kusumi 	voldata = read_voldata(fp);
299c8efd889STomohiro Kusumi 	if (test_voldata(voldata))
300841ef9e9STomohiro Kusumi 		goto fail;
301030fdd8aSTomohiro Kusumi 
302c8efd889STomohiro Kusumi 	error = read_label(fp, label, size, devpath);
303841ef9e9STomohiro Kusumi fail:
304030fdd8aSTomohiro Kusumi 	free(voldata);
305030fdd8aSTomohiro Kusumi 	return (error);
306030fdd8aSTomohiro Kusumi }
307