1/*- 2 * Copyright (c) 2002 Poul-Henning Kamp 3 * Copyright (c) 2002 Networks Associates Technology, Inc. 4 * All rights reserved. 5 * 6 * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7 * and NAI Labs, the Security Research Division of Network Associates, Inc. 8 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9 * DARPA CHATS research program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: releng/11.0/sys/geom/geom_mbr.c 300288 2016-05-20 08:25:37Z kib $"); 35 36#include <sys/param.h> 37#include <sys/errno.h> 38#include <sys/endian.h> 39#include <sys/systm.h> 40#include <sys/sysctl.h> 41#include <sys/kernel.h> 42#include <sys/fcntl.h> 43#include <sys/malloc.h> 44#include <sys/bio.h> 45#include <sys/lock.h> 46#include <sys/mutex.h> 47#include <sys/md5.h> 48#include <sys/proc.h> 49 50#include <sys/diskmbr.h> 51#include <sys/sbuf.h> 52#include <geom/geom.h> 53#include <geom/geom_slice.h> 54 55FEATURE(geom_mbr, "GEOM DOS/MBR partitioning support"); 56 57#define MBR_CLASS_NAME "MBR" 58#define MBREXT_CLASS_NAME "MBREXT" 59 60static struct dos_partition historical_bogus_partition_table[NDOSPART] = { 61 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, 62 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, 63 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, 64 { 0x80, 0, 1, 0, DOSPTYP_386BSD, 255, 255, 255, 0, 50000, }, 65}; 66 67static struct dos_partition historical_bogus_partition_table_fixed[NDOSPART] = { 68 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, 69 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, 70 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, 71 { 0x80, 0, 1, 0, DOSPTYP_386BSD, 254, 255, 255, 0, 50000, }, 72}; 73 74static void 75g_mbr_print(int i, struct dos_partition *dp) 76{ 77 78 printf("[%d] f:%02x typ:%d", i, dp->dp_flag, dp->dp_typ); 79 printf(" s(CHS):%d/%d/%d", DPCYL(dp->dp_scyl, dp->dp_ssect), 80 dp->dp_shd, DPSECT(dp->dp_ssect)); 81 printf(" e(CHS):%d/%d/%d", DPCYL(dp->dp_ecyl, dp->dp_esect), 82 dp->dp_ehd, DPSECT(dp->dp_esect)); 83 printf(" s:%d l:%d\n", dp->dp_start, dp->dp_size); 84} 85 86struct g_mbr_softc { 87 int type [NDOSPART]; 88 u_int sectorsize; 89 u_char sec0[512]; 90 u_char slicesum[16]; 91}; 92 93/* 94 * XXX: Add gctl_req arg and give good error msgs. 95 * XXX: Check that length argument does not bring boot code inside any slice. 96 */ 97static int 98g_mbr_modify(struct g_geom *gp, struct g_mbr_softc *ms, u_char *sec0, int len __unused) 99{ 100 int i, error; 101 off_t l[NDOSPART]; 102 struct dos_partition ndp[NDOSPART], *dp; 103 MD5_CTX md5sum; 104 105 g_topology_assert(); 106 107 if (sec0[0x1fe] != 0x55 && sec0[0x1ff] != 0xaa) 108 return (EBUSY); 109 110 dp = ndp; 111 for (i = 0; i < NDOSPART; i++) { 112 dos_partition_dec( 113 sec0 + DOSPARTOFF + i * sizeof(struct dos_partition), 114 dp + i); 115 } 116 if ((!bcmp(dp, historical_bogus_partition_table, 117 sizeof historical_bogus_partition_table)) || 118 (!bcmp(dp, historical_bogus_partition_table_fixed, 119 sizeof historical_bogus_partition_table_fixed))) { 120 /* 121 * We will not allow people to write these from "the inside", 122 * Since properly selfdestructing takes too much code. If 123 * people really want to do this, they cannot have any 124 * providers of this geom open, and in that case they can just 125 * as easily overwrite the MBR in the parent device. 126 */ 127 return(EBUSY); 128 } 129 for (i = 0; i < NDOSPART; i++) { 130 /* 131 * A Protective MBR (PMBR) has a single partition of 132 * type 0xEE spanning the whole disk. Such a MBR 133 * protects a GPT on the disk from MBR tools that 134 * don't know anything about GPT. We're interpreting 135 * it a bit more loosely: any partition of type 0xEE 136 * is to be skipped as it doesn't contain any data 137 * that we should care about. We still allow other 138 * partitions to be present in the MBR. A PMBR will 139 * be handled correctly anyway. 140 */ 141 if (dp[i].dp_typ == DOSPTYP_PMBR) 142 l[i] = 0; 143 else if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) 144 l[i] = 0; 145 else if (dp[i].dp_typ == 0) 146 l[i] = 0; 147 else 148 l[i] = (off_t)dp[i].dp_size * ms->sectorsize; 149 error = g_slice_config(gp, i, G_SLICE_CONFIG_CHECK, 150 (off_t)dp[i].dp_start * ms->sectorsize, l[i], 151 ms->sectorsize, "%ss%d", gp->name, 1 + i); 152 if (error) 153 return (error); 154 } 155 for (i = 0; i < NDOSPART; i++) { 156 ms->type[i] = dp[i].dp_typ; 157 g_slice_config(gp, i, G_SLICE_CONFIG_SET, 158 (off_t)dp[i].dp_start * ms->sectorsize, l[i], 159 ms->sectorsize, "%ss%d", gp->name, 1 + i); 160 } 161 bcopy(sec0, ms->sec0, 512); 162 163 /* 164 * Calculate MD5 from the first sector and use it for avoiding 165 * recursive slices creation. 166 */ 167 MD5Init(&md5sum); 168 MD5Update(&md5sum, ms->sec0, sizeof(ms->sec0)); 169 MD5Final(ms->slicesum, &md5sum); 170 171 return (0); 172} 173 174static int 175g_mbr_ioctl(struct g_provider *pp, u_long cmd, void *data, int fflag, struct thread *td) 176{ 177 struct g_geom *gp; 178 struct g_mbr_softc *ms; 179 struct g_slicer *gsp; 180 struct g_consumer *cp; 181 int error, opened; 182 183 gp = pp->geom; 184 gsp = gp->softc; 185 ms = gsp->softc; 186 187 opened = 0; 188 error = 0; 189 switch(cmd) { 190 case DIOCSMBR: { 191 if (!(fflag & FWRITE)) 192 return (EPERM); 193 g_topology_lock(); 194 cp = LIST_FIRST(&gp->consumer); 195 if (cp->acw == 0) { 196 error = g_access(cp, 0, 1, 0); 197 if (error == 0) 198 opened = 1; 199 } 200 if (!error) 201 error = g_mbr_modify(gp, ms, data, 512); 202 if (!error) 203 error = g_write_data(cp, 0, data, 512); 204 if (opened) 205 g_access(cp, 0, -1 , 0); 206 g_topology_unlock(); 207 return(error); 208 } 209 default: 210 return (ENOIOCTL); 211 } 212} 213 214static int 215g_mbr_start(struct bio *bp) 216{ 217 struct g_provider *pp; 218 struct g_geom *gp; 219 struct g_mbr_softc *mp; 220 struct g_slicer *gsp; 221 int idx; 222 223 pp = bp->bio_to; 224 idx = pp->index; 225 gp = pp->geom; 226 gsp = gp->softc; 227 mp = gsp->softc; 228 if (bp->bio_cmd == BIO_GETATTR) { 229 if (g_handleattr_int(bp, "MBR::type", mp->type[idx])) 230 return (1); 231 if (g_handleattr_off_t(bp, "MBR::offset", 232 gsp->slices[idx].offset)) 233 return (1); 234 if (g_handleattr(bp, "MBR::slicesum", mp->slicesum, 235 sizeof(mp->slicesum))) 236 return (1); 237 } 238 239 return (0); 240} 241 242static void 243g_mbr_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp __unused, struct g_provider *pp) 244{ 245 struct g_mbr_softc *mp; 246 struct g_slicer *gsp; 247 248 gsp = gp->softc; 249 mp = gsp->softc; 250 g_slice_dumpconf(sb, indent, gp, cp, pp); 251 if (pp != NULL) { 252 if (indent == NULL) 253 sbuf_printf(sb, " ty %d", mp->type[pp->index]); 254 else 255 sbuf_printf(sb, "%s<type>%d</type>\n", indent, 256 mp->type[pp->index]); 257 } 258} 259 260static struct g_geom * 261g_mbr_taste(struct g_class *mp, struct g_provider *pp, int insist) 262{ 263 struct g_geom *gp; 264 struct g_consumer *cp; 265 int error; 266 struct g_mbr_softc *ms; 267 u_int fwsectors, sectorsize; 268 u_char *buf; 269 u_char hash[16]; 270 MD5_CTX md5sum; 271 272 g_trace(G_T_TOPOLOGY, "mbr_taste(%s,%s)", mp->name, pp->name); 273 g_topology_assert(); 274 if (!strcmp(pp->geom->class->name, MBR_CLASS_NAME)) 275 return (NULL); 276 gp = g_slice_new(mp, NDOSPART, pp, &cp, &ms, sizeof *ms, g_mbr_start); 277 if (gp == NULL) 278 return (NULL); 279 g_topology_unlock(); 280 do { 281 error = g_getattr("GEOM::fwsectors", cp, &fwsectors); 282 if (error) 283 fwsectors = 17; 284 sectorsize = cp->provider->sectorsize; 285 if (sectorsize < 512) 286 break; 287 ms->sectorsize = sectorsize; 288 buf = g_read_data(cp, 0, sectorsize, NULL); 289 if (buf == NULL) 290 break; 291 292 /* 293 * Calculate MD5 from the first sector and use it for avoiding 294 * recursive slices creation. 295 */ 296 bcopy(buf, ms->sec0, 512); 297 MD5Init(&md5sum); 298 MD5Update(&md5sum, ms->sec0, sizeof(ms->sec0)); 299 MD5Final(ms->slicesum, &md5sum); 300 301 error = g_getattr("MBR::slicesum", cp, &hash); 302 if (!error && !bcmp(ms->slicesum, hash, sizeof(hash))) { 303 g_free(buf); 304 break; 305 } 306 307 g_topology_lock(); 308 g_mbr_modify(gp, ms, buf, 512); 309 g_topology_unlock(); 310 g_free(buf); 311 break; 312 } while (0); 313 g_topology_lock(); 314 g_access(cp, -1, 0, 0); 315 if (LIST_EMPTY(&gp->provider)) { 316 g_slice_spoiled(cp); 317 return (NULL); 318 } 319 return (gp); 320} 321 322static void 323g_mbr_config(struct gctl_req *req, struct g_class *mp, const char *verb) 324{ 325 struct g_geom *gp; 326 struct g_consumer *cp; 327 struct g_mbr_softc *ms; 328 struct g_slicer *gsp; 329 int opened = 0, error = 0; 330 void *data; 331 int len; 332 333 g_topology_assert(); 334 gp = gctl_get_geom(req, mp, "geom"); 335 if (gp == NULL) 336 return; 337 if (strcmp(verb, "write MBR")) { 338 gctl_error(req, "Unknown verb"); 339 return; 340 } 341 gsp = gp->softc; 342 ms = gsp->softc; 343 data = gctl_get_param(req, "data", &len); 344 if (data == NULL) 345 return; 346 if (len < 512 || (len % 512)) { 347 gctl_error(req, "Wrong request length"); 348 return; 349 } 350 cp = LIST_FIRST(&gp->consumer); 351 if (cp->acw == 0) { 352 error = g_access(cp, 0, 1, 0); 353 if (error == 0) 354 opened = 1; 355 } 356 if (!error) 357 error = g_mbr_modify(gp, ms, data, len); 358 if (error) 359 gctl_error(req, "conflict with open slices"); 360 if (!error) 361 error = g_write_data(cp, 0, data, len); 362 if (error) 363 gctl_error(req, "sector zero write failed"); 364 if (opened) 365 g_access(cp, 0, -1 , 0); 366 return; 367} 368 369static struct g_class g_mbr_class = { 370 .name = MBR_CLASS_NAME, 371 .version = G_VERSION, 372 .taste = g_mbr_taste, 373 .dumpconf = g_mbr_dumpconf, 374 .ctlreq = g_mbr_config, 375 .ioctl = g_mbr_ioctl, 376}; 377 378DECLARE_GEOM_CLASS(g_mbr_class, g_mbr); 379 380#define NDOSEXTPART 32 381struct g_mbrext_softc { 382 int type [NDOSEXTPART]; 383}; 384 385static int 386g_mbrext_start(struct bio *bp) 387{ 388 struct g_provider *pp; 389 struct g_geom *gp; 390 struct g_mbrext_softc *mp; 391 struct g_slicer *gsp; 392 int idx; 393 394 pp = bp->bio_to; 395 idx = pp->index; 396 gp = pp->geom; 397 gsp = gp->softc; 398 mp = gsp->softc; 399 if (bp->bio_cmd == BIO_GETATTR) { 400 if (g_handleattr_int(bp, "MBR::type", mp->type[idx])) 401 return (1); 402 } 403 return (0); 404} 405 406static void 407g_mbrext_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp __unused, struct g_provider *pp) 408{ 409 struct g_mbrext_softc *mp; 410 struct g_slicer *gsp; 411 412 g_slice_dumpconf(sb, indent, gp, cp, pp); 413 gsp = gp->softc; 414 mp = gsp->softc; 415 if (pp != NULL) { 416 if (indent == NULL) 417 sbuf_printf(sb, " ty %d", mp->type[pp->index]); 418 else 419 sbuf_printf(sb, "%s<type>%d</type>\n", indent, 420 mp->type[pp->index]); 421 } 422} 423 424static struct g_geom * 425g_mbrext_taste(struct g_class *mp, struct g_provider *pp, int insist __unused) 426{ 427 struct g_geom *gp; 428 struct g_consumer *cp; 429 int error, i, slice; 430 struct g_mbrext_softc *ms; 431 off_t off; 432 u_char *buf; 433 struct dos_partition dp[4]; 434 u_int fwsectors, sectorsize; 435 436 g_trace(G_T_TOPOLOGY, "g_mbrext_taste(%s,%s)", mp->name, pp->name); 437 g_topology_assert(); 438 if (strcmp(pp->geom->class->name, MBR_CLASS_NAME)) 439 return (NULL); 440 gp = g_slice_new(mp, NDOSEXTPART, pp, &cp, &ms, sizeof *ms, 441 g_mbrext_start); 442 if (gp == NULL) 443 return (NULL); 444 g_topology_unlock(); 445 off = 0; 446 slice = 0; 447 do { 448 error = g_getattr("MBR::type", cp, &i); 449 if (error || (i != DOSPTYP_EXT && i != DOSPTYP_EXTLBA)) 450 break; 451 error = g_getattr("GEOM::fwsectors", cp, &fwsectors); 452 if (error) 453 fwsectors = 17; 454 sectorsize = cp->provider->sectorsize; 455 if (sectorsize != 512) 456 break; 457 for (;;) { 458 buf = g_read_data(cp, off, sectorsize, NULL); 459 if (buf == NULL) 460 break; 461 if (buf[0x1fe] != 0x55 && buf[0x1ff] != 0xaa) { 462 g_free(buf); 463 break; 464 } 465 for (i = 0; i < NDOSPART; i++) 466 dos_partition_dec( 467 buf + DOSPARTOFF + 468 i * sizeof(struct dos_partition), dp + i); 469 g_free(buf); 470 if (0 && bootverbose) { 471 printf("MBREXT Slice %d on %s:\n", 472 slice + 5, gp->name); 473 g_mbr_print(0, dp); 474 g_mbr_print(1, dp + 1); 475 } 476 if ((dp[0].dp_flag & 0x7f) == 0 && 477 dp[0].dp_size != 0 && dp[0].dp_typ != 0) { 478 g_topology_lock(); 479 g_slice_config(gp, slice, G_SLICE_CONFIG_SET, 480 (((off_t)dp[0].dp_start) << 9ULL) + off, 481 ((off_t)dp[0].dp_size) << 9ULL, 482 sectorsize, 483 "%*.*s%d", 484 (int)strlen(gp->name) - 1, 485 (int)strlen(gp->name) - 1, 486 gp->name, 487 slice + 5); 488 g_topology_unlock(); 489 ms->type[slice] = dp[0].dp_typ; 490 slice++; 491 } 492 if (dp[1].dp_flag != 0) 493 break; 494 if (dp[1].dp_typ != DOSPTYP_EXT && 495 dp[1].dp_typ != DOSPTYP_EXTLBA) 496 break; 497 if (dp[1].dp_size == 0) 498 break; 499 off = ((off_t)dp[1].dp_start) << 9ULL; 500 } 501 break; 502 } while (0); 503 g_topology_lock(); 504 g_access(cp, -1, 0, 0); 505 if (LIST_EMPTY(&gp->provider)) { 506 g_slice_spoiled(cp); 507 return (NULL); 508 } 509 return (gp); 510} 511 512 513static struct g_class g_mbrext_class = { 514 .name = MBREXT_CLASS_NAME, 515 .version = G_VERSION, 516 .taste = g_mbrext_taste, 517 .dumpconf = g_mbrext_dumpconf, 518}; 519 520DECLARE_GEOM_CLASS(g_mbrext_class, g_mbrext); 521