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 (!strcmp(ipdata.filename, pfs)) 131 *res = true; 132 } else 133 assert(0); 134 } 135 break; 136 case HAMMER2_BREF_TYPE_INDIRECT: 137 bscan = &media->npdata[0]; 138 bcount = bytes / sizeof(hammer2_blockref_t); 139 break; 140 default: 141 bscan = NULL; 142 bcount = 0; 143 break; 144 } 145 146 for (i = 0; i < bcount; ++i) { 147 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) { 148 if (__find_pfs(fp, &bscan[i], pfs, res) == -1) { 149 free(media); 150 return (-1); 151 } 152 } 153 } 154 free(media); 155 156 return (0); 157 } 158 159 static int 160 __read_label(FILE *fp, char *label, size_t size, const char *devpath) 161 { 162 hammer2_blockref_t broot, best, *bref; 163 hammer2_media_data_t *vols[HAMMER2_NUM_VOLHDRS], *media; 164 size_t bytes; 165 bool res = false; 166 int i, best_i, error = 0; 167 const char *pfs; 168 169 best_i = -1; 170 memset(&best, 0, sizeof(best)); 171 172 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) { 173 memset(&broot, 0, sizeof(broot)); 174 broot.type = HAMMER2_BREF_TYPE_VOLUME; 175 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 176 vols[i] = read_buf(fp, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 177 sizeof(*vols[i])); 178 broot.mirror_tid = vols[i]->voldata.mirror_tid; 179 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 180 best_i = i; 181 best = broot; 182 } 183 } 184 if (best_i == -1) { 185 warnx("Failed to find best zone"); 186 error = 1; 187 goto done; 188 } 189 190 bref = &vols[best_i]->voldata.sroot_blockset.blockref[0]; 191 if (bref->type != HAMMER2_BREF_TYPE_INODE) { 192 warnx("Blockref type is not inode"); 193 error = 1; 194 goto done; 195 } 196 197 media = __read_media(fp, bref, &bytes); 198 if (media == NULL) { 199 error = 1; 200 goto done; 201 } 202 203 pfs = strchr(devpath, '@'); 204 if (!pfs) { 205 assert(strlen(devpath)); 206 switch (devpath[strlen(devpath) - 1]) { 207 case 'a': 208 pfs = "BOOT"; 209 break; 210 case 'd': 211 pfs = "ROOT"; 212 break; 213 default: 214 pfs = "DATA"; 215 break; 216 } 217 } else 218 pfs++; 219 220 /* XXX autofs -media mount can't handle multiple mounts */ 221 if (__find_pfs(fp, bref, pfs, &res) == 0 && res) 222 strlcpy(label, pfs, size); 223 else 224 strlcpy(label, (char*)media->ipdata.filename, size); 225 free(media); 226 done: 227 for (i = 0; i < HAMMER2_NUM_VOLHDRS; i++) 228 free(vols[i]); 229 230 return (error); 231 } 232 233 int 234 fstyp_hammer2(FILE *fp, char *label, size_t size, const char *devpath) 235 { 236 hammer2_volume_data_t *voldata; 237 int error = 1; 238 239 voldata = __read_voldata(fp); 240 if (__test_voldata(voldata)) 241 goto done; 242 243 error = __read_label(fp, label, size, devpath); 244 done: 245 free(voldata); 246 return (error); 247 } 248