hammer2.c revision 1.6 1 /* $NetBSD: hammer2.c,v 1.6 2020/09/23 14:39:23 tkusumi Exp $ */
2
3 /*-
4 * Copyright (c) 2017-2019 The DragonFly Project
5 * Copyright (c) 2017-2019 Tomohiro Kusumi <tkusumi (at) netbsd.org>
6 * All rights reserved.
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 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: hammer2.c,v 1.6 2020/09/23 14:39:23 tkusumi Exp $");
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdbool.h>
35 #include <string.h>
36 #include <err.h>
37 #include <assert.h>
38
39 #include "fstyp.h"
40 #include "hammer2_disk.h"
41
42 static hammer2_volume_data_t*
43 read_voldata(FILE *fp)
44 {
45 hammer2_volume_data_t *voldata;
46
47 voldata = read_buf(fp, 0, sizeof(*voldata));
48 if (voldata == NULL)
49 err(1, "failed to read volume data");
50
51 return (voldata);
52 }
53
54 static int
55 test_voldata(const hammer2_volume_data_t *voldata)
56 {
57 if (voldata->magic != HAMMER2_VOLUME_ID_HBO &&
58 voldata->magic != HAMMER2_VOLUME_ID_ABO)
59 return (1);
60
61 return (0);
62 }
63
64 static hammer2_media_data_t*
65 read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes)
66 {
67 hammer2_media_data_t *media;
68 hammer2_off_t io_off, io_base;
69 size_t bytes, io_bytes, boff;
70
71 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
72 if (bytes)
73 bytes = (size_t)1 << bytes;
74 *media_bytes = bytes;
75
76 if (!bytes) {
77 warnx("blockref has no data");
78 return (NULL);
79 }
80
81 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
82 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
83 boff = (size_t)(io_off - io_base);
84
85 io_bytes = HAMMER2_LBUFSIZE;
86 while (io_bytes + boff < bytes)
87 io_bytes <<= 1;
88
89 if (io_bytes > sizeof(hammer2_media_data_t)) {
90 warnx("invalid I/O bytes");
91 return (NULL);
92 }
93
94 if (fseek(fp, (long int)io_base, SEEK_SET) == -1) {
95 warnx("failed to seek media");
96 return (NULL);
97 }
98 media = read_buf(fp, (off_t)io_base, io_bytes);
99 if (media == NULL) {
100 warnx("failed to read media");
101 return (NULL);
102 }
103 if (boff)
104 memcpy(media, (char *)media + boff, bytes);
105
106 return (media);
107 }
108
109 static int
110 find_pfs(FILE *fp, const hammer2_blockref_t *bref, const char *pfs, bool *res)
111 {
112 hammer2_media_data_t *media;
113 hammer2_inode_data_t ipdata;
114 hammer2_blockref_t *bscan;
115 size_t bytes;
116 int i, bcount;
117
118 media = read_media(fp, bref, &bytes);
119 if (media == NULL)
120 return (-1);
121
122 switch (bref->type) {
123 case HAMMER2_BREF_TYPE_INODE:
124 ipdata = media->ipdata;
125 if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
126 bscan = &ipdata.u.blockset.blockref[0];
127 bcount = HAMMER2_SET_COUNT;
128 } else {
129 bscan = NULL;
130 bcount = 0;
131 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
132 if (memchr(ipdata.filename, 0,
133 sizeof(ipdata.filename))) {
134 if (!strcmp(
135 (const char*)ipdata.filename, pfs))
136 *res = true;
137 } else {
138 if (strlen(pfs) > 0 &&
139 !memcmp(ipdata.filename, pfs,
140 strlen(pfs)))
141 *res = true;
142 }
143 } else
144 assert(0);
145 }
146 break;
147 case HAMMER2_BREF_TYPE_INDIRECT:
148 bscan = &media->npdata[0];
149 bcount = (int)(bytes / sizeof(hammer2_blockref_t));
150 break;
151 default:
152 bscan = NULL;
153 bcount = 0;
154 break;
155 }
156
157 for (i = 0; i < bcount; ++i) {
158 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) {
159 if (find_pfs(fp, &bscan[i], pfs, res) == -1) {
160 free(media);
161 return (-1);
162 }
163 }
164 }
165 free(media);
166
167 return (0);
168 }
169
170 static char*
171 extract_device_name(const char *devpath)
172 {
173 char *p, *head;
174
175 if (!devpath)
176 return NULL;
177
178 p = strdup(devpath);
179 head = p;
180
181 p = strchr(p, '@');
182 if (p)
183 *p = 0;
184
185 p = strrchr(head, '/');
186 if (p) {
187 p++;
188 if (*p == 0) {
189 free(head);
190 return NULL;
191 }
192 p = strdup(p);
193 free(head);
194 return p;
195 }
196
197 return head;
198 }
199
200 static int
201 read_label(FILE *fp, char *label, size_t size)
202 {
203 hammer2_blockref_t broot, best, *bref;
204 hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media;
205 size_t bytes;
206 bool res = false;
207 int i, best_i, error = 1;
208 const char *pfs;
209 char *devname;
210
211 best_i = -1;
212 memset(&best, 0, sizeof(best));
213
214 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) {
215 memset(&broot, 0, sizeof(broot));
216 broot.type = HAMMER2_BREF_TYPE_VOLUME;
217 broot.data_off = ((hammer2_off_t)i *
218 (hammer2_off_t)HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
219 vols[i] = read_buf(fp,
220 (off_t)(broot.data_off & ~HAMMER2_OFF_MASK_RADIX),
221 sizeof(*vols[i]));
222 broot.mirror_tid = vols[i]->voldata.mirror_tid;
223 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
224 best_i = i;
225 best = broot;
226 }
227 }
228
229 bref = &vols[best_i]->voldata.sroot_blockset.blockref[0];
230 if (bref->type != HAMMER2_BREF_TYPE_INODE) {
231 warnx("blockref type is not inode");
232 goto fail;
233 }
234
235 media = read_media(fp, bref, &bytes);
236 if (media == NULL) {
237 goto fail;
238 }
239
240 /*
241 * fstyp_function in DragonFly takes an additional devpath argument
242 * which doesn't exist in FreeBSD and NetBSD.
243 */
244 #ifdef HAS_DEVPATH
245 pfs = strchr(devpath, '@');
246 if (!pfs) {
247 assert(strlen(devpath));
248 switch (devpath[strlen(devpath) - 1]) {
249 case 'a':
250 pfs = "BOOT";
251 break;
252 case 'd':
253 pfs = "ROOT";
254 break;
255 default:
256 pfs = "DATA";
257 break;
258 }
259 } else
260 pfs++;
261
262 if (strlen(pfs) > HAMMER2_INODE_MAXNAME) {
263 goto fail;
264 }
265 devname = extract_device_name(devpath);
266 #else
267 pfs = "";
268 devname = extract_device_name(NULL);
269 assert(!devname);
270 #endif
271
272 /* Add device name to help support multiple autofs -media mounts. */
273 if (find_pfs(fp, bref, pfs, &res) == 0 && res) {
274 if (devname)
275 snprintf(label, size, "%s_%s", pfs, devname);
276 else
277 strlcpy(label, pfs, size);
278 } else {
279 memset(label, 0, size);
280 memcpy(label, media->ipdata.filename,
281 sizeof(media->ipdata.filename));
282 if (devname) {
283 strlcat(label, "_", size);
284 strlcat(label, devname, size);
285 }
286 }
287 if (devname)
288 free(devname);
289 free(media);
290 error = 0;
291 fail:
292 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++)
293 free(vols[i]);
294
295 return (error);
296 }
297
298 int
299 fstyp_hammer2(FILE *fp, char *label, size_t size)
300 {
301 hammer2_volume_data_t *voldata;
302 int error = 1;
303
304 voldata = read_voldata(fp);
305 if (test_voldata(voldata))
306 goto fail;
307
308 error = read_label(fp, label, size);
309 fail:
310 free(voldata);
311 return (error);
312 }
313