xref: /dragonfly/usr.sbin/fstyp/hammer2.c (revision d8d5b238)
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 (memchr(ipdata.filename, 0,
131 				    sizeof(ipdata.filename))) {
132 					if (!strcmp(
133 					    (const char*)ipdata.filename, pfs))
134 						*res = true;
135 				} else {
136 					if (!memcmp(ipdata.filename, pfs,
137 					    strlen(pfs)))
138 						*res = true;
139 				}
140 			} else
141 				assert(0);
142 		}
143 		break;
144 	case HAMMER2_BREF_TYPE_INDIRECT:
145 		bscan = &media->npdata[0];
146 		bcount = bytes / sizeof(hammer2_blockref_t);
147 		break;
148 	default:
149 		bscan = NULL;
150 		bcount = 0;
151 		break;
152 	}
153 
154 	for (i = 0; i < bcount; ++i) {
155 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) {
156 			if (__find_pfs(fp, &bscan[i], pfs, res) == -1) {
157 				free(media);
158 				return (-1);
159 			}
160 		}
161 	}
162 	free(media);
163 
164 	return (0);
165 }
166 
167 static int
168 __read_label(FILE *fp, char *label, size_t size, const char *devpath)
169 {
170 	hammer2_blockref_t broot, best, *bref;
171 	hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media;
172 	size_t bytes;
173 	bool res = false;
174 	int i, best_i, error = 0;
175 	const char *pfs;
176 
177 	best_i = -1;
178 	memset(&best, 0, sizeof(best));
179 
180 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
181 		memset(&broot, 0, sizeof(broot));
182 		broot.type = HAMMER2_BREF_TYPE_VOLUME;
183 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
184 		vols[i] = read_buf(fp, broot.data_off & ~HAMMER2_OFF_MASK_RADIX,
185 		    sizeof(*vols[i]));
186 		broot.mirror_tid = vols[i]->voldata.mirror_tid;
187 		if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
188 			best_i = i;
189 			best = broot;
190 		}
191 	}
192 	if (best_i == -1) {
193 		warnx("Failed to find best zone");
194 		error = 1;
195 		goto done;
196 	}
197 
198 	bref = &vols[best_i]->voldata.sroot_blockset.blockref[0];
199 	if (bref->type != HAMMER2_BREF_TYPE_INODE) {
200 		warnx("Blockref type is not inode");
201 		error = 1;
202 		goto done;
203 	}
204 
205 	media = __read_media(fp, bref, &bytes);
206 	if (media == NULL) {
207 		error = 1;
208 		goto done;
209 	}
210 
211 	pfs = strchr(devpath, '@');
212 	if (!pfs) {
213 		assert(strlen(devpath));
214 		switch (devpath[strlen(devpath) - 1]) {
215 		case 'a':
216 			pfs = "BOOT";
217 			break;
218 		case 'd':
219 			pfs = "ROOT";
220 			break;
221 		default:
222 			pfs = "DATA";
223 			break;
224 		}
225 	} else
226 		pfs++;
227 
228 	if (strlen(pfs) > HAMMER2_INODE_MAXNAME) {
229 		error = 1;
230 		goto done;
231 	}
232 
233 	/* XXX autofs -media mount can't handle multiple mounts */
234 	if (__find_pfs(fp, bref, pfs, &res) == 0 && res)
235 		strlcpy(label, pfs, size);
236 	else
237 		strlcpy(label, (char*)media->ipdata.filename, size);
238 	free(media);
239 done:
240 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++)
241 		free(vols[i]);
242 
243 	return (error);
244 }
245 
246 int
247 fstyp_hammer2(FILE *fp, char *label, size_t size, const char *devpath)
248 {
249 	hammer2_volume_data_t *voldata;
250 	int error = 1;
251 
252 	voldata = __read_voldata(fp);
253 	if (__test_voldata(voldata))
254 		goto done;
255 
256 	error = __read_label(fp, label, size, devpath);
257 done:
258 	free(voldata);
259 	return (error);
260 }
261