1 /*- 2 * Copyright (c) 2017-2019 The DragonFly Project 3 * Copyright (c) 2017-2019 Tomohiro Kusumi <tkusumi@netbsd.org> 4 * All rights reserved. 5 * 6 * This software was developed by Edward Tomasz Napierala under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 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 <sys/types.h> 40 41 #include "hammer2_disk.h" 42 43 #include "fstyp.h" 44 45 static hammer2_volume_data_t* 46 read_voldata(FILE *fp) 47 { 48 hammer2_volume_data_t *voldata; 49 50 voldata = read_buf(fp, 0, sizeof(*voldata)); 51 if (voldata == NULL) 52 err(1, "failed to read volume data"); 53 54 return (voldata); 55 } 56 57 static int 58 test_voldata(const hammer2_volume_data_t *voldata) 59 { 60 if (voldata->magic != HAMMER2_VOLUME_ID_HBO && 61 voldata->magic != HAMMER2_VOLUME_ID_ABO) 62 return (1); 63 64 return (0); 65 } 66 67 static hammer2_media_data_t* 68 read_media(FILE *fp, const hammer2_blockref_t *bref, size_t *media_bytes) 69 { 70 hammer2_media_data_t *media; 71 hammer2_off_t io_off, io_base; 72 size_t bytes, io_bytes, boff; 73 74 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 75 if (bytes) 76 bytes = (size_t)1 << bytes; 77 *media_bytes = bytes; 78 79 if (!bytes) { 80 warnx("blockref has no data"); 81 return (NULL); 82 } 83 84 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 85 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 86 boff = io_off - io_base; 87 88 io_bytes = HAMMER2_MINIOSIZE; 89 while (io_bytes + boff < bytes) 90 io_bytes <<= 1; 91 92 if (io_bytes > sizeof(hammer2_media_data_t)) { 93 warnx("invalid I/O bytes"); 94 return (NULL); 95 } 96 97 if (fseek(fp, io_base, SEEK_SET) == -1) { 98 warnx("failed to seek media"); 99 return (NULL); 100 } 101 media = read_buf(fp, io_base, io_bytes); 102 if (media == NULL) { 103 warnx("failed to read media"); 104 return (NULL); 105 } 106 if (boff) 107 memcpy(media, (char *)media + boff, bytes); 108 109 return (media); 110 } 111 112 static int 113 find_pfs(FILE *fp, const hammer2_blockref_t *bref, const char *pfs, bool *res) 114 { 115 hammer2_media_data_t *media; 116 hammer2_inode_data_t ipdata; 117 hammer2_blockref_t *bscan; 118 size_t bytes; 119 int i, bcount; 120 121 media = read_media(fp, bref, &bytes); 122 if (media == NULL) 123 return (-1); 124 125 switch (bref->type) { 126 case HAMMER2_BREF_TYPE_INODE: 127 ipdata = media->ipdata; 128 if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 129 bscan = &ipdata.u.blockset.blockref[0]; 130 bcount = HAMMER2_SET_COUNT; 131 } else { 132 bscan = NULL; 133 bcount = 0; 134 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) { 135 if (memchr(ipdata.filename, 0, 136 sizeof(ipdata.filename))) { 137 if (!strcmp( 138 (const char*)ipdata.filename, pfs)) 139 *res = true; 140 } else { 141 if (strlen(pfs) > 0 && 142 !memcmp(ipdata.filename, pfs, 143 strlen(pfs))) 144 *res = true; 145 } 146 } else 147 assert(0); 148 } 149 break; 150 case HAMMER2_BREF_TYPE_INDIRECT: 151 bscan = &media->npdata[0]; 152 bcount = bytes / sizeof(hammer2_blockref_t); 153 break; 154 default: 155 bscan = NULL; 156 bcount = 0; 157 break; 158 } 159 160 for (i = 0; i < bcount; ++i) { 161 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) { 162 if (find_pfs(fp, &bscan[i], pfs, res) == -1) { 163 free(media); 164 return (-1); 165 } 166 } 167 } 168 free(media); 169 170 return (0); 171 } 172 173 static char* 174 extract_device_name(const char *devpath) 175 { 176 char *p, *head; 177 178 if (!devpath) 179 return NULL; 180 181 p = strdup(devpath); 182 head = p; 183 184 p = strchr(p, '@'); 185 if (p) 186 *p = 0; 187 188 p = strrchr(head, '/'); 189 if (p) { 190 p++; 191 if (*p == 0) { 192 free(head); 193 return NULL; 194 } 195 p = strdup(p); 196 free(head); 197 return p; 198 } 199 200 return head; 201 } 202 203 static int 204 read_label(FILE *fp, char *label, size_t size) 205 { 206 hammer2_blockref_t broot, best, *bref; 207 hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media; 208 size_t bytes; 209 bool res = false; 210 int i, best_i, error = 1; 211 const char *pfs; 212 char *devname; 213 214 best_i = -1; 215 memset(&best, 0, sizeof(best)); 216 217 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) { 218 memset(&broot, 0, sizeof(broot)); 219 broot.type = HAMMER2_BREF_TYPE_VOLUME; 220 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 221 vols[i] = read_buf(fp, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 222 sizeof(*vols[i])); 223 broot.mirror_tid = vols[i]->voldata.mirror_tid; 224 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 225 best_i = i; 226 best = broot; 227 } 228 } 229 230 bref = &vols[best_i]->voldata.sroot_blockset.blockref[0]; 231 if (bref->type != HAMMER2_BREF_TYPE_INODE) { 232 warnx("blockref type is not inode"); 233 goto fail; 234 } 235 236 media = read_media(fp, bref, &bytes); 237 if (media == NULL) { 238 goto fail; 239 } 240 241 pfs = ""; 242 devname = extract_device_name(NULL); 243 assert(!devname); /* Currently always NULL in FreeBSD. */ 244 245 /* Add device name to help support multiple autofs -media mounts. */ 246 if (find_pfs(fp, bref, pfs, &res) == 0 && res) { 247 if (devname) 248 snprintf(label, size, "%s_%s", pfs, devname); 249 else 250 strlcpy(label, pfs, size); 251 } else { 252 memset(label, 0, size); 253 memcpy(label, media->ipdata.filename, 254 sizeof(media->ipdata.filename)); 255 if (devname) { 256 strlcat(label, "_", size); 257 strlcat(label, devname, size); 258 } 259 } 260 if (devname) 261 free(devname); 262 free(media); 263 error = 0; 264 fail: 265 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) 266 free(vols[i]); 267 268 return (error); 269 } 270 271 int 272 fstyp_hammer2(FILE *fp, char *label, size_t size) 273 { 274 hammer2_volume_data_t *voldata; 275 int error = 1; 276 277 voldata = read_voldata(fp); 278 if (test_voldata(voldata)) 279 goto fail; 280 281 error = read_label(fp, label, size); 282 fail: 283 free(voldata); 284 return (error); 285 } 286