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