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$"); 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 DROP_GIANT(); 194 g_topology_lock(); 195 cp = LIST_FIRST(&gp->consumer); 196 if (cp->acw == 0) { 197 error = g_access(cp, 0, 1, 0); 198 if (error == 0) 199 opened = 1; 200 } 201 if (!error) 202 error = g_mbr_modify(gp, ms, data, 512); 203 if (!error) 204 error = g_write_data(cp, 0, data, 512); 205 if (opened) 206 g_access(cp, 0, -1 , 0); 207 g_topology_unlock(); 208 PICKUP_GIANT(); 209 return(error); 210 } 211 default: 212 return (ENOIOCTL); 213 } 214} 215 216static int 217g_mbr_start(struct bio *bp) 218{ 219 struct g_provider *pp; 220 struct g_geom *gp; 221 struct g_mbr_softc *mp; 222 struct g_slicer *gsp; 223 int idx; 224 225 pp = bp->bio_to; 226 idx = pp->index; 227 gp = pp->geom; 228 gsp = gp->softc; 229 mp = gsp->softc; 230 if (bp->bio_cmd == BIO_GETATTR) { 231 if (g_handleattr_int(bp, "MBR::type", mp->type[idx])) 232 return (1); 233 if (g_handleattr_off_t(bp, "MBR::offset", 234 gsp->slices[idx].offset)) 235 return (1); 236 if (g_handleattr(bp, "MBR::slicesum", mp->slicesum, 237 sizeof(mp->slicesum))) 238 return (1); 239 } 240 241 return (0); 242} 243 244static void 245g_mbr_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp __unused, struct g_provider *pp) 246{ 247 struct g_mbr_softc *mp; 248 struct g_slicer *gsp; 249 250 gsp = gp->softc; 251 mp = gsp->softc; 252 g_slice_dumpconf(sb, indent, gp, cp, pp); 253 if (pp != NULL) { 254 if (indent == NULL) 255 sbuf_printf(sb, " ty %d", mp->type[pp->index]); 256 else 257 sbuf_printf(sb, "%s<type>%d</type>\n", indent, 258 mp->type[pp->index]); 259 } 260} 261 262static struct g_geom * 263g_mbr_taste(struct g_class *mp, struct g_provider *pp, int insist) 264{ 265 struct g_geom *gp; 266 struct g_consumer *cp; 267 int error; 268 struct g_mbr_softc *ms; 269 u_int fwsectors, sectorsize; 270 u_char *buf; 271 u_char hash[16]; 272 MD5_CTX md5sum; 273 274 g_trace(G_T_TOPOLOGY, "mbr_taste(%s,%s)", mp->name, pp->name); 275 g_topology_assert(); 276 if (!strcmp(pp->geom->class->name, MBR_CLASS_NAME)) 277 return (NULL); 278 gp = g_slice_new(mp, NDOSPART, pp, &cp, &ms, sizeof *ms, g_mbr_start); 279 if (gp == NULL) 280 return (NULL); 281 g_topology_unlock(); 282 do { 283 error = g_getattr("GEOM::fwsectors", cp, &fwsectors); 284 if (error) 285 fwsectors = 17; 286 sectorsize = cp->provider->sectorsize; 287 if (sectorsize < 512) 288 break; 289 ms->sectorsize = sectorsize; 290 buf = g_read_data(cp, 0, sectorsize, NULL); 291 if (buf == NULL) 292 break; 293 294 /* 295 * Calculate MD5 from the first sector and use it for avoiding 296 * recursive slices creation. 297 */ 298 bcopy(buf, ms->sec0, 512); 299 MD5Init(&md5sum); 300 MD5Update(&md5sum, ms->sec0, sizeof(ms->sec0)); 301 MD5Final(ms->slicesum, &md5sum); 302 303 error = g_getattr("MBR::slicesum", cp, &hash); 304 if (!error && !bcmp(ms->slicesum, hash, sizeof(hash))) { 305 g_free(buf); 306 break; 307 } 308 309 g_topology_lock(); 310 g_mbr_modify(gp, ms, buf, 512); 311 g_topology_unlock(); 312 g_free(buf); 313 break; 314 } while (0); 315 g_topology_lock(); 316 g_access(cp, -1, 0, 0); 317 if (LIST_EMPTY(&gp->provider)) { 318 g_slice_spoiled(cp); 319 return (NULL); 320 } 321 return (gp); 322} 323 324static void 325g_mbr_config(struct gctl_req *req, struct g_class *mp, const char *verb) 326{ 327 struct g_geom *gp; 328 struct g_consumer *cp; 329 struct g_mbr_softc *ms; 330 struct g_slicer *gsp; 331 int opened = 0, error = 0; 332 void *data; 333 int len; 334 335 g_topology_assert(); 336 gp = gctl_get_geom(req, mp, "geom"); 337 if (gp == NULL) 338 return; 339 if (strcmp(verb, "write MBR")) { 340 gctl_error(req, "Unknown verb"); 341 return; 342 } 343 gsp = gp->softc; 344 ms = gsp->softc; 345 data = gctl_get_param(req, "data", &len); 346 if (data == NULL) 347 return; 348 if (len < 512 || (len % 512)) { 349 gctl_error(req, "Wrong request length"); 350 return; 351 } 352 cp = LIST_FIRST(&gp->consumer); 353 if (cp->acw == 0) { 354 error = g_access(cp, 0, 1, 0); 355 if (error == 0) 356 opened = 1; 357 } 358 if (!error) 359 error = g_mbr_modify(gp, ms, data, len); 360 if (error) 361 gctl_error(req, "conflict with open slices"); 362 if (!error) 363 error = g_write_data(cp, 0, data, len); 364 if (error) 365 gctl_error(req, "sector zero write failed"); 366 if (opened) 367 g_access(cp, 0, -1 , 0); 368 return; 369} 370 371static struct g_class g_mbr_class = { 372 .name = MBR_CLASS_NAME, 373 .version = G_VERSION, 374 .taste = g_mbr_taste, 375 .dumpconf = g_mbr_dumpconf, 376 .ctlreq = g_mbr_config, 377 .ioctl = g_mbr_ioctl, 378}; 379 380DECLARE_GEOM_CLASS(g_mbr_class, g_mbr); 381 382#define NDOSEXTPART 32 383struct g_mbrext_softc { 384 int type [NDOSEXTPART]; 385}; 386 387static int 388g_mbrext_start(struct bio *bp) 389{ 390 struct g_provider *pp; 391 struct g_geom *gp; 392 struct g_mbrext_softc *mp; 393 struct g_slicer *gsp; 394 int idx; 395 396 pp = bp->bio_to; 397 idx = pp->index; 398 gp = pp->geom; 399 gsp = gp->softc; 400 mp = gsp->softc; 401 if (bp->bio_cmd == BIO_GETATTR) { 402 if (g_handleattr_int(bp, "MBR::type", mp->type[idx])) 403 return (1); 404 } 405 return (0); 406} 407 408static void 409g_mbrext_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp __unused, struct g_provider *pp) 410{ 411 struct g_mbrext_softc *mp; 412 struct g_slicer *gsp; 413 414 g_slice_dumpconf(sb, indent, gp, cp, pp); 415 gsp = gp->softc; 416 mp = gsp->softc; 417 if (pp != NULL) { 418 if (indent == NULL) 419 sbuf_printf(sb, " ty %d", mp->type[pp->index]); 420 else 421 sbuf_printf(sb, "%s<type>%d</type>\n", indent, 422 mp->type[pp->index]); 423 } 424} 425 426static struct g_geom * 427g_mbrext_taste(struct g_class *mp, struct g_provider *pp, int insist __unused) 428{ 429 struct g_geom *gp; 430 struct g_consumer *cp; 431 int error, i, slice; 432 struct g_mbrext_softc *ms; 433 off_t off; 434 u_char *buf; 435 struct dos_partition dp[4]; 436 u_int fwsectors, sectorsize; 437 438 g_trace(G_T_TOPOLOGY, "g_mbrext_taste(%s,%s)", mp->name, pp->name); 439 g_topology_assert(); 440 if (strcmp(pp->geom->class->name, MBR_CLASS_NAME)) 441 return (NULL); 442 gp = g_slice_new(mp, NDOSEXTPART, pp, &cp, &ms, sizeof *ms, 443 g_mbrext_start); 444 if (gp == NULL) 445 return (NULL); 446 g_topology_unlock(); 447 off = 0; 448 slice = 0; 449 do { 450 error = g_getattr("MBR::type", cp, &i); 451 if (error || (i != DOSPTYP_EXT && i != DOSPTYP_EXTLBA)) 452 break; 453 error = g_getattr("GEOM::fwsectors", cp, &fwsectors); 454 if (error) 455 fwsectors = 17; 456 sectorsize = cp->provider->sectorsize; 457 if (sectorsize != 512) 458 break; 459 for (;;) { 460 buf = g_read_data(cp, off, sectorsize, NULL); 461 if (buf == NULL) 462 break; 463 if (buf[0x1fe] != 0x55 && buf[0x1ff] != 0xaa) { 464 g_free(buf); 465 break; 466 } 467 for (i = 0; i < NDOSPART; i++) 468 dos_partition_dec( 469 buf + DOSPARTOFF + 470 i * sizeof(struct dos_partition), dp + i); 471 g_free(buf); 472 if (0 && bootverbose) { 473 printf("MBREXT Slice %d on %s:\n", 474 slice + 5, gp->name); 475 g_mbr_print(0, dp); 476 g_mbr_print(1, dp + 1); 477 } 478 if ((dp[0].dp_flag & 0x7f) == 0 && 479 dp[0].dp_size != 0 && dp[0].dp_typ != 0) { 480 g_topology_lock(); 481 g_slice_config(gp, slice, G_SLICE_CONFIG_SET, 482 (((off_t)dp[0].dp_start) << 9ULL) + off, 483 ((off_t)dp[0].dp_size) << 9ULL, 484 sectorsize, 485 "%*.*s%d", 486 (int)strlen(gp->name) - 1, 487 (int)strlen(gp->name) - 1, 488 gp->name, 489 slice + 5); 490 g_topology_unlock(); 491 ms->type[slice] = dp[0].dp_typ; 492 slice++; 493 } 494 if (dp[1].dp_flag != 0) 495 break; 496 if (dp[1].dp_typ != DOSPTYP_EXT && 497 dp[1].dp_typ != DOSPTYP_EXTLBA) 498 break; 499 if (dp[1].dp_size == 0) 500 break; 501 off = ((off_t)dp[1].dp_start) << 9ULL; 502 } 503 break; 504 } while (0); 505 g_topology_lock(); 506 g_access(cp, -1, 0, 0); 507 if (LIST_EMPTY(&gp->provider)) { 508 g_slice_spoiled(cp); 509 return (NULL); 510 } 511 return (gp); 512} 513 514 515static struct g_class g_mbrext_class = { 516 .name = MBREXT_CLASS_NAME, 517 .version = G_VERSION, 518 .taste = g_mbrext_taste, 519 .dumpconf = g_mbrext_dumpconf, 520}; 521 522DECLARE_GEOM_CLASS(g_mbrext_class, g_mbrext); 523