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