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