1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2006-2008 Marcel Moolenaar 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/apm.h> 34 #include <sys/bio.h> 35 #include <sys/endian.h> 36 #include <sys/kernel.h> 37 #include <sys/kobj.h> 38 #include <sys/limits.h> 39 #include <sys/lock.h> 40 #include <sys/malloc.h> 41 #include <sys/mutex.h> 42 #include <sys/queue.h> 43 #include <sys/sbuf.h> 44 #include <sys/systm.h> 45 #include <sys/sysctl.h> 46 #include <geom/geom.h> 47 #include <geom/geom_int.h> 48 #include <geom/part/g_part.h> 49 50 #include "g_part_if.h" 51 52 FEATURE(geom_part_apm, "GEOM partitioning class for Apple-style partitions"); 53 54 struct g_part_apm_table { 55 struct g_part_table base; 56 struct apm_ddr ddr; 57 struct apm_ent self; 58 int tivo_series1; 59 }; 60 61 struct g_part_apm_entry { 62 struct g_part_entry base; 63 struct apm_ent ent; 64 }; 65 66 static int g_part_apm_add(struct g_part_table *, struct g_part_entry *, 67 struct g_part_parms *); 68 static int g_part_apm_create(struct g_part_table *, struct g_part_parms *); 69 static int g_part_apm_destroy(struct g_part_table *, struct g_part_parms *); 70 static void g_part_apm_dumpconf(struct g_part_table *, struct g_part_entry *, 71 struct sbuf *, const char *); 72 static int g_part_apm_dumpto(struct g_part_table *, struct g_part_entry *); 73 static int g_part_apm_modify(struct g_part_table *, struct g_part_entry *, 74 struct g_part_parms *); 75 static const char *g_part_apm_name(struct g_part_table *, struct g_part_entry *, 76 char *, size_t); 77 static int g_part_apm_probe(struct g_part_table *, struct g_consumer *); 78 static int g_part_apm_read(struct g_part_table *, struct g_consumer *); 79 static const char *g_part_apm_type(struct g_part_table *, struct g_part_entry *, 80 char *, size_t); 81 static int g_part_apm_write(struct g_part_table *, struct g_consumer *); 82 static int g_part_apm_resize(struct g_part_table *, struct g_part_entry *, 83 struct g_part_parms *); 84 85 static kobj_method_t g_part_apm_methods[] = { 86 KOBJMETHOD(g_part_add, g_part_apm_add), 87 KOBJMETHOD(g_part_create, g_part_apm_create), 88 KOBJMETHOD(g_part_destroy, g_part_apm_destroy), 89 KOBJMETHOD(g_part_dumpconf, g_part_apm_dumpconf), 90 KOBJMETHOD(g_part_dumpto, g_part_apm_dumpto), 91 KOBJMETHOD(g_part_modify, g_part_apm_modify), 92 KOBJMETHOD(g_part_resize, g_part_apm_resize), 93 KOBJMETHOD(g_part_name, g_part_apm_name), 94 KOBJMETHOD(g_part_probe, g_part_apm_probe), 95 KOBJMETHOD(g_part_read, g_part_apm_read), 96 KOBJMETHOD(g_part_type, g_part_apm_type), 97 KOBJMETHOD(g_part_write, g_part_apm_write), 98 { 0, 0 } 99 }; 100 101 static struct g_part_scheme g_part_apm_scheme = { 102 "APM", 103 g_part_apm_methods, 104 sizeof(struct g_part_apm_table), 105 .gps_entrysz = sizeof(struct g_part_apm_entry), 106 .gps_minent = 16, 107 .gps_maxent = 4096, 108 }; 109 G_PART_SCHEME_DECLARE(g_part_apm); 110 MODULE_VERSION(geom_part_apm, 0); 111 112 static void 113 swab(char *buf, size_t bufsz) 114 { 115 int i; 116 char ch; 117 118 for (i = 0; i < bufsz; i += 2) { 119 ch = buf[i]; 120 buf[i] = buf[i + 1]; 121 buf[i + 1] = ch; 122 } 123 } 124 125 static int 126 apm_parse_type(const char *type, char *buf, size_t bufsz) 127 { 128 const char *alias; 129 130 if (type[0] == '!') { 131 type++; 132 if (strlen(type) > bufsz) 133 return (EINVAL); 134 if (!strcmp(type, APM_ENT_TYPE_SELF) || 135 !strcmp(type, APM_ENT_TYPE_UNUSED)) 136 return (EINVAL); 137 strncpy(buf, type, bufsz); 138 return (0); 139 } 140 alias = g_part_alias_name(G_PART_ALIAS_APPLE_BOOT); 141 if (!strcasecmp(type, alias)) { 142 strcpy(buf, APM_ENT_TYPE_APPLE_BOOT); 143 return (0); 144 } 145 alias = g_part_alias_name(G_PART_ALIAS_APPLE_HFS); 146 if (!strcasecmp(type, alias)) { 147 strcpy(buf, APM_ENT_TYPE_APPLE_HFS); 148 return (0); 149 } 150 alias = g_part_alias_name(G_PART_ALIAS_APPLE_UFS); 151 if (!strcasecmp(type, alias)) { 152 strcpy(buf, APM_ENT_TYPE_APPLE_UFS); 153 return (0); 154 } 155 alias = g_part_alias_name(G_PART_ALIAS_FREEBSD); 156 if (!strcasecmp(type, alias)) { 157 strcpy(buf, APM_ENT_TYPE_FREEBSD); 158 return (0); 159 } 160 alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_NANDFS); 161 if (!strcasecmp(type, alias)) { 162 strcpy(buf, APM_ENT_TYPE_FREEBSD_NANDFS); 163 return (0); 164 } 165 alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP); 166 if (!strcasecmp(type, alias)) { 167 strcpy(buf, APM_ENT_TYPE_FREEBSD_SWAP); 168 return (0); 169 } 170 alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS); 171 if (!strcasecmp(type, alias)) { 172 strcpy(buf, APM_ENT_TYPE_FREEBSD_UFS); 173 return (0); 174 } 175 alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM); 176 if (!strcasecmp(type, alias)) { 177 strcpy(buf, APM_ENT_TYPE_FREEBSD_VINUM); 178 return (0); 179 } 180 alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS); 181 if (!strcasecmp(type, alias)) { 182 strcpy(buf, APM_ENT_TYPE_FREEBSD_ZFS); 183 return (0); 184 } 185 return (EINVAL); 186 } 187 188 static int 189 apm_read_ent(struct g_consumer *cp, uint32_t blk, struct apm_ent *ent, 190 int tivo_series1) 191 { 192 struct g_provider *pp; 193 char *buf; 194 int error; 195 196 pp = cp->provider; 197 buf = g_read_data(cp, pp->sectorsize * blk, pp->sectorsize, &error); 198 if (buf == NULL) 199 return (error); 200 if (tivo_series1) 201 swab(buf, pp->sectorsize); 202 ent->ent_sig = be16dec(buf); 203 ent->ent_pmblkcnt = be32dec(buf + 4); 204 ent->ent_start = be32dec(buf + 8); 205 ent->ent_size = be32dec(buf + 12); 206 bcopy(buf + 16, ent->ent_name, sizeof(ent->ent_name)); 207 bcopy(buf + 48, ent->ent_type, sizeof(ent->ent_type)); 208 g_free(buf); 209 return (0); 210 } 211 212 static int 213 g_part_apm_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 214 struct g_part_parms *gpp) 215 { 216 struct g_part_apm_entry *entry; 217 struct g_part_apm_table *table; 218 int error; 219 220 entry = (struct g_part_apm_entry *)baseentry; 221 table = (struct g_part_apm_table *)basetable; 222 entry->ent.ent_sig = APM_ENT_SIG; 223 entry->ent.ent_pmblkcnt = table->self.ent_pmblkcnt; 224 entry->ent.ent_start = gpp->gpp_start; 225 entry->ent.ent_size = gpp->gpp_size; 226 if (baseentry->gpe_deleted) { 227 bzero(entry->ent.ent_type, sizeof(entry->ent.ent_type)); 228 bzero(entry->ent.ent_name, sizeof(entry->ent.ent_name)); 229 } 230 error = apm_parse_type(gpp->gpp_type, entry->ent.ent_type, 231 sizeof(entry->ent.ent_type)); 232 if (error) 233 return (error); 234 if (gpp->gpp_parms & G_PART_PARM_LABEL) { 235 if (strlen(gpp->gpp_label) > sizeof(entry->ent.ent_name)) 236 return (EINVAL); 237 strncpy(entry->ent.ent_name, gpp->gpp_label, 238 sizeof(entry->ent.ent_name)); 239 } 240 if (baseentry->gpe_index >= table->self.ent_pmblkcnt) 241 table->self.ent_pmblkcnt = baseentry->gpe_index + 1; 242 KASSERT(table->self.ent_size >= table->self.ent_pmblkcnt, 243 ("%s", __func__)); 244 KASSERT(table->self.ent_size > baseentry->gpe_index, 245 ("%s", __func__)); 246 return (0); 247 } 248 249 static int 250 g_part_apm_create(struct g_part_table *basetable, struct g_part_parms *gpp) 251 { 252 struct g_provider *pp; 253 struct g_part_apm_table *table; 254 uint32_t last; 255 256 /* We don't nest, which means that our depth should be 0. */ 257 if (basetable->gpt_depth != 0) 258 return (ENXIO); 259 260 table = (struct g_part_apm_table *)basetable; 261 pp = gpp->gpp_provider; 262 if (pp->sectorsize != 512 || 263 pp->mediasize < (2 + 2 * basetable->gpt_entries) * pp->sectorsize) 264 return (ENOSPC); 265 266 /* APM uses 32-bit LBAs. */ 267 last = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX) - 1; 268 269 basetable->gpt_first = 2 + basetable->gpt_entries; 270 basetable->gpt_last = last; 271 272 table->ddr.ddr_sig = APM_DDR_SIG; 273 table->ddr.ddr_blksize = pp->sectorsize; 274 table->ddr.ddr_blkcount = last + 1; 275 276 table->self.ent_sig = APM_ENT_SIG; 277 table->self.ent_pmblkcnt = basetable->gpt_entries + 1; 278 table->self.ent_start = 1; 279 table->self.ent_size = table->self.ent_pmblkcnt; 280 strcpy(table->self.ent_name, "Apple"); 281 strcpy(table->self.ent_type, APM_ENT_TYPE_SELF); 282 return (0); 283 } 284 285 static int 286 g_part_apm_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 287 { 288 289 /* Wipe the first 2 sectors to clear the partitioning. */ 290 basetable->gpt_smhead |= 3; 291 return (0); 292 } 293 294 static void 295 g_part_apm_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry, 296 struct sbuf *sb, const char *indent) 297 { 298 union { 299 char name[APM_ENT_NAMELEN + 1]; 300 char type[APM_ENT_TYPELEN + 1]; 301 } u; 302 struct g_part_apm_entry *entry; 303 304 entry = (struct g_part_apm_entry *)baseentry; 305 if (indent == NULL) { 306 /* conftxt: libdisk compatibility */ 307 sbuf_printf(sb, " xs APPLE xt %s", entry->ent.ent_type); 308 } else if (entry != NULL) { 309 /* confxml: partition entry information */ 310 strncpy(u.name, entry->ent.ent_name, APM_ENT_NAMELEN); 311 u.name[APM_ENT_NAMELEN] = '\0'; 312 sbuf_printf(sb, "%s<label>", indent); 313 g_conf_printf_escaped(sb, "%s", u.name); 314 sbuf_printf(sb, "</label>\n"); 315 strncpy(u.type, entry->ent.ent_type, APM_ENT_TYPELEN); 316 u.type[APM_ENT_TYPELEN] = '\0'; 317 sbuf_printf(sb, "%s<rawtype>", indent); 318 g_conf_printf_escaped(sb, "%s", u.type); 319 sbuf_printf(sb, "</rawtype>\n"); 320 } else { 321 /* confxml: scheme information */ 322 } 323 } 324 325 static int 326 g_part_apm_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 327 { 328 struct g_part_apm_entry *entry; 329 330 entry = (struct g_part_apm_entry *)baseentry; 331 return ((!strcmp(entry->ent.ent_type, APM_ENT_TYPE_FREEBSD_SWAP)) 332 ? 1 : 0); 333 } 334 335 static int 336 g_part_apm_modify(struct g_part_table *basetable, 337 struct g_part_entry *baseentry, struct g_part_parms *gpp) 338 { 339 struct g_part_apm_entry *entry; 340 int error; 341 342 entry = (struct g_part_apm_entry *)baseentry; 343 if (gpp->gpp_parms & G_PART_PARM_LABEL) { 344 if (strlen(gpp->gpp_label) > sizeof(entry->ent.ent_name)) 345 return (EINVAL); 346 } 347 if (gpp->gpp_parms & G_PART_PARM_TYPE) { 348 error = apm_parse_type(gpp->gpp_type, entry->ent.ent_type, 349 sizeof(entry->ent.ent_type)); 350 if (error) 351 return (error); 352 } 353 if (gpp->gpp_parms & G_PART_PARM_LABEL) { 354 strncpy(entry->ent.ent_name, gpp->gpp_label, 355 sizeof(entry->ent.ent_name)); 356 } 357 return (0); 358 } 359 360 static int 361 g_part_apm_resize(struct g_part_table *basetable, 362 struct g_part_entry *baseentry, struct g_part_parms *gpp) 363 { 364 struct g_part_apm_entry *entry; 365 struct g_provider *pp; 366 367 if (baseentry == NULL) { 368 pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider; 369 basetable->gpt_last = MIN(pp->mediasize / pp->sectorsize, 370 UINT32_MAX) - 1; 371 return (0); 372 } 373 374 entry = (struct g_part_apm_entry *)baseentry; 375 baseentry->gpe_end = baseentry->gpe_start + gpp->gpp_size - 1; 376 entry->ent.ent_size = gpp->gpp_size; 377 378 return (0); 379 } 380 381 static const char * 382 g_part_apm_name(struct g_part_table *table, struct g_part_entry *baseentry, 383 char *buf, size_t bufsz) 384 { 385 386 snprintf(buf, bufsz, "s%d", baseentry->gpe_index + 1); 387 return (buf); 388 } 389 390 static int 391 g_part_apm_probe(struct g_part_table *basetable, struct g_consumer *cp) 392 { 393 struct g_provider *pp; 394 struct g_part_apm_table *table; 395 char *buf; 396 int error; 397 398 /* We don't nest, which means that our depth should be 0. */ 399 if (basetable->gpt_depth != 0) 400 return (ENXIO); 401 402 table = (struct g_part_apm_table *)basetable; 403 table->tivo_series1 = 0; 404 pp = cp->provider; 405 406 /* Sanity-check the provider. */ 407 if (pp->mediasize < 4 * pp->sectorsize) 408 return (ENOSPC); 409 410 /* Check that there's a Driver Descriptor Record (DDR). */ 411 buf = g_read_data(cp, 0L, pp->sectorsize, &error); 412 if (buf == NULL) 413 return (error); 414 if (be16dec(buf) == APM_DDR_SIG) { 415 /* Normal Apple DDR */ 416 table->ddr.ddr_sig = be16dec(buf); 417 table->ddr.ddr_blksize = be16dec(buf + 2); 418 table->ddr.ddr_blkcount = be32dec(buf + 4); 419 g_free(buf); 420 if (table->ddr.ddr_blksize != pp->sectorsize) 421 return (ENXIO); 422 if (table->ddr.ddr_blkcount > pp->mediasize / pp->sectorsize) 423 return (ENXIO); 424 } else { 425 /* 426 * Check for Tivo drives, which have no DDR and a different 427 * signature. Those whose first two bytes are 14 92 are 428 * Series 2 drives, and aren't supported. Those that start 429 * with 92 14 are series 1 drives and are supported. 430 */ 431 if (be16dec(buf) != 0x9214) { 432 /* If this is 0x1492 it could be a series 2 drive */ 433 g_free(buf); 434 return (ENXIO); 435 } 436 table->ddr.ddr_sig = APM_DDR_SIG; /* XXX */ 437 table->ddr.ddr_blksize = pp->sectorsize; /* XXX */ 438 table->ddr.ddr_blkcount = 439 MIN(pp->mediasize / pp->sectorsize, UINT32_MAX); 440 table->tivo_series1 = 1; 441 g_free(buf); 442 } 443 444 /* Check that there's a Partition Map. */ 445 error = apm_read_ent(cp, 1, &table->self, table->tivo_series1); 446 if (error) 447 return (error); 448 if (table->self.ent_sig != APM_ENT_SIG) 449 return (ENXIO); 450 if (strcmp(table->self.ent_type, APM_ENT_TYPE_SELF)) 451 return (ENXIO); 452 if (table->self.ent_pmblkcnt >= table->ddr.ddr_blkcount) 453 return (ENXIO); 454 return (G_PART_PROBE_PRI_NORM); 455 } 456 457 static int 458 g_part_apm_read(struct g_part_table *basetable, struct g_consumer *cp) 459 { 460 struct apm_ent ent; 461 struct g_part_apm_entry *entry; 462 struct g_part_apm_table *table; 463 int error, index; 464 465 table = (struct g_part_apm_table *)basetable; 466 467 basetable->gpt_first = table->self.ent_size + 1; 468 basetable->gpt_last = table->ddr.ddr_blkcount - 1; 469 basetable->gpt_entries = table->self.ent_size - 1; 470 471 for (index = table->self.ent_pmblkcnt - 1; index > 0; index--) { 472 error = apm_read_ent(cp, index + 1, &ent, table->tivo_series1); 473 if (error) 474 continue; 475 if (!strcmp(ent.ent_type, APM_ENT_TYPE_UNUSED)) 476 continue; 477 entry = (struct g_part_apm_entry *)g_part_new_entry(basetable, 478 index, ent.ent_start, ent.ent_start + ent.ent_size - 1); 479 entry->ent = ent; 480 } 481 482 return (0); 483 } 484 485 static const char * 486 g_part_apm_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 487 char *buf, size_t bufsz) 488 { 489 struct g_part_apm_entry *entry; 490 const char *type; 491 size_t len; 492 493 entry = (struct g_part_apm_entry *)baseentry; 494 type = entry->ent.ent_type; 495 if (!strcmp(type, APM_ENT_TYPE_APPLE_BOOT)) 496 return (g_part_alias_name(G_PART_ALIAS_APPLE_BOOT)); 497 if (!strcmp(type, APM_ENT_TYPE_APPLE_HFS)) 498 return (g_part_alias_name(G_PART_ALIAS_APPLE_HFS)); 499 if (!strcmp(type, APM_ENT_TYPE_APPLE_UFS)) 500 return (g_part_alias_name(G_PART_ALIAS_APPLE_UFS)); 501 if (!strcmp(type, APM_ENT_TYPE_FREEBSD)) 502 return (g_part_alias_name(G_PART_ALIAS_FREEBSD)); 503 if (!strcmp(type, APM_ENT_TYPE_FREEBSD_NANDFS)) 504 return (g_part_alias_name(G_PART_ALIAS_FREEBSD_NANDFS)); 505 if (!strcmp(type, APM_ENT_TYPE_FREEBSD_SWAP)) 506 return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP)); 507 if (!strcmp(type, APM_ENT_TYPE_FREEBSD_UFS)) 508 return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS)); 509 if (!strcmp(type, APM_ENT_TYPE_FREEBSD_VINUM)) 510 return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM)); 511 if (!strcmp(type, APM_ENT_TYPE_FREEBSD_ZFS)) 512 return (g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS)); 513 buf[0] = '!'; 514 len = MIN(sizeof(entry->ent.ent_type), bufsz - 2); 515 bcopy(type, buf + 1, len); 516 buf[len + 1] = '\0'; 517 return (buf); 518 } 519 520 static int 521 g_part_apm_write(struct g_part_table *basetable, struct g_consumer *cp) 522 { 523 struct g_provider *pp; 524 struct g_part_entry *baseentry; 525 struct g_part_apm_entry *entry; 526 struct g_part_apm_table *table; 527 char *buf, *ptr; 528 uint32_t index; 529 int error; 530 size_t tblsz; 531 532 pp = cp->provider; 533 table = (struct g_part_apm_table *)basetable; 534 /* 535 * Tivo Series 1 disk partitions are currently read-only. 536 */ 537 if (table->tivo_series1) 538 return (EOPNOTSUPP); 539 540 /* Write the DDR only when we're newly created. */ 541 if (basetable->gpt_created) { 542 buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 543 be16enc(buf, table->ddr.ddr_sig); 544 be16enc(buf + 2, table->ddr.ddr_blksize); 545 be32enc(buf + 4, table->ddr.ddr_blkcount); 546 error = g_write_data(cp, 0, buf, pp->sectorsize); 547 g_free(buf); 548 if (error) 549 return (error); 550 } 551 552 /* Allocate the buffer for all entries */ 553 tblsz = table->self.ent_pmblkcnt; 554 buf = g_malloc(tblsz * pp->sectorsize, M_WAITOK | M_ZERO); 555 556 /* Fill the self entry */ 557 be16enc(buf, APM_ENT_SIG); 558 be32enc(buf + 4, table->self.ent_pmblkcnt); 559 be32enc(buf + 8, table->self.ent_start); 560 be32enc(buf + 12, table->self.ent_size); 561 bcopy(table->self.ent_name, buf + 16, sizeof(table->self.ent_name)); 562 bcopy(table->self.ent_type, buf + 48, sizeof(table->self.ent_type)); 563 564 baseentry = LIST_FIRST(&basetable->gpt_entry); 565 for (index = 1; index < tblsz; index++) { 566 entry = (baseentry != NULL && index == baseentry->gpe_index) 567 ? (struct g_part_apm_entry *)baseentry : NULL; 568 ptr = buf + index * pp->sectorsize; 569 be16enc(ptr, APM_ENT_SIG); 570 be32enc(ptr + 4, table->self.ent_pmblkcnt); 571 if (entry != NULL && !baseentry->gpe_deleted) { 572 be32enc(ptr + 8, entry->ent.ent_start); 573 be32enc(ptr + 12, entry->ent.ent_size); 574 bcopy(entry->ent.ent_name, ptr + 16, 575 sizeof(entry->ent.ent_name)); 576 bcopy(entry->ent.ent_type, ptr + 48, 577 sizeof(entry->ent.ent_type)); 578 } else { 579 strcpy(ptr + 48, APM_ENT_TYPE_UNUSED); 580 } 581 if (entry != NULL) 582 baseentry = LIST_NEXT(baseentry, gpe_entry); 583 } 584 585 for (index = 0; index < tblsz; index += MAXPHYS / pp->sectorsize) { 586 error = g_write_data(cp, (1 + index) * pp->sectorsize, 587 buf + index * pp->sectorsize, 588 (tblsz - index > MAXPHYS / pp->sectorsize) ? MAXPHYS: 589 (tblsz - index) * pp->sectorsize); 590 if (error) { 591 g_free(buf); 592 return (error); 593 } 594 } 595 g_free(buf); 596 return (0); 597 } 598