disklabel.c revision 1.47
1/*	$NetBSD: disklabel.c,v 1.47 2022/06/21 15:42:43 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->dp.free_space +=
524			    parts->l.d_partitions[part].p_size;
525			parts->l.d_partitions[part].p_size = 0;
526			parts->l.d_partitions[part].p_offset = 0;
527			parts->l.d_partitions[part].p_fstype = FS_UNUSED;
528			parts->dp.num_part--;
529			return true;
530		}
531		ndx++;
532	}
533
534	if (err_msg)
535		*err_msg = INTERNAL_ERROR;
536	return false;
537}
538
539static bool
540disklabel_delete_range(struct disk_partitions *arg, daddr_t r_start,
541    daddr_t r_size)
542{
543	struct disklabel_disk_partitions *parts =
544	    (struct disklabel_disk_partitions*)arg;
545
546	for (int part = 0; part < parts->l.d_npartitions; part++) {
547		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
548		    && parts->l.d_partitions[part].p_size == 0)
549			continue;
550
551		if (part == RAW_PART)
552			continue;
553
554		daddr_t start = parts->l.d_partitions[part].p_offset;
555		daddr_t end = start + parts->l.d_partitions[part].p_size;
556
557#if RAW_PART == 3
558		if (parts->dp.parent != NULL &&
559		    part == RAW_PART - 1 && start == r_start &&
560		    r_start + r_size == end)
561			continue;
562#endif
563
564		if ((start >= r_start && start <= r_start+r_size) ||
565		    (end >= r_start && end <= r_start+r_size)) {
566			if (start == parts->install_target)
567				parts->install_target  = -1;
568			if (parts->dp.num_part > 1)
569				parts->dp.num_part--;
570			parts->dp.free_space +=
571			    parts->l.d_partitions[part].p_size;
572			parts->l.d_partitions[part].p_fstype = FS_UNUSED;
573			parts->l.d_partitions[part].p_size = 0;
574		}
575	}
576
577	return true;
578}
579
580static void
581dl_init_types(void)
582{
583	for (size_t i = 0; i < __arraycount(dl_types); i++) {
584		if (fstypenames[i] == NULL)
585			break;
586		dl_types[i].short_desc =
587		dl_types[i].description = getfslabelname(i, 0);
588		enum part_type pt;
589		switch (i) {
590		case FS_UNUSED:	pt = PT_undef; break;
591		case FS_BSDFFS:
592		case FS_RAID:
593		case FS_BSDLFS:
594		case FS_CGD:
595				pt = PT_root; break;
596		case FS_SWAP:	pt = PT_swap; break;
597		case FS_MSDOS:	pt = PT_FAT; break;
598		case FS_EX2FS:	pt = PT_EXT2; break;
599		case FS_SYSVBFS:
600				pt = PT_SYSVBFS; break;
601		default:	pt = PT_unknown; break;
602		}
603		dl_types[i].generic_ptype = pt;
604	}
605}
606
607static uint8_t
608dl_part_type_from_generic(const struct part_type_desc *gent)
609{
610
611	if (dl_types[0].description == NULL)
612		dl_init_types();
613	for (size_t i = 0; i < __arraycount(dl_types); i++)
614		if (gent == &dl_types[i])
615			return (uint8_t)i;
616
617	for (size_t i = 0; i < dl_custom_ptype_count; i++)
618		if (gent == &dl_custom_ptypes[i].desc)
619			return dl_custom_ptypes[i].type;
620
621	return 0;
622}
623
624static size_t
625disklabel_type_count(void)
626{
627	return __arraycount(dl_types) + dl_custom_ptype_count;
628}
629
630static const struct part_type_desc *
631disklabel_get_type(size_t ndx)
632{
633	if (dl_types[0].description == NULL)
634		dl_init_types();
635
636	if (ndx < __arraycount(dl_types))
637		return &dl_types[ndx];
638
639	ndx -= __arraycount(dl_types);
640	if (ndx >= dl_custom_ptype_count)
641		return NULL;
642
643	return &dl_custom_ptypes[ndx].desc;
644}
645
646static const struct part_type_desc *
647disklabel_find_type(uint type, bool create_if_unknown)
648{
649	if (dl_types[0].description == NULL)
650		dl_init_types();
651
652	if (type < __arraycount(dl_types))
653		return &dl_types[type];
654
655	for (size_t i = 0; i < dl_custom_ptype_count; i++)
656		if (dl_custom_ptypes[i].type == type)
657			return &dl_custom_ptypes[i].desc;
658
659	if (create_if_unknown) {
660		struct dl_custom_ptype *nt;
661
662		nt = realloc(dl_custom_ptypes, dl_custom_ptype_count+1);
663		if (nt == NULL)
664			return NULL;
665		dl_custom_ptypes = nt;
666		nt = dl_custom_ptypes + dl_custom_ptype_count;
667		dl_custom_ptype_count++;
668		memset(nt, 0, sizeof(*nt));
669		nt->type = type;
670		snprintf(nt->short_desc, sizeof(nt->short_desc), "%u", type);
671		nt->short_desc[sizeof(nt->short_desc)-1] = 0;
672		snprintf(nt->description, sizeof(nt->description),
673		    "%s (%u)", msg_string(MSG_custom_type), type);
674		nt->description[sizeof(nt->description)-1] = 0;
675		nt->desc.generic_ptype = PT_unknown;
676		nt->desc.short_desc = nt->short_desc;
677		nt->desc.description = nt->description;
678		return &nt->desc;
679	}
680
681	return NULL;
682}
683
684static const struct part_type_desc *
685disklabel_create_custom_part_type(const char *custom, const char **err_msg)
686{
687	char *endp;
688	unsigned long fstype;
689
690	fstype = strtoul(custom, &endp, 10);
691	if (*endp != 0) {
692		if (err_msg)
693			*err_msg = msg_string(MSG_dl_type_invalid);
694		return NULL;
695	}
696
697	return disklabel_find_type(fstype, true);
698}
699
700static const struct part_type_desc *
701disklabel_get_fs_part_type(enum part_type pt, unsigned fstype, unsigned subtype)
702{
703	return disklabel_find_type(fstype, false);
704}
705
706static const struct part_type_desc *
707disklabel_create_unknown_part_type(void)
708{
709	return disklabel_find_type(FS_OTHER, false);
710}
711
712static const struct part_type_desc *
713disklabel_get_generic_type(enum part_type pt)
714{
715	size_t nt;
716
717	if (dl_types[0].description == NULL)
718		dl_init_types();
719
720	switch (pt) {
721	case PT_root:	nt = FS_BSDFFS; break;
722	case PT_swap:	nt = FS_SWAP; break;
723	case PT_FAT:
724	case PT_EFI_SYSTEM:
725			nt = FS_MSDOS; break;
726	case PT_EXT2:	nt = FS_EX2FS; break;
727	case PT_SYSVBFS:
728			nt = FS_SYSVBFS; break;
729	default:	nt = FS_UNUSED; break;
730	}
731
732	return disklabel_get_type(nt);
733}
734
735static bool
736disklabel_get_default_fstype(const struct part_type_desc *nat_type,
737    unsigned *fstype, unsigned *fs_sub_type)
738{
739
740	*fstype = dl_part_type_from_generic(nat_type);
741#ifdef DEFAULT_UFS2
742        if (*fstype == FS_BSDFFS)
743                *fs_sub_type = 2;
744        else
745#endif
746                *fs_sub_type = 0;
747        return true;
748}
749
750static bool
751disklabel_get_part_info(const struct disk_partitions *arg, part_id id,
752    struct disk_part_info *info)
753{
754	const struct disklabel_disk_partitions *parts =
755	    (const struct disklabel_disk_partitions*)arg;
756	part_id ndx;
757
758	if (dl_types[0].description == NULL)
759		dl_init_types();
760
761	ndx = 0;
762	for (int part = 0; part < parts->l.d_npartitions; part++) {
763		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
764		    && parts->l.d_partitions[part].p_size == 0)
765			continue;
766
767		if (ndx == id) {
768			memset(info, 0, sizeof(*info));
769			info->start = parts->l.d_partitions[part].p_offset;
770			info->size = parts->l.d_partitions[part].p_size;
771			info->nat_type = disklabel_find_type(
772			    parts->l.d_partitions[part].p_fstype, true);
773			if (parts->last_mounted[part][0] != 0)
774				info->last_mounted = parts->last_mounted[part];
775			info->fs_type = parts->l.d_partitions[part].p_fstype;
776			info->fs_sub_type = parts->fs_sub_type[part];
777			info->fs_opt2 = parts->l.d_partitions[part].p_fsize;
778			info->fs_opt1 = info->fs_opt2 *
779			    parts->l.d_partitions[part].p_frag;
780			info->fs_opt3 = parts->fs_opt3[part];
781			if (part == RAW_PART &&
782			    parts->l.d_partitions[part].p_fstype == FS_UNUSED)
783				info->flags |=
784				    PTI_PSCHEME_INTERNAL|PTI_RAW_PART;
785			if (info->start == parts->install_target &&
786			    parts->l.d_partitions[part].p_fstype != FS_UNUSED)
787				info->flags |= PTI_INSTALL_TARGET;
788#if RAW_PART == 3
789			if (part == (RAW_PART-1) && parts->dp.parent != NULL &&
790			    parts->l.d_partitions[part].p_fstype == FS_UNUSED)
791				info->flags |=
792				    PTI_PSCHEME_INTERNAL|PTI_WHOLE_DISK;
793#endif
794			return true;
795		}
796
797		ndx++;
798		if (ndx > parts->dp.num_part || ndx > id)
799			break;
800	}
801
802	return false;
803}
804
805static bool
806disklabel_set_part_info(struct disk_partitions *arg, part_id id,
807    const struct disk_part_info *info, const char **err_msg)
808{
809	struct disklabel_disk_partitions *parts =
810	    (struct disklabel_disk_partitions*)arg;
811	part_id ndx;
812	bool was_inst_target;
813
814	if (dl_types[0].description == NULL)
815		dl_init_types();
816
817	ndx = 0;
818	for (int part = 0; part < parts->l.d_npartitions; part++) {
819		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
820		    && parts->l.d_partitions[part].p_size == 0)
821			continue;
822
823		if (ndx == id) {
824			was_inst_target = parts->l.d_partitions[part].p_offset
825			    == parts->install_target;
826			parts->l.d_partitions[part].p_offset = info->start;
827			if (part != RAW_PART
828#if RAW_PART == 3
829				|| part == RAW_PART-1
830#endif
831							) {
832				parts->dp.free_space +=
833				    parts->l.d_partitions[part].p_size -
834				    info->size;
835			}
836			parts->l.d_partitions[part].p_size = info->size;
837			parts->l.d_partitions[part].p_fstype =
838			    dl_part_type_from_generic(info->nat_type);
839			parts->l.d_partitions[part].p_fsize = info->fs_opt2;
840			if (info->fs_opt2 != 0)
841				parts->l.d_partitions[part].p_frag =
842				    info->fs_opt1 / info->fs_opt2;
843			else
844				parts->l.d_partitions[part].p_frag = 0;
845			parts->fs_opt3[part] = info->fs_opt3;
846			if (info->last_mounted != NULL &&
847			    info->last_mounted != parts->last_mounted[part])
848				strlcpy(parts->last_mounted[part],
849				    info->last_mounted,
850				    sizeof(parts->last_mounted[part]));
851			if (info->flags & PTI_INSTALL_TARGET)
852				parts->install_target = info->start;
853			else if (was_inst_target)
854				parts->install_target = -1;
855			assert(info->fs_type == 0 || info->fs_type ==
856			    parts->l.d_partitions[part].p_fstype);
857			if (info->fs_sub_type != 0)
858				parts->fs_sub_type[part] = info->fs_sub_type;
859			return true;
860		}
861
862		ndx++;
863		if (ndx > parts->dp.num_part || ndx > id)
864			break;
865	}
866
867	return false;
868}
869
870static size_t
871disklabel_get_free_spaces_internal(const struct
872    disklabel_disk_partitions *parts,
873    struct disk_part_free_space *result, size_t max_num_result,
874    daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
875{
876	size_t cnt = 0, i;
877	daddr_t s, e, from, size, end_of_disk;
878
879	if (start < parts->dp.disk_start)
880		start = parts->dp.disk_start;
881	if (min_space_size < 1)
882		min_space_size = 1;
883	if (align > 1 && (start % align) != 0)
884		start = max(roundup(start, align), align);
885	end_of_disk = parts->dp.disk_start + parts->dp.disk_size;
886	from = start;
887	while (from < end_of_disk && cnt < max_num_result) {
888again:
889		size = parts->dp.disk_start + parts->dp.disk_size - from;
890		start = from;
891		for (i = 0; i < parts->l.d_npartitions; i++) {
892			if (i == RAW_PART)
893				continue;
894			if (parts->l.d_partitions[i].p_fstype == FS_UNUSED)
895				continue;
896			if (parts->l.d_partitions[i].p_size == 0)
897				continue;
898
899			s = parts->l.d_partitions[i].p_offset;
900			e = parts->l.d_partitions[i].p_size + s;
901			if (s == ignore)
902				continue;
903			if (e < from)
904				continue;
905			if (s <= from && e > from) {
906				if (e - 1 >= end_of_disk)
907					return cnt;
908
909				from = e + 1;
910				if (align > 1) {
911					from = max(roundup(from, align), align);
912					if (from >= end_of_disk) {
913						size = 0;
914						break;
915					}
916				}
917				goto again;
918			}
919			if (s > from && s - from < size) {
920				size = s - from;
921			}
922		}
923		if (size >= min_space_size) {
924			result->start = start;
925			result->size = size;
926			result++;
927			cnt++;
928		}
929		from += size + 1;
930		if (align > 1)
931			from = max(roundup(from, align), align);
932	}
933
934	return cnt;
935}
936
937static bool
938disklabel_can_add_partition(const struct disk_partitions *arg)
939{
940	const struct disklabel_disk_partitions *parts =
941	    (const struct disklabel_disk_partitions*)arg;
942	struct disk_part_free_space space;
943	int i;
944
945	if (dl_maxpart == 0)
946		dl_maxpart = getmaxpartitions();
947	if (parts->dp.free_space < parts->ptn_alignment)
948		return false;
949	if (parts->dp.num_part >= dl_maxpart)
950		return false;
951	if (disklabel_get_free_spaces_internal(parts, &space, 1,
952	    parts->ptn_alignment, parts->ptn_alignment, 0, -1) < 1)
953		return false;
954	if (parts->l.d_npartitions < dl_maxpart)
955		return true;
956	for (i = 0; i < parts->l.d_npartitions; i++) {
957		if (i == RAW_PART)
958			continue;
959#if RAW_PART == 3
960		if (i == RAW_PART-1 && parts->dp.parent != NULL)
961			continue;
962#endif
963		if (parts->l.d_partitions[i].p_fstype == FS_UNUSED)
964			return true;
965	}
966	return false;
967}
968
969static bool
970disklabel_get_disk_pack_name(const struct disk_partitions *arg,
971    char *buf, size_t len)
972{
973	const struct disklabel_disk_partitions *parts =
974	    (const struct disklabel_disk_partitions*)arg;
975
976	strlcpy(buf, parts->l.d_packname, min(len,
977	    sizeof(parts->l.d_packname)+1));
978	return true;
979}
980
981static bool
982disklabel_set_disk_pack_name(struct disk_partitions *arg, const char *pack)
983{
984	struct disklabel_disk_partitions *parts =
985	    (struct disklabel_disk_partitions*)arg;
986
987	strncpy(parts->l.d_packname, pack, sizeof(parts->l.d_packname));
988	return true;
989}
990
991static bool
992disklabel_get_part_device(const struct disk_partitions *arg,
993    part_id ptn, char *devname, size_t max_devname_len, int *part,
994    enum dev_name_usage which_name, bool with_path, bool life)
995{
996	const struct disklabel_disk_partitions *parts =
997	    (const struct disklabel_disk_partitions*)arg;
998	part_id id;
999	int part_index;
1000	char pname;
1001
1002	if (ptn >= parts->l.d_npartitions)
1003		return false;
1004
1005	for (id = part_index = 0; part_index < parts->l.d_npartitions;
1006	    part_index++) {
1007		if (parts->l.d_partitions[part_index].p_fstype == FS_UNUSED &&
1008		    parts->l.d_partitions[part_index].p_size == 0)
1009			continue;
1010		if (id == ptn)
1011			break;
1012		id++;
1013		if (id > ptn)
1014			return false;
1015	}
1016
1017	if (part != 0)
1018		*part = part_index;
1019
1020	pname = 'a'+ part_index;
1021
1022	switch (which_name) {
1023	case parent_device_only:
1024		strlcpy(devname, arg->disk, max_devname_len);
1025		return true;
1026	case logical_name:
1027	case plain_name:
1028		if (with_path)
1029			snprintf(devname, max_devname_len, _PATH_DEV "%s%c",
1030			    arg->disk, pname);
1031		else
1032			snprintf(devname, max_devname_len, "%s%c",
1033			    arg->disk, pname);
1034		return true;
1035	case raw_dev_name:
1036		if (with_path)
1037			snprintf(devname, max_devname_len, _PATH_DEV "r%s%c",
1038			    arg->disk, pname);
1039		else
1040			snprintf(devname, max_devname_len, "r%s%c",
1041			    arg->disk, pname);
1042		return true;
1043	}
1044
1045	return false;
1046}
1047
1048/*
1049 * If the requested partition file system type internally skips
1050 * the disk label sector, we can allow it to start at the beginning
1051 * of the disk. In most cases though we have to move the partition
1052 * to start past the label sector.
1053 */
1054static bool
1055need_to_skip_past_label(const struct disk_part_info *info)
1056{
1057	switch (info->fs_type) {
1058	case FS_BSDFFS:
1059	case FS_RAID:
1060		return false;
1061	}
1062
1063	return true;
1064}
1065
1066static part_id
1067disklabel_add_partition(struct disk_partitions *arg,
1068    const struct disk_part_info *info, const char **err_msg)
1069{
1070	struct disklabel_disk_partitions *parts =
1071	    (struct disklabel_disk_partitions*)arg;
1072	int i, part = -1;
1073	part_id new_id;
1074	struct disk_part_free_space space;
1075	struct disk_part_info data = *info;
1076
1077	if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 1,
1078	    data.start, -1) < 1) {
1079		if (err_msg)
1080			*err_msg = msg_string(MSG_No_free_space);
1081		return NO_PART;
1082	}
1083	if (space.start <= (parts->dp.disk_start + LABELSECTOR) &&
1084	    need_to_skip_past_label(&data)) {
1085		daddr_t new_start = roundup(parts->dp.disk_start + LABELSECTOR,
1086		    parts->ptn_alignment);
1087		daddr_t off = new_start - space.start;
1088		space.start += off;
1089		space.size -= off;
1090	}
1091	if (data.size > space.size)
1092		data.size = space.size;
1093	daddr_t dend = data.start+data.size;
1094	if (space.start > data.start)
1095		data.start = space.start;
1096	if (space.start + space.size < dend)
1097		data.size = space.start+space.size-data.start;
1098
1099	if (dl_maxpart == 0)
1100		dl_maxpart = getmaxpartitions();
1101
1102	for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) {
1103		if (parts->l.d_partitions[i].p_size > 0)
1104			new_id++;
1105		if (data.nat_type->generic_ptype != PT_root &&
1106		    data.nat_type->generic_ptype != PT_swap && i < RAW_PART)
1107			continue;
1108		if (i == 0 && data.nat_type->generic_ptype != PT_root)
1109			continue;
1110		if (i == 1 && data.nat_type->generic_ptype != PT_swap)
1111			continue;
1112		if (i == RAW_PART)
1113			continue;
1114#if RAW_PART == 3
1115		if (i == RAW_PART-1 && parts->dp.parent != NULL)
1116			continue;
1117#endif
1118		if (parts->l.d_partitions[i].p_size > 0)
1119			continue;
1120		part = i;
1121		break;
1122	}
1123
1124	if (part < 0) {
1125		if (parts->l.d_npartitions >= dl_maxpart) {
1126			if (err_msg)
1127				*err_msg =
1128				    msg_string(MSG_err_too_many_partitions);
1129			return NO_PART;
1130		}
1131
1132		part = parts->l.d_npartitions++;
1133	}
1134	parts->l.d_partitions[part].p_offset = data.start;
1135	parts->l.d_partitions[part].p_size = data.size;
1136	parts->l.d_partitions[part].p_fstype =
1137	     dl_part_type_from_generic(data.nat_type);
1138	parts->l.d_partitions[part].p_fsize = info->fs_opt2;
1139	if (info->fs_opt2 != 0)
1140		parts->l.d_partitions[part].p_frag =
1141		    info->fs_opt1 / info->fs_opt2;
1142	else
1143		parts->l.d_partitions[part].p_frag = 0;
1144	if (data.last_mounted && data.last_mounted[0])
1145		strlcpy(parts->last_mounted[part], data.last_mounted,
1146		    sizeof(parts->last_mounted[part]));
1147	else
1148		parts->last_mounted[part][0] = 0;
1149	parts->fs_sub_type[part] = data.fs_sub_type;
1150	parts->dp.num_part++;
1151	if (data.size <= parts->dp.free_space)
1152		parts->dp.free_space -= data.size;
1153	else
1154		parts->dp.free_space = 0;
1155
1156	return new_id;
1157}
1158
1159static part_id
1160disklabel_add_outer_partition(struct disk_partitions *arg,
1161    const struct disk_part_info *info, const char **err_msg)
1162{
1163	struct disklabel_disk_partitions *parts =
1164	    (struct disklabel_disk_partitions*)arg;
1165	int i, part = -1;
1166	part_id new_id;
1167
1168	if (dl_maxpart == 0)
1169		dl_maxpart = getmaxpartitions();
1170
1171	for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) {
1172		if (parts->l.d_partitions[i].p_size > 0)
1173			new_id++;
1174		if (info->nat_type->generic_ptype != PT_root &&
1175		    info->nat_type->generic_ptype != PT_swap && i < RAW_PART)
1176			continue;
1177		if (i == 0 && info->nat_type->generic_ptype != PT_root)
1178			continue;
1179		if (i == 1 && info->nat_type->generic_ptype != PT_swap)
1180			continue;
1181		if (i == RAW_PART)
1182			continue;
1183#if RAW_PART == 3
1184		if (i == RAW_PART-1 && parts->dp.parent != NULL)
1185			continue;
1186#endif
1187		if (parts->l.d_partitions[i].p_size > 0)
1188			continue;
1189		part = i;
1190		break;
1191	}
1192
1193	if (part < 0) {
1194		if (parts->l.d_npartitions >= dl_maxpart) {
1195			if (err_msg)
1196				*err_msg =
1197				    msg_string(MSG_err_too_many_partitions);
1198			return NO_PART;
1199		}
1200
1201		part = parts->l.d_npartitions++;
1202	}
1203	parts->l.d_partitions[part].p_offset = info->start;
1204	parts->l.d_partitions[part].p_size = info->size;
1205	parts->l.d_partitions[part].p_fstype =
1206	     dl_part_type_from_generic(info->nat_type);
1207	parts->l.d_partitions[part].p_fsize = info->fs_opt2;
1208	if (info->fs_opt2 != 0)
1209		parts->l.d_partitions[part].p_frag =
1210		    info->fs_opt1 / info->fs_opt2;
1211	else
1212		parts->l.d_partitions[part].p_frag = 0;
1213	if (info->last_mounted && info->last_mounted[0])
1214		strlcpy(parts->last_mounted[part], info->last_mounted,
1215		    sizeof(parts->last_mounted[part]));
1216	else
1217		parts->last_mounted[part][0] = 0;
1218	parts->fs_sub_type[part] = info->fs_sub_type;
1219	parts->dp.num_part++;
1220
1221	return new_id;
1222}
1223
1224static size_t
1225disklabel_get_free_spaces(const struct disk_partitions *arg,
1226    struct disk_part_free_space *result, size_t max_num_result,
1227    daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
1228{
1229	const struct disklabel_disk_partitions *parts =
1230	    (const struct disklabel_disk_partitions*)arg;
1231
1232	return disklabel_get_free_spaces_internal(parts, result,
1233	    max_num_result, min_space_size, align, start, ignore);
1234}
1235
1236static daddr_t
1237disklabel_max_free_space_at(const struct disk_partitions *arg, daddr_t start)
1238{
1239	const struct disklabel_disk_partitions *parts =
1240	    (const struct disklabel_disk_partitions*)arg;
1241	struct disk_part_free_space space;
1242
1243	if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 0,
1244	    start, start) == 1)
1245		return space.size;
1246
1247	return 0;
1248}
1249
1250static daddr_t
1251disklabel_get_alignment(const struct disk_partitions *arg)
1252{
1253	const struct disklabel_disk_partitions *parts =
1254	    (const struct disklabel_disk_partitions*)arg;
1255
1256	return parts->ptn_alignment;
1257}
1258
1259static part_id
1260disklabel_find_by_name(struct disk_partitions *arg, const char *name)
1261{
1262	const struct disklabel_disk_partitions *parts =
1263	    (const struct disklabel_disk_partitions*)arg;
1264	char *sl, part;
1265	ptrdiff_t n;
1266	part_id pno, id, i;
1267
1268	sl = strrchr(name, '/');
1269	if (sl == NULL)
1270		return NO_PART;
1271	n = sl - name;
1272	if (strncmp(name, parts->l.d_packname, n) != 0)
1273		return NO_PART;
1274	part = name[n+1];
1275	if (part < 'a')
1276		return NO_PART;
1277	pno = part - 'a';
1278	if (pno >= parts->l.d_npartitions)
1279		return NO_PART;
1280	if (parts->l.d_partitions[pno].p_fstype == FS_UNUSED)
1281		return NO_PART;
1282	for (id = 0, i = 0; i < pno; i++)
1283		if (parts->l.d_partitions[i].p_fstype != FS_UNUSED ||
1284		    parts->l.d_partitions[i].p_size != 0)
1285			id++;
1286	return id;
1287}
1288
1289static void
1290disklabel_free(struct disk_partitions *arg)
1291{
1292
1293	assert(arg != NULL);
1294	free(__UNCONST(arg->disk));
1295	free(arg);
1296}
1297
1298static void
1299disklabel_destroy_part_scheme(struct disk_partitions *arg)
1300{
1301
1302	run_program(RUN_SILENT, "disklabel -D %s", arg->disk);
1303	free(arg);
1304}
1305
1306const struct disk_partitioning_scheme
1307disklabel_parts = {
1308	.name = MSG_parttype_disklabel,
1309	.short_name = MSG_parttype_disklabel_short,
1310	.new_type_prompt = MSG_dl_get_custom_fstype,
1311	.size_limit = (daddr_t)UINT32_MAX,
1312	.write_to_disk = disklabel_write_to_disk,
1313	.read_from_disk = disklabel_parts_read,
1314	.create_new_for_disk = disklabel_parts_new,
1315#ifdef NO_DISKLABEL_BOOT
1316	.have_boot_support = disklabel_non_bootable,
1317#endif
1318	.change_disk_geom = disklabel_change_geom,
1319	.get_cylinder_size = disklabel_cylinder_size,
1320	.find_by_name = disklabel_find_by_name,
1321	.get_disk_pack_name = disklabel_get_disk_pack_name,
1322	.set_disk_pack_name = disklabel_set_disk_pack_name,
1323	.delete_all_partitions = disklabel_delete_all,
1324	.delete_partitions_in_range = disklabel_delete_range,
1325	.delete_partition = disklabel_delete,
1326	.get_part_types_count = disklabel_type_count,
1327	.get_part_type = disklabel_get_type,
1328	.get_generic_part_type = disklabel_get_generic_type,
1329	.get_fs_part_type = disklabel_get_fs_part_type,
1330	.get_default_fstype = disklabel_get_default_fstype,
1331	.create_custom_part_type = disklabel_create_custom_part_type,
1332	.create_unknown_part_type = disklabel_create_unknown_part_type,
1333	.get_part_alignment = disklabel_get_alignment,
1334	.adapt_foreign_part_info = generic_adapt_foreign_part_info,
1335	.get_part_info = disklabel_get_part_info,
1336	.can_add_partition = disklabel_can_add_partition,
1337	.set_part_info = disklabel_set_part_info,
1338	.add_partition = disklabel_add_partition,
1339	.add_outer_partition = disklabel_add_outer_partition,
1340	.max_free_space_at = disklabel_max_free_space_at,
1341	.get_free_spaces = disklabel_get_free_spaces,
1342	.get_part_device = disklabel_get_part_device,
1343	.free = disklabel_free,
1344	.destroy_part_scheme = disklabel_destroy_part_scheme,
1345};
1346