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