xref: /dragonfly/usr.sbin/fstyp/hammer2.c (revision ed183f8c)
1 /*-
2  * Copyright (c) 2017-2019 The DragonFly Project
3  * All rights reserved.
4  *
5  * This software was developed by Edward Tomasz Napierala under sponsorship
6  * from the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
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 the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdbool.h>
33 #include <string.h>
34 #include <err.h>
35 #include <assert.h>
36 #include <vfs/hammer2/hammer2_disk.h>
37 
38 #include "fstyp.h"
39 
40 static hammer2_volume_data_t*
41 __read_voldata(FILE *fp)
42 {
43 	hammer2_volume_data_t *voldata;
44 
45 	voldata = read_buf(fp, 0, sizeof(*voldata));
46 	if (voldata == NULL)
47 		err(1, "failed to read volume data");
48 
49 	return (voldata);
50 }
51 
52 static int
53 __test_voldata(const hammer2_volume_data_t *voldata)
54 {
55 	if (voldata->magic != HAMMER2_VOLUME_ID_HBO &&
56 	    voldata->magic != HAMMER2_VOLUME_ID_ABO)
57 		return (1);
58 
59 	return (0);
60 }
61 
62 static hammer2_media_data_t*
63 __read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes)
64 {
65 	hammer2_media_data_t *media;
66 	hammer2_off_t io_off, io_base;
67 	size_t bytes, io_bytes, boff;
68 
69 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
70 	if (bytes)
71 		bytes = (size_t)1 << bytes;
72 	*media_bytes = bytes;
73 
74 	if (!bytes) {
75 		warnx("Blockref has no data");
76 		return (NULL);
77 	}
78 
79 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
80 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
81 	boff = io_off - io_base;
82 
83 	io_bytes = HAMMER2_MINIOSIZE;
84 	while (io_bytes + boff < bytes)
85 		io_bytes <<= 1;
86 
87 	if (io_bytes > sizeof(hammer2_media_data_t)) {
88 		warnx("Invalid I/O bytes");
89 		return (NULL);
90 	}
91 
92 	if (fseek(fp, io_base, SEEK_SET) == -1) {
93 		warnx("Failed to seek media");
94 		return (NULL);
95 	}
96 	media = read_buf(fp, io_base, io_bytes);
97 	if (media == NULL) {
98 		warnx("Failed to read media");
99 		return (NULL);
100 	}
101 	if (boff)
102 		memcpy(media, (char *)media + boff, bytes);
103 
104 	return (media);
105 }
106 
107 static int
108 __find_pfs(FILE *fp, const hammer2_blockref_t *bref, const char *pfs, bool *res)
109 {
110 	hammer2_media_data_t *media;
111 	hammer2_inode_data_t ipdata;
112 	hammer2_blockref_t *bscan;
113 	size_t bytes;
114 	int i, bcount;
115 
116 	media = __read_media(fp, bref, &bytes);
117 	if (media == NULL)
118 		return (-1);
119 
120 	switch (bref->type) {
121 	case HAMMER2_BREF_TYPE_INODE:
122 		ipdata = media->ipdata;
123 		if (ipdata.meta.pfs_type & HAMMER2_PFSTYPE_SUPROOT) {
124 			bscan = &ipdata.u.blockset.blockref[0];
125 			bcount = HAMMER2_SET_COUNT;
126 		} else {
127 			bscan = NULL;
128 			bcount = 0;
129 			if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
130 				if (!strcmp(ipdata.filename, pfs))
131 					*res = true;
132 			} else
133 				assert(0);
134 		}
135 		break;
136 	case HAMMER2_BREF_TYPE_INDIRECT:
137 		bscan = &media->npdata[0];
138 		bcount = bytes / sizeof(hammer2_blockref_t);
139 		break;
140 	default:
141 		bscan = NULL;
142 		bcount = 0;
143 		break;
144 	}
145 
146 	for (i = 0; i < bcount; ++i) {
147 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) {
148 			if (__find_pfs(fp, &bscan[i], pfs, res) == -1) {
149 				free(media);
150 				return (-1);
151 			}
152 		}
153 	}
154 	free(media);
155 
156 	return (0);
157 }
158 
159 static int
160 __read_label(FILE *fp, char *label, size_t size, const char *devpath)
161 {
162 	hammer2_blockref_t broot, best, *bref;
163 	hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media;
164 	size_t bytes;
165 	bool res = false;
166 	int i, best_i, error = 0;
167 	const char *pfs;
168 
169 	best_i = -1;
170 	memset(&best, 0, sizeof(best));
171 
172 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
173 		memset(&broot, 0, sizeof(broot));
174 		broot.type = HAMMER2_BREF_TYPE_VOLUME;
175 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
176 		vols[i] = read_buf(fp, broot.data_off & ~HAMMER2_OFF_MASK_RADIX,
177 		    sizeof(*vols[i]));
178 		broot.mirror_tid = vols[i]->voldata.mirror_tid;
179 		if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
180 			best_i = i;
181 			best = broot;
182 		}
183 	}
184 	if (best_i == -1) {
185 		warnx("Failed to find best zone");
186 		error = 1;
187 		goto done;
188 	}
189 
190 	bref = &vols[best_i]->voldata.sroot_blockset.blockref[0];
191 	if (bref->type != HAMMER2_BREF_TYPE_INODE) {
192 		warnx("Blockref type is not inode");
193 		error = 1;
194 		goto done;
195 	}
196 
197 	media = __read_media(fp, bref, &bytes);
198 	if (media == NULL) {
199 		error = 1;
200 		goto done;
201 	}
202 
203 	pfs = strchr(devpath, '@');
204 	if (!pfs) {
205 		assert(strlen(devpath));
206 		switch (devpath[strlen(devpath) - 1]) {
207 		case 'a':
208 			pfs = "BOOT";
209 			break;
210 		case 'd':
211 			pfs = "ROOT";
212 			break;
213 		default:
214 			pfs = "DATA";
215 			break;
216 		}
217 	} else
218 		pfs++;
219 
220 	/* XXX autofs -media mount can't handle multiple mounts */
221 	if (__find_pfs(fp, bref, pfs, &res) == 0 && res)
222 		strlcpy(label, pfs, size);
223 	else
224 		strlcpy(label, (char*)media->ipdata.filename, size);
225 	free(media);
226 done:
227 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++)
228 		free(vols[i]);
229 
230 	return (error);
231 }
232 
233 int
234 fstyp_hammer2(FILE *fp, char *label, size_t size, const char *devpath)
235 {
236 	hammer2_volume_data_t *voldata;
237 	int error = 1;
238 
239 	voldata = __read_voldata(fp);
240 	if (__test_voldata(voldata))
241 		goto done;
242 
243 	error = __read_label(fp, label, size, devpath);
244 done:
245 	free(voldata);
246 	return (error);
247 }
248