disklabel.c revision 1.49
1/*	$NetBSD: disklabel.c,v 1.49 2022/06/24 22:28:11 tsutsui Exp $	*/
2
3/*
4 * Copyright 2018 The NetBSD Foundation, Inc.
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 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30#include "defs.h"
31#include "md.h"
32#include <assert.h>
33#include <util.h>
34#include <paths.h>
35#include <sys/ioctl.h>
36#include <sys/param.h>
37
38const struct disk_partitioning_scheme disklabel_parts;
39
40/*************** disklabel ******************************************/
41/* a disklabel based disk_partitions interface */
42struct disklabel_disk_partitions {
43	struct disk_partitions dp;
44	struct disklabel l;
45	daddr_t ptn_alignment, install_target;
46	char last_mounted[MAXPARTITIONS][MOUNTLEN];
47	uint fs_sub_type[MAXPARTITIONS], fs_opt3[MAXPARTITIONS];
48};
49
50/*
51 * Maximum number of disklabel partitions the current kernel supports
52 */
53size_t dl_maxpart;
54
55/* index into this array is the type code */
56static struct part_type_desc dl_types[__arraycount(fstypenames)-1];
57
58struct dl_custom_ptype {
59	unsigned int type;
60	char short_desc[6], description[30];
61	struct part_type_desc desc;
62};
63struct dl_custom_ptype * dl_custom_ptypes;
64size_t dl_custom_ptype_count;
65
66static uint8_t dl_part_type_from_generic(const struct part_type_desc*);
67
68static void
69disklabel_init_default_alignment(struct disklabel_disk_partitions *parts,
70    uint track)
71{
72	if (track == 0)
73		track = MEG / parts->dp.bytes_per_sector;
74
75	if (dl_maxpart == 0)
76		dl_maxpart = getmaxpartitions();
77
78#ifdef MD_DISKLABEL_SET_ALIGN_PRE
79	if (MD_DISKLABEL_SET_ALIGN_PRE(parts->ptn_alignment, track))
80		return;
81#endif
82	/* Use 1MB alignemnt for large (>128GB) disks */
83	if (parts->dp.disk_size > HUGE_DISK_SIZE) {
84		parts->ptn_alignment = 2048;
85	} else if (parts->dp.disk_size > TINY_DISK_SIZE ||
86	    parts->dp.bytes_per_sector > 512) {
87		parts->ptn_alignment = 64;
88	} else {
89		parts->ptn_alignment = 1;
90	}
91#ifdef MD_DISKLABEL_SET_ALIGN_POST
92	MD_DISKLABEL_SET_ALIGN_POST(parts->ptn_alignment, track);
93#endif
94}
95
96static bool
97disklabel_change_geom(struct disk_partitions *arg, int ncyl, int nhead,
98    int nsec)
99{
100	struct disklabel_disk_partitions *parts =
101	    (struct disklabel_disk_partitions*)arg;
102
103	assert(parts->l.d_secsize != 0);
104	assert(parts->l.d_nsectors != 0);
105	assert(parts->l.d_ntracks != 0);
106	assert(parts->l.d_ncylinders != 0);
107	assert(parts->l.d_secpercyl != 0);
108
109	disklabel_init_default_alignment(parts, nhead * nsec);
110	if (ncyl*nhead*nsec <= TINY_DISK_SIZE)
111		set_default_sizemult(arg->disk,
112		    arg->bytes_per_sector, arg->bytes_per_sector);
113	else
114		set_default_sizemult(arg->disk, MEG,
115		    arg->bytes_per_sector);
116
117	return true;
118}
119
120static size_t
121disklabel_cylinder_size(const struct disk_partitions *arg)
122{
123	const struct disklabel_disk_partitions *parts =
124	    (const struct disklabel_disk_partitions*)arg;
125
126	return parts->l.d_secpercyl;
127}
128
129#ifdef NO_DISKLABEL_BOOT
130static bool
131disklabel_non_bootable(const char *disk)
132{
133
134	return false;
135}
136#endif
137
138static struct disk_partitions *
139disklabel_parts_new(const char *dev, daddr_t start, daddr_t len,
140    bool is_boot_drive, struct disk_partitions *parent)
141{
142	struct disklabel_disk_partitions *parts;
143	struct disk_geom geo;
144	daddr_t total_size;
145
146	if (!get_disk_geom(dev, &geo))
147		return NULL;
148
149	parts = calloc(1, sizeof(*parts));
150	if (parts == NULL)
151		return NULL;
152
153	parts->install_target = -1;
154	total_size = geo.dg_secperunit;
155	if (len*(geo.dg_secsize/512) > disklabel_parts.size_limit)
156		len = disklabel_parts.size_limit/(geo.dg_secsize/512);
157	if (total_size*(geo.dg_secsize/512) > disklabel_parts.size_limit)
158		total_size = disklabel_parts.size_limit/(geo.dg_secsize/512);
159
160	parts->l.d_ncylinders = geo.dg_ncylinders;
161	parts->l.d_ntracks = geo.dg_ntracks;
162	parts->l.d_nsectors = geo.dg_nsectors;
163	parts->l.d_secsize = geo.dg_secsize;
164	parts->l.d_secpercyl = geo.dg_nsectors * geo.dg_ntracks;
165
166	parts->dp.pscheme = &disklabel_parts;
167	parts->dp.disk = strdup(dev);
168	parts->dp.disk_start = start;
169	parts->dp.disk_size = parts->dp.free_space = len;
170	parts->dp.bytes_per_sector = parts->l.d_secsize;
171	disklabel_init_default_alignment(parts, parts->l.d_secpercyl);
172	parts->dp.parent = parent;
173
174	strncpy(parts->l.d_packname, "fictious", sizeof parts->l.d_packname);
175
176#if RAW_PART == 3
177	if (parts->dp.parent != NULL) {
178		parts->l.d_partitions[RAW_PART-1].p_fstype = FS_UNUSED;
179		parts->l.d_partitions[RAW_PART-1].p_offset = start;
180		parts->l.d_partitions[RAW_PART-1].p_size = len;
181		parts->dp.num_part++;
182	}
183#endif
184	parts->l.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
185	parts->l.d_partitions[RAW_PART].p_offset = 0;
186	parts->l.d_partitions[RAW_PART].p_size = total_size;
187	parts->dp.num_part++;
188
189	parts->l.d_npartitions = RAW_PART+1;
190
191	return &parts->dp;
192}
193
194static struct disk_partitions *
195disklabel_parts_read(const char *disk, daddr_t start, daddr_t len, size_t bps,
196    const struct disk_partitioning_scheme *scheme)
197{
198	int fd;
199	char diskpath[MAXPATHLEN];
200	uint flags;
201	bool have_own_label = false;
202
203	/* read partitions */
204
205	struct disklabel_disk_partitions *parts = calloc(1, sizeof(*parts));
206	if (parts == NULL)
207		return NULL;
208	parts->install_target = -1;
209
210	fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
211	if (fd == -1) {
212		free(parts);
213		return NULL;
214	}
215
216	/*
217	 * We should actually try to read the label inside the start/len
218	 * boundary, but for simplicity just rely on the kernel and
219	 * instead verify a FS_UNUSED partition at RAW_PART-1 (if
220	 * RAW_PART > 'c') is within the given limits.
221	 */
222	if (ioctl(fd, DIOCGDINFO, &parts->l) < 0) {
223		free(parts);
224		close(fd);
225		return NULL;
226	}
227#if RAW_PART == 3
228	if (parts->l.d_partitions[RAW_PART-1].p_fstype == FS_UNUSED) {
229		daddr_t dlstart = parts->l.d_partitions[RAW_PART-1].p_offset;
230		daddr_t dlend = start +
231		    parts->l.d_partitions[RAW_PART-1].p_size;
232
233		if (dlstart < start || dlend > (start+len)) {
234			/*
235			 * Kernel assumes different outer partition
236			 * (probably not yet written back to disk)
237			 * so this label is invalid.
238			 */
239			free(parts);
240			close(fd);
241			return NULL;
242		}
243	}
244#endif
245
246	if (len > disklabel_parts.size_limit)
247		len = disklabel_parts.size_limit;
248	parts->dp.pscheme = scheme;
249	parts->dp.disk = strdup(disk);
250	parts->dp.disk_start = start;
251	parts->dp.disk_size = parts->dp.free_space = len;
252	parts->l.d_secsize = bps;
253	parts->dp.bytes_per_sector = bps;
254	disklabel_init_default_alignment(parts, parts->l.d_secpercyl);
255
256	for (int part = 0; part < parts->l.d_npartitions; part++) {
257		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
258		    && parts->l.d_partitions[part].p_size == 0)
259			continue;
260
261		parts->dp.num_part++;
262		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED)
263			continue;
264
265		flags = 0;
266		if (parts->l.d_partitions[part].p_fstype == FS_MSDOS)
267			flags = GLM_MAYBE_FAT32;
268		else if (parts->l.d_partitions[part].p_fstype == FS_BSDFFS) {
269			flags = GLM_LIKELY_FFS;
270			if (parts->install_target < 0)
271				parts->install_target =
272				    parts->l.d_partitions[part].p_offset;
273		}
274		if (flags != 0) {
275			uint fs_type, fs_sub_type;
276			const char *lm = get_last_mounted(fd,
277			    parts->l.d_partitions[part].p_offset,
278			    &fs_type, &fs_sub_type, flags);
279			if (lm != NULL && *lm != 0) {
280				strlcpy(parts->last_mounted[part], lm,
281				    sizeof(parts->last_mounted[part]));
282				if (parts->l.d_partitions[part].p_fstype ==
283				    fs_type)
284					parts->fs_sub_type[part] = fs_sub_type;
285				canonicalize_last_mounted(
286				    parts->last_mounted[part]);
287			}
288		}
289
290		if (parts->l.d_partitions[part].p_size > parts->dp.free_space)
291			parts->dp.free_space = 0;
292		else
293			parts->dp.free_space -=
294			    parts->l.d_partitions[part].p_size;
295	}
296	close(fd);
297
298	/*
299	 * Verify we really have a disklabel on the target disk.
300	 */
301	if (run_program(RUN_SILENT | RUN_ERROR_OK,
302	    "disklabel -r %s", disk) == 0) {
303		have_own_label = true;
304	}
305#ifdef DISKLABEL_NO_ONDISK_VERIFY
306	else {
307		/*
308		 * disklabel(8) with -r checks a native disklabel at
309		 * LABELOFFSET sector, but several ports don't have
310		 * a native label and use emulated one translated from
311		 * port specific MD disk partition information.
312		 * Unfortunately, there is no MI way to check whether
313		 * the disk has a native BSD disklabel by readdisklabel(9)
314		 * via DIOCGDINFO.  So check if returned label looks
315		 * defaults set by readdisklabel(9) per MD way.
316		 */
317		have_own_label = !md_disklabel_is_default(&parts->l);
318	}
319#endif
320
321	if (!have_own_label) {
322		bool found_real_part = false;
323
324		if (parts->l.d_npartitions <= RAW_PART ||
325		    parts->l.d_partitions[RAW_PART].p_size == 0)
326			goto no_valid_label;
327
328		/*
329		 * Check if kernel translation gave us "something" besides
330		 * the raw or the whole-disk partition.
331		 * If not: report missing disklabel.
332		 */
333		for (int part = 0; part < parts->l.d_npartitions; part++) {
334			if (parts->l.d_partitions[part].p_fstype == FS_UNUSED)
335				continue;
336			if (/* part == 0 && */	/* PR kern/54882 */
337			    parts->l.d_partitions[part].p_offset ==
338			     parts->l.d_partitions[RAW_PART].p_offset &&
339			    parts->l.d_partitions[part].p_size ==
340			     parts->l.d_partitions[RAW_PART].p_size)
341				continue;
342			if (part == RAW_PART)
343				continue;
344			found_real_part = true;
345			break;
346		}
347		if (!found_real_part) {
348			/* no partition there yet */
349no_valid_label:
350			free(parts);
351			return NULL;
352		}
353	}
354
355	return &parts->dp;
356}
357
358/*
359 * Escape a string for usage as a tag name in a capfile(5),
360 * we really know there is enough space in the destination buffer...
361 */
362static void
363escape_capfile(char *dest, const char *src, size_t len)
364{
365	while (*src && len > 0) {
366		if (*src == ':')
367			*dest++ = ' ';
368		else
369			*dest++ = *src;
370		src++;
371		len--;
372	}
373	*dest = 0;
374}
375
376static bool
377disklabel_write_to_disk(struct disk_partitions *arg)
378{
379	struct disklabel_disk_partitions *parts =
380	    (struct disklabel_disk_partitions*)arg;
381	FILE *f;
382	char fname[PATH_MAX], packname[sizeof(parts->l.d_packname)+1],
383	    disktype[sizeof(parts->l.d_typename)+1];
384	int i, rv = 0;
385	const char *disk = parts->dp.disk, *s;
386	const struct partition *lp;
387	char *d;
388	size_t n;
389
390	assert(parts->l.d_secsize != 0);
391	assert(parts->l.d_nsectors != 0);
392	assert(parts->l.d_ntracks != 0);
393	assert(parts->l.d_ncylinders != 0);
394	assert(parts->l.d_secpercyl != 0);
395
396	/* make sure we have a 0 terminated packname */
397	strlcpy(packname, parts->l.d_packname, sizeof packname);
398	if (packname[0] == 0)
399		strcpy(packname, "fictious");
400
401	/* fill typename with disk name prefix, if not already set */
402	if (strlen(parts->l.d_typename) == 0) {
403		for (n = 0, d = parts->l.d_typename, s = disk;
404		    *s && n < sizeof(parts->l.d_typename); d++, s++, n++) {
405			if (isdigit((unsigned char)*s))
406				break;
407			*d = *s;
408		}
409	}
410
411	/* we need a valid disk type name, so enforce an arbitrary if
412	 * above did not yield a usable one */
413	if (strlen(parts->l.d_typename) == 0)
414		strncpy(parts->l.d_typename, "SCSI",
415		    sizeof(parts->l.d_typename));
416	escape_capfile(disktype, parts->l.d_typename,
417	    sizeof(parts->l.d_typename));
418
419	sprintf(fname, "/tmp/disklabel.%u", getpid());
420	f = fopen(fname, "w");
421	if (f == NULL)
422		return false;
423
424	lp = parts->l.d_partitions;
425	scripting_fprintf(NULL, "cat <<EOF >%s\n", fname);
426	scripting_fprintf(f, "%s|NetBSD installation generated:\\\n",
427	    disktype);
428	scripting_fprintf(f, "\t:nc#%d:nt#%d:ns#%d:\\\n",
429	    parts->l.d_ncylinders, parts->l.d_ntracks, parts->l.d_nsectors);
430	scripting_fprintf(f, "\t:sc#%d:su#%" PRIu32 ":\\\n",
431	    parts->l.d_secpercyl, lp[RAW_PART].p_offset+lp[RAW_PART].p_size);
432	scripting_fprintf(f, "\t:se#%d:\\\n", parts->l.d_secsize);
433
434	for (i = 0; i < parts->l.d_npartitions; i++) {
435		scripting_fprintf(f, "\t:p%c#%" PRIu32 ":o%c#%" PRIu32
436		    ":t%c=%s:", 'a'+i, (uint32_t)lp[i].p_size,
437		    'a'+i, (uint32_t)lp[i].p_offset, 'a'+i,
438		    getfslabelname(lp[i].p_fstype, 0));
439		if (lp[i].p_fstype == FS_BSDLFS ||
440		    lp[i].p_fstype == FS_BSDFFS)
441			scripting_fprintf (f, "b%c#%" PRIu32 ":f%c#%" PRIu32
442			    ":", 'a'+i,
443			    (uint32_t)(lp[i].p_fsize *
444			    lp[i].p_frag),
445			    'a'+i, (uint32_t)lp[i].p_fsize);
446
447		if (i < parts->l.d_npartitions - 1)
448			scripting_fprintf(f, "\\\n");
449		else
450			scripting_fprintf(f, "\n");
451	}
452	scripting_fprintf(NULL, "EOF\n");
453
454	fclose(f);
455
456	/*
457	 * Label a disk using an MD-specific string DISKLABEL_CMD for
458	 * to invoke disklabel.
459	 * if MD code does not define DISKLABEL_CMD, this is a no-op.
460	 *
461	 * i386 port uses "/sbin/disklabel -w -r", just like i386
462	 * miniroot scripts, though this may leave a bogus incore label.
463	 *
464	 * Sun ports should use DISKLABEL_CMD "/sbin/disklabel -w"
465	 * to get incore to ondisk inode translation for the Sun proms.
466	 */
467#ifdef DISKLABEL_CMD
468	/* disklabel the disk */
469	rv = run_program(0, "%s -f %s %s '%s' '%s'",
470	    DISKLABEL_CMD, fname, disk, disktype, packname);
471#endif
472
473	unlink(fname);
474
475	return rv == 0;
476}
477
478static bool
479disklabel_delete_all(struct disk_partitions *arg)
480{
481	struct disklabel_disk_partitions *parts =
482	    (struct disklabel_disk_partitions*)arg;
483	daddr_t total_size = parts->l.d_partitions[RAW_PART].p_size;
484
485	memset(&parts->l.d_partitions, 0, sizeof(parts->l.d_partitions));
486	parts->dp.num_part = 0;
487
488#if RAW_PART == 3
489	if (parts->dp.parent != NULL) {
490		parts->l.d_partitions[RAW_PART-1].p_fstype = FS_UNUSED;
491		parts->l.d_partitions[RAW_PART-1].p_offset =
492		    parts->dp.disk_start;
493		parts->l.d_partitions[RAW_PART-1].p_size = parts->dp.disk_size;
494		parts->dp.num_part++;
495	}
496#endif
497	parts->l.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
498	parts->l.d_partitions[RAW_PART].p_offset = 0;
499	parts->l.d_partitions[RAW_PART].p_size = total_size;
500	parts->dp.num_part++;
501
502	parts->l.d_npartitions = RAW_PART+1;
503	return true;
504}
505
506static bool
507disklabel_delete(struct disk_partitions *arg, part_id id,
508    const char **err_msg)
509{
510	struct disklabel_disk_partitions *parts =
511	    (struct disklabel_disk_partitions*)arg;
512	part_id ndx;
513
514	ndx = 0;
515	for (int part = 0; part < parts->l.d_npartitions; part++) {
516		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
517		    && parts->l.d_partitions[part].p_size == 0)
518			continue;
519
520		if (ndx == id) {
521			if (part == RAW_PART
522#if RAW_PART == 3
523				|| (part == RAW_PART-1 &&
524				    parts->dp.parent != NULL)
525#endif
526						) {
527				if (err_msg)
528					*err_msg = msg_string(
529					    MSG_part_not_deletable);
530				return false;
531			}
532			if (parts->install_target ==
533			    parts->l.d_partitions[part].p_offset)
534				parts->install_target = -1;
535			parts->dp.free_space +=
536			    parts->l.d_partitions[part].p_size;
537			parts->l.d_partitions[part].p_size = 0;
538			parts->l.d_partitions[part].p_offset = 0;
539			parts->l.d_partitions[part].p_fstype = FS_UNUSED;
540			parts->dp.num_part--;
541			return true;
542		}
543		ndx++;
544	}
545
546	if (err_msg)
547		*err_msg = INTERNAL_ERROR;
548	return false;
549}
550
551static bool
552disklabel_delete_range(struct disk_partitions *arg, daddr_t r_start,
553    daddr_t r_size)
554{
555	struct disklabel_disk_partitions *parts =
556	    (struct disklabel_disk_partitions*)arg;
557
558	for (int part = 0; part < parts->l.d_npartitions; part++) {
559		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
560		    && parts->l.d_partitions[part].p_size == 0)
561			continue;
562
563		if (part == RAW_PART)
564			continue;
565
566		daddr_t start = parts->l.d_partitions[part].p_offset;
567		daddr_t end = start + parts->l.d_partitions[part].p_size;
568
569#if RAW_PART == 3
570		if (parts->dp.parent != NULL &&
571		    part == RAW_PART - 1 && start == r_start &&
572		    r_start + r_size == end)
573			continue;
574#endif
575
576		if ((start >= r_start && start <= r_start+r_size) ||
577		    (end >= r_start && end <= r_start+r_size)) {
578			if (start == parts->install_target)
579				parts->install_target  = -1;
580			if (parts->dp.num_part > 1)
581				parts->dp.num_part--;
582			parts->dp.free_space +=
583			    parts->l.d_partitions[part].p_size;
584			parts->l.d_partitions[part].p_fstype = FS_UNUSED;
585			parts->l.d_partitions[part].p_size = 0;
586		}
587	}
588
589	return true;
590}
591
592static void
593dl_init_types(void)
594{
595	for (size_t i = 0; i < __arraycount(dl_types); i++) {
596		if (fstypenames[i] == NULL)
597			break;
598		dl_types[i].short_desc =
599		dl_types[i].description = getfslabelname(i, 0);
600		enum part_type pt;
601		switch (i) {
602		case FS_UNUSED:	pt = PT_undef; break;
603		case FS_BSDFFS:
604		case FS_RAID:
605		case FS_BSDLFS:
606		case FS_CGD:
607				pt = PT_root; break;
608		case FS_SWAP:	pt = PT_swap; break;
609		case FS_MSDOS:	pt = PT_FAT; break;
610		case FS_EX2FS:	pt = PT_EXT2; break;
611		case FS_SYSVBFS:
612				pt = PT_SYSVBFS; break;
613		default:	pt = PT_unknown; break;
614		}
615		dl_types[i].generic_ptype = pt;
616	}
617}
618
619static uint8_t
620dl_part_type_from_generic(const struct part_type_desc *gent)
621{
622
623	if (dl_types[0].description == NULL)
624		dl_init_types();
625	for (size_t i = 0; i < __arraycount(dl_types); i++)
626		if (gent == &dl_types[i])
627			return (uint8_t)i;
628
629	for (size_t i = 0; i < dl_custom_ptype_count; i++)
630		if (gent == &dl_custom_ptypes[i].desc)
631			return dl_custom_ptypes[i].type;
632
633	return 0;
634}
635
636static size_t
637disklabel_type_count(void)
638{
639	return __arraycount(dl_types) + dl_custom_ptype_count;
640}
641
642static const struct part_type_desc *
643disklabel_get_type(size_t ndx)
644{
645	if (dl_types[0].description == NULL)
646		dl_init_types();
647
648	if (ndx < __arraycount(dl_types))
649		return &dl_types[ndx];
650
651	ndx -= __arraycount(dl_types);
652	if (ndx >= dl_custom_ptype_count)
653		return NULL;
654
655	return &dl_custom_ptypes[ndx].desc;
656}
657
658static const struct part_type_desc *
659disklabel_find_type(uint type, bool create_if_unknown)
660{
661	if (dl_types[0].description == NULL)
662		dl_init_types();
663
664	if (type < __arraycount(dl_types))
665		return &dl_types[type];
666
667	for (size_t i = 0; i < dl_custom_ptype_count; i++)
668		if (dl_custom_ptypes[i].type == type)
669			return &dl_custom_ptypes[i].desc;
670
671	if (create_if_unknown) {
672		struct dl_custom_ptype *nt;
673
674		nt = realloc(dl_custom_ptypes, dl_custom_ptype_count+1);
675		if (nt == NULL)
676			return NULL;
677		dl_custom_ptypes = nt;
678		nt = dl_custom_ptypes + dl_custom_ptype_count;
679		dl_custom_ptype_count++;
680		memset(nt, 0, sizeof(*nt));
681		nt->type = type;
682		snprintf(nt->short_desc, sizeof(nt->short_desc), "%u", type);
683		nt->short_desc[sizeof(nt->short_desc)-1] = 0;
684		snprintf(nt->description, sizeof(nt->description),
685		    "%s (%u)", msg_string(MSG_custom_type), type);
686		nt->description[sizeof(nt->description)-1] = 0;
687		nt->desc.generic_ptype = PT_unknown;
688		nt->desc.short_desc = nt->short_desc;
689		nt->desc.description = nt->description;
690		return &nt->desc;
691	}
692
693	return NULL;
694}
695
696static const struct part_type_desc *
697disklabel_create_custom_part_type(const char *custom, const char **err_msg)
698{
699	char *endp;
700	unsigned long fstype;
701
702	fstype = strtoul(custom, &endp, 10);
703	if (*endp != 0) {
704		if (err_msg)
705			*err_msg = msg_string(MSG_dl_type_invalid);
706		return NULL;
707	}
708
709	return disklabel_find_type(fstype, true);
710}
711
712static const struct part_type_desc *
713disklabel_get_fs_part_type(enum part_type pt, unsigned fstype, unsigned subtype)
714{
715	return disklabel_find_type(fstype, false);
716}
717
718static const struct part_type_desc *
719disklabel_create_unknown_part_type(void)
720{
721	return disklabel_find_type(FS_OTHER, false);
722}
723
724static const struct part_type_desc *
725disklabel_get_generic_type(enum part_type pt)
726{
727	size_t nt;
728
729	if (dl_types[0].description == NULL)
730		dl_init_types();
731
732	switch (pt) {
733	case PT_root:	nt = FS_BSDFFS; break;
734	case PT_swap:	nt = FS_SWAP; break;
735	case PT_FAT:
736	case PT_EFI_SYSTEM:
737			nt = FS_MSDOS; break;
738	case PT_EXT2:	nt = FS_EX2FS; break;
739	case PT_SYSVBFS:
740			nt = FS_SYSVBFS; break;
741	default:	nt = FS_UNUSED; break;
742	}
743
744	return disklabel_get_type(nt);
745}
746
747static bool
748disklabel_get_default_fstype(const struct part_type_desc *nat_type,
749    unsigned *fstype, unsigned *fs_sub_type)
750{
751
752	*fstype = dl_part_type_from_generic(nat_type);
753#ifdef DEFAULT_UFS2
754        if (*fstype == FS_BSDFFS)
755                *fs_sub_type = 2;
756        else
757#endif
758                *fs_sub_type = 0;
759        return true;
760}
761
762static bool
763disklabel_get_part_info(const struct disk_partitions *arg, part_id id,
764    struct disk_part_info *info)
765{
766	const struct disklabel_disk_partitions *parts =
767	    (const struct disklabel_disk_partitions*)arg;
768	part_id ndx;
769
770	if (dl_types[0].description == NULL)
771		dl_init_types();
772
773	ndx = 0;
774	for (int part = 0; part < parts->l.d_npartitions; part++) {
775		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
776		    && parts->l.d_partitions[part].p_size == 0)
777			continue;
778
779		if (ndx == id) {
780			memset(info, 0, sizeof(*info));
781			info->start = parts->l.d_partitions[part].p_offset;
782			info->size = parts->l.d_partitions[part].p_size;
783			info->nat_type = disklabel_find_type(
784			    parts->l.d_partitions[part].p_fstype, true);
785			if (parts->last_mounted[part][0] != 0)
786				info->last_mounted = parts->last_mounted[part];
787			info->fs_type = parts->l.d_partitions[part].p_fstype;
788			info->fs_sub_type = parts->fs_sub_type[part];
789			info->fs_opt2 = parts->l.d_partitions[part].p_fsize;
790			info->fs_opt1 = info->fs_opt2 *
791			    parts->l.d_partitions[part].p_frag;
792			info->fs_opt3 = parts->fs_opt3[part];
793			if (part == RAW_PART &&
794			    parts->l.d_partitions[part].p_fstype == FS_UNUSED)
795				info->flags |=
796				    PTI_PSCHEME_INTERNAL|PTI_RAW_PART;
797			if (info->start == parts->install_target &&
798			    parts->l.d_partitions[part].p_fstype != FS_UNUSED)
799				info->flags |= PTI_INSTALL_TARGET;
800#if RAW_PART == 3
801			if (part == (RAW_PART-1) && parts->dp.parent != NULL &&
802			    parts->l.d_partitions[part].p_fstype == FS_UNUSED)
803				info->flags |=
804				    PTI_PSCHEME_INTERNAL|PTI_WHOLE_DISK;
805#endif
806			return true;
807		}
808
809		ndx++;
810		if (ndx > parts->dp.num_part || ndx > id)
811			break;
812	}
813
814	return false;
815}
816
817static bool
818disklabel_set_part_info(struct disk_partitions *arg, part_id id,
819    const struct disk_part_info *info, const char **err_msg)
820{
821	struct disklabel_disk_partitions *parts =
822	    (struct disklabel_disk_partitions*)arg;
823	part_id ndx;
824	bool was_inst_target;
825
826	if (dl_types[0].description == NULL)
827		dl_init_types();
828
829	ndx = 0;
830	for (int part = 0; part < parts->l.d_npartitions; part++) {
831		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
832		    && parts->l.d_partitions[part].p_size == 0)
833			continue;
834
835		if (ndx == id) {
836			was_inst_target = parts->l.d_partitions[part].p_offset
837			    == parts->install_target;
838			parts->l.d_partitions[part].p_offset = info->start;
839			if (part != RAW_PART
840#if RAW_PART == 3
841				&& (part != RAW_PART-1 ||
842				    parts->dp.parent == NULL)
843#endif
844							) {
845				parts->dp.free_space +=
846				    parts->l.d_partitions[part].p_size -
847				    info->size;
848			}
849			parts->l.d_partitions[part].p_size = info->size;
850			parts->l.d_partitions[part].p_fstype =
851			    dl_part_type_from_generic(info->nat_type);
852			parts->l.d_partitions[part].p_fsize = info->fs_opt2;
853			if (info->fs_opt2 != 0)
854				parts->l.d_partitions[part].p_frag =
855				    info->fs_opt1 / info->fs_opt2;
856			else
857				parts->l.d_partitions[part].p_frag = 0;
858			parts->fs_opt3[part] = info->fs_opt3;
859			if (info->last_mounted != NULL &&
860			    info->last_mounted != parts->last_mounted[part])
861				strlcpy(parts->last_mounted[part],
862				    info->last_mounted,
863				    sizeof(parts->last_mounted[part]));
864			if (info->flags & PTI_INSTALL_TARGET)
865				parts->install_target = info->start;
866			else if (was_inst_target)
867				parts->install_target = -1;
868			assert(info->fs_type == 0 || info->fs_type ==
869			    parts->l.d_partitions[part].p_fstype);
870			if (info->fs_sub_type != 0)
871				parts->fs_sub_type[part] = info->fs_sub_type;
872			return true;
873		}
874
875		ndx++;
876		if (ndx > parts->dp.num_part || ndx > id)
877			break;
878	}
879
880	return false;
881}
882
883static size_t
884disklabel_get_free_spaces_internal(const struct
885    disklabel_disk_partitions *parts,
886    struct disk_part_free_space *result, size_t max_num_result,
887    daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
888{
889	size_t cnt = 0, i;
890	daddr_t s, e, from, size, end_of_disk;
891
892	if (start < parts->dp.disk_start)
893		start = parts->dp.disk_start;
894	if (min_space_size < 1)
895		min_space_size = 1;
896	if (align > 1 && (start % align) != 0)
897		start = max(roundup(start, align), align);
898	end_of_disk = parts->dp.disk_start + parts->dp.disk_size;
899	from = start;
900	while (from < end_of_disk && cnt < max_num_result) {
901again:
902		size = parts->dp.disk_start + parts->dp.disk_size - from;
903		start = from;
904		for (i = 0; i < parts->l.d_npartitions; i++) {
905			if (i == RAW_PART)
906				continue;
907			if (parts->l.d_partitions[i].p_fstype == FS_UNUSED)
908				continue;
909			if (parts->l.d_partitions[i].p_size == 0)
910				continue;
911
912			s = parts->l.d_partitions[i].p_offset;
913			e = parts->l.d_partitions[i].p_size + s;
914			if (s == ignore)
915				continue;
916			if (e < from)
917				continue;
918			if (s <= from && e > from) {
919				if (e - 1 >= end_of_disk)
920					return cnt;
921
922				from = e + 1;
923				if (align > 1) {
924					from = max(roundup(from, align), align);
925					if (from >= end_of_disk) {
926						size = 0;
927						break;
928					}
929				}
930				goto again;
931			}
932			if (s > from && s - from < size) {
933				size = s - from;
934			}
935		}
936		if (size >= min_space_size) {
937			result->start = start;
938			result->size = size;
939			result++;
940			cnt++;
941		}
942		from += size + 1;
943		if (align > 1)
944			from = max(roundup(from, align), align);
945	}
946
947	return cnt;
948}
949
950static bool
951disklabel_can_add_partition(const struct disk_partitions *arg)
952{
953	const struct disklabel_disk_partitions *parts =
954	    (const struct disklabel_disk_partitions*)arg;
955	struct disk_part_free_space space;
956	int i;
957
958	if (dl_maxpart == 0)
959		dl_maxpart = getmaxpartitions();
960	if (parts->dp.free_space < parts->ptn_alignment)
961		return false;
962	if (parts->dp.num_part >= dl_maxpart)
963		return false;
964	if (disklabel_get_free_spaces_internal(parts, &space, 1,
965	    parts->ptn_alignment, parts->ptn_alignment, 0, -1) < 1)
966		return false;
967	if (parts->l.d_npartitions < dl_maxpart)
968		return true;
969	for (i = 0; i < parts->l.d_npartitions; i++) {
970		if (i == RAW_PART)
971			continue;
972#if RAW_PART == 3
973		if (i == RAW_PART-1 && parts->dp.parent != NULL)
974			continue;
975#endif
976		if (parts->l.d_partitions[i].p_fstype == FS_UNUSED)
977			return true;
978	}
979	return false;
980}
981
982static bool
983disklabel_get_disk_pack_name(const struct disk_partitions *arg,
984    char *buf, size_t len)
985{
986	const struct disklabel_disk_partitions *parts =
987	    (const struct disklabel_disk_partitions*)arg;
988
989	strlcpy(buf, parts->l.d_packname, min(len,
990	    sizeof(parts->l.d_packname)+1));
991	return true;
992}
993
994static bool
995disklabel_set_disk_pack_name(struct disk_partitions *arg, const char *pack)
996{
997	struct disklabel_disk_partitions *parts =
998	    (struct disklabel_disk_partitions*)arg;
999
1000	strncpy(parts->l.d_packname, pack, sizeof(parts->l.d_packname));
1001	return true;
1002}
1003
1004static bool
1005disklabel_get_part_device(const struct disk_partitions *arg,
1006    part_id ptn, char *devname, size_t max_devname_len, int *part,
1007    enum dev_name_usage which_name, bool with_path, bool life)
1008{
1009	const struct disklabel_disk_partitions *parts =
1010	    (const struct disklabel_disk_partitions*)arg;
1011	part_id id;
1012	int part_index;
1013	char pname;
1014
1015	if (ptn >= parts->l.d_npartitions)
1016		return false;
1017
1018	for (id = part_index = 0; part_index < parts->l.d_npartitions;
1019	    part_index++) {
1020		if (parts->l.d_partitions[part_index].p_fstype == FS_UNUSED &&
1021		    parts->l.d_partitions[part_index].p_size == 0)
1022			continue;
1023		if (id == ptn)
1024			break;
1025		id++;
1026		if (id > ptn)
1027			return false;
1028	}
1029
1030	if (part != 0)
1031		*part = part_index;
1032
1033	pname = 'a'+ part_index;
1034
1035	switch (which_name) {
1036	case parent_device_only:
1037		strlcpy(devname, arg->disk, max_devname_len);
1038		return true;
1039	case logical_name:
1040	case plain_name:
1041		if (with_path)
1042			snprintf(devname, max_devname_len, _PATH_DEV "%s%c",
1043			    arg->disk, pname);
1044		else
1045			snprintf(devname, max_devname_len, "%s%c",
1046			    arg->disk, pname);
1047		return true;
1048	case raw_dev_name:
1049		if (with_path)
1050			snprintf(devname, max_devname_len, _PATH_DEV "r%s%c",
1051			    arg->disk, pname);
1052		else
1053			snprintf(devname, max_devname_len, "r%s%c",
1054			    arg->disk, pname);
1055		return true;
1056	}
1057
1058	return false;
1059}
1060
1061/*
1062 * If the requested partition file system type internally skips
1063 * the disk label sector, we can allow it to start at the beginning
1064 * of the disk. In most cases though we have to move the partition
1065 * to start past the label sector.
1066 */
1067static bool
1068need_to_skip_past_label(const struct disk_part_info *info)
1069{
1070	switch (info->fs_type) {
1071	case FS_BSDFFS:
1072	case FS_RAID:
1073		return false;
1074	}
1075
1076	return true;
1077}
1078
1079static part_id
1080disklabel_add_partition(struct disk_partitions *arg,
1081    const struct disk_part_info *info, const char **err_msg)
1082{
1083	struct disklabel_disk_partitions *parts =
1084	    (struct disklabel_disk_partitions*)arg;
1085	int i, part = -1;
1086	part_id new_id;
1087	struct disk_part_free_space space;
1088	struct disk_part_info data = *info;
1089
1090	if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 1,
1091	    data.start, -1) < 1) {
1092		if (err_msg)
1093			*err_msg = msg_string(MSG_No_free_space);
1094		return NO_PART;
1095	}
1096	if (space.start <= (parts->dp.disk_start + LABELSECTOR) &&
1097	    need_to_skip_past_label(&data)) {
1098		daddr_t new_start = roundup(parts->dp.disk_start + LABELSECTOR,
1099		    parts->ptn_alignment);
1100		daddr_t off = new_start - space.start;
1101		space.start += off;
1102		space.size -= off;
1103	}
1104	if (data.size > space.size)
1105		data.size = space.size;
1106	daddr_t dend = data.start+data.size;
1107	if (space.start > data.start)
1108		data.start = space.start;
1109	if (space.start + space.size < dend)
1110		data.size = space.start+space.size-data.start;
1111
1112	if (dl_maxpart == 0)
1113		dl_maxpart = getmaxpartitions();
1114
1115	for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) {
1116		if (parts->l.d_partitions[i].p_size > 0)
1117			new_id++;
1118		if (data.nat_type->generic_ptype != PT_root &&
1119		    data.nat_type->generic_ptype != PT_swap && i < RAW_PART)
1120			continue;
1121		if (i == 0 && data.nat_type->generic_ptype != PT_root)
1122			continue;
1123		if (i == 1 && data.nat_type->generic_ptype != PT_swap)
1124			continue;
1125		if (i == RAW_PART)
1126			continue;
1127#if RAW_PART == 3
1128		if (i == RAW_PART-1 && parts->dp.parent != NULL)
1129			continue;
1130#endif
1131		if (parts->l.d_partitions[i].p_size > 0)
1132			continue;
1133		part = i;
1134		break;
1135	}
1136
1137	if (part < 0) {
1138		if (parts->l.d_npartitions >= dl_maxpart) {
1139			if (err_msg)
1140				*err_msg =
1141				    msg_string(MSG_err_too_many_partitions);
1142			return NO_PART;
1143		}
1144
1145		part = parts->l.d_npartitions++;
1146	}
1147	parts->l.d_partitions[part].p_offset = data.start;
1148	parts->l.d_partitions[part].p_size = data.size;
1149	parts->l.d_partitions[part].p_fstype =
1150	     dl_part_type_from_generic(data.nat_type);
1151	parts->l.d_partitions[part].p_fsize = info->fs_opt2;
1152	if (info->fs_opt2 != 0)
1153		parts->l.d_partitions[part].p_frag =
1154		    info->fs_opt1 / info->fs_opt2;
1155	else
1156		parts->l.d_partitions[part].p_frag = 0;
1157	if (data.last_mounted && data.last_mounted[0])
1158		strlcpy(parts->last_mounted[part], data.last_mounted,
1159		    sizeof(parts->last_mounted[part]));
1160	else
1161		parts->last_mounted[part][0] = 0;
1162	parts->fs_sub_type[part] = data.fs_sub_type;
1163	parts->dp.num_part++;
1164	if (data.size <= parts->dp.free_space)
1165		parts->dp.free_space -= data.size;
1166	else
1167		parts->dp.free_space = 0;
1168
1169	return new_id;
1170}
1171
1172static part_id
1173disklabel_add_outer_partition(struct disk_partitions *arg,
1174    const struct disk_part_info *info, const char **err_msg)
1175{
1176	struct disklabel_disk_partitions *parts =
1177	    (struct disklabel_disk_partitions*)arg;
1178	int i, part = -1;
1179	part_id new_id;
1180
1181	if (dl_maxpart == 0)
1182		dl_maxpart = getmaxpartitions();
1183
1184	for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) {
1185		if (parts->l.d_partitions[i].p_size > 0)
1186			new_id++;
1187		if (info->nat_type->generic_ptype != PT_root &&
1188		    info->nat_type->generic_ptype != PT_swap && i < RAW_PART)
1189			continue;
1190		if (i == 0 && info->nat_type->generic_ptype != PT_root)
1191			continue;
1192		if (i == 1 && info->nat_type->generic_ptype != PT_swap)
1193			continue;
1194		if (i == RAW_PART)
1195			continue;
1196#if RAW_PART == 3
1197		if (i == RAW_PART-1 && parts->dp.parent != NULL)
1198			continue;
1199#endif
1200		if (parts->l.d_partitions[i].p_size > 0)
1201			continue;
1202		part = i;
1203		break;
1204	}
1205
1206	if (part < 0) {
1207		if (parts->l.d_npartitions >= dl_maxpart) {
1208			if (err_msg)
1209				*err_msg =
1210				    msg_string(MSG_err_too_many_partitions);
1211			return NO_PART;
1212		}
1213
1214		part = parts->l.d_npartitions++;
1215	}
1216	parts->l.d_partitions[part].p_offset = info->start;
1217	parts->l.d_partitions[part].p_size = info->size;
1218	parts->l.d_partitions[part].p_fstype =
1219	     dl_part_type_from_generic(info->nat_type);
1220	parts->l.d_partitions[part].p_fsize = info->fs_opt2;
1221	if (info->fs_opt2 != 0)
1222		parts->l.d_partitions[part].p_frag =
1223		    info->fs_opt1 / info->fs_opt2;
1224	else
1225		parts->l.d_partitions[part].p_frag = 0;
1226	if (info->last_mounted && info->last_mounted[0])
1227		strlcpy(parts->last_mounted[part], info->last_mounted,
1228		    sizeof(parts->last_mounted[part]));
1229	else
1230		parts->last_mounted[part][0] = 0;
1231	parts->fs_sub_type[part] = info->fs_sub_type;
1232	parts->dp.num_part++;
1233
1234	return new_id;
1235}
1236
1237static size_t
1238disklabel_get_free_spaces(const struct disk_partitions *arg,
1239    struct disk_part_free_space *result, size_t max_num_result,
1240    daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
1241{
1242	const struct disklabel_disk_partitions *parts =
1243	    (const struct disklabel_disk_partitions*)arg;
1244
1245	return disklabel_get_free_spaces_internal(parts, result,
1246	    max_num_result, min_space_size, align, start, ignore);
1247}
1248
1249static daddr_t
1250disklabel_max_free_space_at(const struct disk_partitions *arg, daddr_t start)
1251{
1252	const struct disklabel_disk_partitions *parts =
1253	    (const struct disklabel_disk_partitions*)arg;
1254	struct disk_part_free_space space;
1255
1256	if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 0,
1257	    start, start) == 1)
1258		return space.size;
1259
1260	return 0;
1261}
1262
1263static daddr_t
1264disklabel_get_alignment(const struct disk_partitions *arg)
1265{
1266	const struct disklabel_disk_partitions *parts =
1267	    (const struct disklabel_disk_partitions*)arg;
1268
1269	return parts->ptn_alignment;
1270}
1271
1272static part_id
1273disklabel_find_by_name(struct disk_partitions *arg, const char *name)
1274{
1275	const struct disklabel_disk_partitions *parts =
1276	    (const struct disklabel_disk_partitions*)arg;
1277	char *sl, part;
1278	ptrdiff_t n;
1279	part_id pno, id, i;
1280
1281	sl = strrchr(name, '/');
1282	if (sl == NULL)
1283		return NO_PART;
1284	n = sl - name;
1285	if (strncmp(name, parts->l.d_packname, n) != 0)
1286		return NO_PART;
1287	part = name[n+1];
1288	if (part < 'a')
1289		return NO_PART;
1290	pno = part - 'a';
1291	if (pno >= parts->l.d_npartitions)
1292		return NO_PART;
1293	if (parts->l.d_partitions[pno].p_fstype == FS_UNUSED)
1294		return NO_PART;
1295	for (id = 0, i = 0; i < pno; i++)
1296		if (parts->l.d_partitions[i].p_fstype != FS_UNUSED ||
1297		    parts->l.d_partitions[i].p_size != 0)
1298			id++;
1299	return id;
1300}
1301
1302static void
1303disklabel_free(struct disk_partitions *arg)
1304{
1305
1306	assert(arg != NULL);
1307	free(__UNCONST(arg->disk));
1308	free(arg);
1309}
1310
1311static void
1312disklabel_destroy_part_scheme(struct disk_partitions *arg)
1313{
1314
1315	run_program(RUN_SILENT, "disklabel -D %s", arg->disk);
1316	free(arg);
1317}
1318
1319const struct disk_partitioning_scheme
1320disklabel_parts = {
1321	.name = MSG_parttype_disklabel,
1322	.short_name = MSG_parttype_disklabel_short,
1323	.new_type_prompt = MSG_dl_get_custom_fstype,
1324	.size_limit = (daddr_t)UINT32_MAX,
1325	.write_to_disk = disklabel_write_to_disk,
1326	.read_from_disk = disklabel_parts_read,
1327	.create_new_for_disk = disklabel_parts_new,
1328#ifdef NO_DISKLABEL_BOOT
1329	.have_boot_support = disklabel_non_bootable,
1330#endif
1331	.change_disk_geom = disklabel_change_geom,
1332	.get_cylinder_size = disklabel_cylinder_size,
1333	.find_by_name = disklabel_find_by_name,
1334	.get_disk_pack_name = disklabel_get_disk_pack_name,
1335	.set_disk_pack_name = disklabel_set_disk_pack_name,
1336	.delete_all_partitions = disklabel_delete_all,
1337	.delete_partitions_in_range = disklabel_delete_range,
1338	.delete_partition = disklabel_delete,
1339	.get_part_types_count = disklabel_type_count,
1340	.get_part_type = disklabel_get_type,
1341	.get_generic_part_type = disklabel_get_generic_type,
1342	.get_fs_part_type = disklabel_get_fs_part_type,
1343	.get_default_fstype = disklabel_get_default_fstype,
1344	.create_custom_part_type = disklabel_create_custom_part_type,
1345	.create_unknown_part_type = disklabel_create_unknown_part_type,
1346	.get_part_alignment = disklabel_get_alignment,
1347	.adapt_foreign_part_info = generic_adapt_foreign_part_info,
1348	.get_part_info = disklabel_get_part_info,
1349	.can_add_partition = disklabel_can_add_partition,
1350	.set_part_info = disklabel_set_part_info,
1351	.add_partition = disklabel_add_partition,
1352	.add_outer_partition = disklabel_add_outer_partition,
1353	.max_free_space_at = disklabel_max_free_space_at,
1354	.get_free_spaces = disklabel_get_free_spaces,
1355	.get_part_device = disklabel_get_part_device,
1356	.free = disklabel_free,
1357	.destroy_part_scheme = disklabel_destroy_part_scheme,
1358};
1359