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