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