1 /* $NetBSD: hammer2.c,v 1.4 2020/01/15 15:32:05 tkusumi Exp $ */ 2 3 /*- 4 * Copyright (c) 2017-2019 The DragonFly Project 5 * Copyright (c) 2017-2019 Tomohiro Kusumi <tkusumi@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.4 2020/01/15 15:32:05 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_MINIOSIZE - 1); 83 boff = (size_t)(io_off - io_base); 84 85 io_bytes = HAMMER2_MINIOSIZE; 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