xref: /freebsd/sys/geom/part/g_part_apm.c (revision 09c999b1557a8031d2b60435d71a0a5ed4f0f016)
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