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