disklabel.c revision 1.20
1/*	$NetBSD: disklabel.c,v 1.20 2019/12/12 19:29:05 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;
46	char last_mounted[MAXPARTITIONS][MOUNTLEN];
47	uint fs_sub_type[MAXPARTITIONS];
48};
49
50/*
51 * Maximum number of disklabel partitions the current kernel supports
52 */
53size_t dl_maxpart;
54
55/* index into this arrray 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 / 512;
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->ptn_alignment = 64;
87	} else {
88		parts->ptn_alignment = 1;
89	}
90#ifdef MD_DISKLABEL_SET_ALIGN_POST
91	MD_DISKLABEL_SET_ALIGN_POST(parts->ptn_alignment, track);
92#endif
93}
94
95static bool
96disklabel_change_geom(struct disk_partitions *arg, int ncyl, int nhead,
97    int nsec)
98{
99	struct disklabel_disk_partitions *parts =
100	    (struct disklabel_disk_partitions*)arg;
101
102	assert(parts->l.d_secsize != 0);
103	assert(parts->l.d_nsectors != 0);
104	assert(parts->l.d_ntracks != 0);
105	assert(parts->l.d_ncylinders != 0);
106	assert(parts->l.d_secpercyl != 0);
107
108	disklabel_init_default_alignment(parts, nhead * nsec);
109	if (ncyl*nhead*nsec <= TINY_DISK_SIZE)
110		set_default_sizemult(1);
111	else
112		set_default_sizemult(MEG/512);
113
114	return true;
115}
116
117static struct disk_partitions *
118disklabel_parts_new(const char *dev, daddr_t start, daddr_t len,
119    daddr_t total_size, bool is_boot_drive)
120{
121	struct disklabel_disk_partitions *parts;
122	struct disk_geom geo;
123
124	if (!get_disk_geom(dev, &geo))
125		return NULL;
126
127	parts = calloc(1, sizeof(*parts));
128	if (parts == NULL)
129		return NULL;
130
131	if (len > disklabel_parts.size_limit)
132		len = disklabel_parts.size_limit;
133	if (total_size > disklabel_parts.size_limit)
134		total_size = disklabel_parts.size_limit;
135
136	parts->l.d_ncylinders = geo.dg_ncylinders;
137	parts->l.d_ntracks = geo.dg_ntracks;
138	parts->l.d_nsectors = geo.dg_nsectors;
139	parts->l.d_secsize = geo.dg_secsize;
140	parts->l.d_secpercyl = geo.dg_nsectors * geo.dg_ntracks;
141
142	parts->dp.pscheme = &disklabel_parts;
143	parts->dp.disk = strdup(dev);
144	parts->dp.disk_start = start;
145	parts->dp.disk_size = parts->dp.free_space = len;
146	disklabel_init_default_alignment(parts, parts->l.d_secpercyl);
147
148	strncpy(parts->l.d_packname, "fictious", sizeof parts->l.d_packname);
149
150#if RAW_PART > 2
151	parts->l.d_partitions[RAW_PART-1].p_fstype = FS_UNUSED;
152	parts->l.d_partitions[RAW_PART-1].p_offset = start;
153	parts->l.d_partitions[RAW_PART-1].p_size = len;
154	parts->dp.num_part++;
155#endif
156	parts->l.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
157	parts->l.d_partitions[RAW_PART].p_offset = 0;
158	parts->l.d_partitions[RAW_PART].p_size = total_size;
159	parts->dp.num_part++;
160
161	parts->l.d_npartitions = RAW_PART+1;
162
163	return &parts->dp;
164}
165
166static struct disk_partitions *
167disklabel_parts_read(const char *disk, daddr_t start, daddr_t len,
168    const struct disk_partitioning_scheme *scheme)
169{
170	int fd;
171	char diskpath[MAXPATHLEN];
172	uint flags;
173#ifndef DISKLABEL_NO_ONDISK_VERIFY
174	bool only_dl = only_have_disklabel();
175	bool have_raw_label = false;
176
177	/*
178	 * Verify we really have a disklabel.
179	 */
180	if (run_program(RUN_SILENT | RUN_ERROR_OK,
181	    "disklabel -r %s", disk) == 0)
182		have_raw_label = true;
183#endif
184
185	/* read partitions */
186
187	struct disklabel_disk_partitions *parts = calloc(1, sizeof(*parts));
188	if (parts == NULL)
189		return NULL;
190
191	fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
192	if (fd == -1) {
193		free(parts);
194		return NULL;
195	}
196
197	/*
198	 * We should actually try to read the label inside the start/len
199	 * boundary, but for simplicity just rely on the kernel and
200	 * instead verify a FS_UNUSED partition at RAW_PART-1 (if
201	 * RAW_PART > 'c') is within the given limits.
202	 */
203	if (ioctl(fd, DIOCGDINFO, &parts->l) < 0) {
204		free(parts);
205		close(fd);
206		return NULL;
207	}
208#if RAW_PART > 2
209	if (parts->l.d_partitions[RAW_PART-1].p_fstype == FS_UNUSED) {
210		daddr_t dlstart = parts->l.d_partitions[RAW_PART-1].p_offset;
211		daddr_t dlend = start +
212		    parts->l.d_partitions[RAW_PART-1].p_size;
213
214		if (dlstart < start && dlend > (start+len)) {
215			assert(false);
216			free(parts);
217			close(fd);
218			return NULL;
219		}
220	}
221#endif
222
223	if (len > disklabel_parts.size_limit)
224		len = disklabel_parts.size_limit;
225	parts->dp.pscheme = scheme;
226	parts->dp.disk = strdup(disk);
227	parts->dp.disk_start = start;
228	parts->dp.disk_size = parts->dp.free_space = len;
229	disklabel_init_default_alignment(parts, 0);
230
231	for (int part = 0; part < parts->l.d_npartitions; part++) {
232		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
233		    && parts->l.d_partitions[part].p_size == 0)
234			continue;
235
236		parts->dp.num_part++;
237		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED)
238			continue;
239
240		flags = 0;
241		if (parts->l.d_partitions[part].p_fstype == FS_MSDOS)
242			flags = GLM_MAYBE_FAT32;
243		else if (parts->l.d_partitions[part].p_fstype == FS_BSDFFS)
244			flags = GLM_LIKELY_FFS;
245		if (flags != 0) {
246			uint fs_type, fs_sub_type;
247			const char *lm = get_last_mounted(fd,
248			    parts->l.d_partitions[part].p_offset,
249			    &fs_type, &fs_sub_type, flags);
250			if (lm != NULL && *lm != 0) {
251				strlcpy(parts->last_mounted[part], lm,
252				    sizeof(parts->last_mounted[part]));
253				if (parts->l.d_partitions[part].p_fstype ==
254				    fs_type)
255					parts->fs_sub_type[part] = fs_sub_type;
256				canonicalize_last_mounted(
257				    parts->last_mounted[part]);
258			}
259		}
260
261		if (parts->l.d_partitions[part].p_size > parts->dp.free_space)
262			parts->dp.free_space = 0;
263		else
264			parts->dp.free_space -=
265			    parts->l.d_partitions[part].p_size;
266	}
267	close(fd);
268
269#ifndef DISKLABEL_NO_ONDISK_VERIFY
270	if (!have_raw_label && only_dl) {
271		bool found_real_part = false;
272
273		/*
274		 * Check if kernel translation gave us "something" besides
275		 * the raw or the whole-disk partition.
276		 * If not: report missing disklabel.
277		 */
278		for (int part = 0; part < parts->l.d_npartitions; part++) {
279			if (parts->l.d_partitions[part].p_fstype == FS_UNUSED)
280				continue;
281			if (part == RAW_PART)
282				continue;
283			found_real_part = true;
284			break;
285		}
286		if (!found_real_part) {
287			/* no partion there yet */
288			free(parts);
289			return NULL;
290		}
291	}
292#endif
293
294	return &parts->dp;
295}
296
297/*
298 * Escape a string for usage as a tag name in a capfile(5),
299 * we really know there is enough space in the destination buffer...
300 */
301static void
302escape_capfile(char *dest, const char *src, size_t len)
303{
304	while (*src && len > 0) {
305		if (*src == ':')
306			*dest++ = ' ';
307		else
308			*dest++ = *src;
309		src++;
310		len--;
311	}
312	*dest = 0;
313}
314
315static bool
316disklabel_write_to_disk(struct disk_partitions *arg)
317{
318	struct disklabel_disk_partitions *parts =
319	    (struct disklabel_disk_partitions*)arg;
320	FILE *f;
321	char fname[PATH_MAX], packname[sizeof(parts->l.d_packname)+1],
322	    disktype[sizeof(parts->l.d_typename)+1];
323	int i, rv = 0;
324	const char *disk = parts->dp.disk, *s;
325	const struct partition *lp;
326	char *d;
327	size_t n;
328
329	assert(parts->l.d_secsize != 0);
330	assert(parts->l.d_nsectors != 0);
331	assert(parts->l.d_ntracks != 0);
332	assert(parts->l.d_ncylinders != 0);
333	assert(parts->l.d_secpercyl != 0);
334
335	/* make sure we have a 0 terminated packname */
336	strlcpy(packname, parts->l.d_packname, sizeof packname);
337	if (packname[0] == 0)
338		strcpy(packname, "fictious");
339
340	/* fill typename with disk name prefix, if not already set */
341	if (strlen(parts->l.d_typename) == 0) {
342		for (n = 0, d = parts->l.d_typename, s = disk;
343		    *s && n < sizeof(parts->l.d_typename); d++, s++, n++) {
344			if (isdigit((unsigned char)*s))
345				break;
346			*d = *s;
347		}
348	}
349
350	/* we need a valid disk type name, so enforce an arbitrary if
351	 * above did not yield a usable one */
352	if (strlen(parts->l.d_typename) == 0)
353		strncpy(parts->l.d_typename, "SCSI",
354		    sizeof(parts->l.d_typename));
355	escape_capfile(disktype, parts->l.d_typename,
356	    sizeof(parts->l.d_typename));
357
358	sprintf(fname, "/tmp/disklabel.%u", getpid());
359	f = fopen(fname, "w");
360	if (f == NULL)
361		return false;
362
363	lp = parts->l.d_partitions;
364	scripting_fprintf(NULL, "cat <<EOF >%s\n", fname);
365	scripting_fprintf(f, "%s|NetBSD installation generated:\\\n",
366	    disktype);
367	scripting_fprintf(f, "\t:nc#%d:nt#%d:ns#%d:\\\n",
368	    parts->l.d_ncylinders, parts->l.d_ntracks, parts->l.d_nsectors);
369	scripting_fprintf(f, "\t:sc#%d:su#%" PRIu32 ":\\\n",
370	    parts->l.d_secpercyl, lp[RAW_PART].p_offset+lp[RAW_PART].p_size);
371	scripting_fprintf(f, "\t:se#%d:\\\n", parts->l.d_secsize);
372
373	for (i = 0; i < parts->l.d_npartitions; i++) {
374		scripting_fprintf(f, "\t:p%c#%" PRIu32 ":o%c#%" PRIu32
375		    ":t%c=%s:", 'a'+i, (uint32_t)lp[i].p_size,
376		    'a'+i, (uint32_t)lp[i].p_offset, 'a'+i,
377		    getfslabelname(lp[i].p_fstype, 0));
378		if (lp[i].p_fstype == FS_BSDLFS ||
379		    lp[i].p_fstype == FS_BSDFFS)
380			scripting_fprintf (f, "b%c#%" PRIu32 ":f%c#%" PRIu32
381			    ":", 'a'+i,
382			    (uint32_t)(lp[i].p_fsize *
383			    lp[i].p_frag),
384			    'a'+i, (uint32_t)lp[i].p_fsize);
385
386		if (i < parts->l.d_npartitions - 1)
387			scripting_fprintf(f, "\\\n");
388		else
389			scripting_fprintf(f, "\n");
390	}
391	scripting_fprintf(NULL, "EOF\n");
392
393	fclose(f);
394
395	/*
396	 * Label a disk using an MD-specific string DISKLABEL_CMD for
397	 * to invoke disklabel.
398	 * if MD code does not define DISKLABEL_CMD, this is a no-op.
399	 *
400	 * i386 port uses "/sbin/disklabel -w -r", just like i386
401	 * miniroot scripts, though this may leave a bogus incore label.
402	 *
403	 * Sun ports should use DISKLABEL_CMD "/sbin/disklabel -w"
404	 * to get incore to ondisk inode translation for the Sun proms.
405	 */
406#ifdef DISKLABEL_CMD
407	/* disklabel the disk */
408	rv = run_program(RUN_DISPLAY, "%s -f %s %s '%s' '%s'",
409	    DISKLABEL_CMD, fname, disk, disktype, packname);
410#endif
411
412	unlink(fname);
413
414	return rv == 0;
415}
416
417static bool
418disklabel_delete_all(struct disk_partitions *arg)
419{
420	struct disklabel_disk_partitions *parts =
421	    (struct disklabel_disk_partitions*)arg;
422	daddr_t total_size = parts->l.d_partitions[RAW_PART].p_size;
423
424	memset(&parts->l.d_partitions, 0, sizeof(parts->l.d_partitions));
425	parts->dp.num_part = 0;
426
427#if RAW_PART > 2
428	parts->l.d_partitions[RAW_PART-1].p_fstype = FS_UNUSED;
429	parts->l.d_partitions[RAW_PART-1].p_offset = parts->dp.disk_start;
430	parts->l.d_partitions[RAW_PART-1].p_size = parts->dp.disk_size;
431	parts->dp.num_part++;
432#endif
433	parts->l.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
434	parts->l.d_partitions[RAW_PART].p_offset = 0;
435	parts->l.d_partitions[RAW_PART].p_size = total_size;
436	parts->dp.num_part++;
437
438	parts->l.d_npartitions = RAW_PART+1;
439	return true;
440}
441
442static bool
443disklabel_delete(struct disk_partitions *arg, part_id id,
444    const char **err_msg)
445{
446	struct disklabel_disk_partitions *parts =
447	    (struct disklabel_disk_partitions*)arg;
448	part_id ndx;
449
450	ndx = 0;
451	for (int part = 0; part < parts->l.d_npartitions; part++) {
452		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
453		    && parts->l.d_partitions[part].p_size == 0)
454			continue;
455
456		if (ndx == id) {
457			if (part == RAW_PART
458#if RAW_PART > 2
459				|| part == RAW_PART-1
460#endif
461						) {
462				if (err_msg)
463					*err_msg = msg_string(
464					    MSG_part_not_deletable);
465				return false;
466			}
467			parts->l.d_partitions[part].p_size = 0;
468			parts->l.d_partitions[part].p_offset = 0;
469			parts->l.d_partitions[part].p_fstype = FS_UNUSED;
470			parts->dp.num_part--;
471			return true;
472		}
473		ndx++;
474	}
475
476	if (err_msg)
477		*err_msg = INTERNAL_ERROR;
478	return false;
479}
480
481static bool
482disklabel_delete_range(struct disk_partitions *arg, daddr_t r_start,
483    daddr_t r_size)
484{
485	struct disklabel_disk_partitions *parts =
486	    (struct disklabel_disk_partitions*)arg;
487
488	for (int part = 0; part < parts->l.d_npartitions; part++) {
489		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
490		    && parts->l.d_partitions[part].p_size == 0)
491			continue;
492
493		if (part == RAW_PART)
494			continue;
495
496		daddr_t start = parts->l.d_partitions[part].p_offset;
497		daddr_t end = start + parts->l.d_partitions[part].p_size;
498
499#if RAW_PART > 2
500		if (part == RAW_PART - 1 && start == r_start &&
501		    r_start + r_size == end)
502			continue;
503#endif
504
505		if ((start >= r_start && start <= r_start+r_size) ||
506		    (end >= r_start && end <= r_start+r_size)) {
507			if (parts->dp.num_part > 1)
508				parts->dp.num_part--;
509			parts->dp.free_space +=
510			    parts->l.d_partitions[part].p_size;
511			parts->l.d_partitions[part].p_fstype = FS_UNUSED;
512			parts->l.d_partitions[part].p_size = 0;
513		}
514	}
515
516	return true;
517}
518
519static void
520dl_init_types(void)
521{
522	for (size_t i = 0; i < __arraycount(dl_types); i++) {
523		if (fstypenames[i] == NULL)
524			break;
525		dl_types[i].short_desc =
526		dl_types[i].description = getfslabelname(i, 0);
527		enum part_type pt;
528		switch (i) {
529		case FS_UNUSED:	pt = PT_undef; break;
530		case FS_BSDFFS:	pt = PT_root; break;
531		case FS_SWAP:	pt = PT_swap; break;
532		case FS_MSDOS:	pt = PT_FAT; break;
533		default:	pt = PT_unknown; break;
534		}
535		dl_types[i].generic_ptype = pt;
536	}
537}
538
539static uint8_t
540dl_part_type_from_generic(const struct part_type_desc *gent)
541{
542
543	if (dl_types[0].description == NULL)
544		dl_init_types();
545	for (size_t i = 0; i < __arraycount(dl_types); i++)
546		if (gent == &dl_types[i])
547			return (uint8_t)i;
548
549	for (size_t i = 0; i < dl_custom_ptype_count; i++)
550		if (gent == &dl_custom_ptypes[i].desc)
551			return dl_custom_ptypes[i].type;
552
553	return 0;
554}
555
556static size_t
557disklabel_type_count(void)
558{
559	return __arraycount(dl_types) + dl_custom_ptype_count;
560}
561
562static const struct part_type_desc *
563disklabel_get_type(size_t ndx)
564{
565	if (dl_types[0].description == NULL)
566		dl_init_types();
567
568	if (ndx < __arraycount(dl_types))
569		return &dl_types[ndx];
570
571	ndx -= __arraycount(dl_types);
572	if (ndx >= dl_custom_ptype_count)
573		return NULL;
574
575	return &dl_custom_ptypes[ndx].desc;
576}
577
578static const struct part_type_desc *
579disklabel_find_type(uint type, bool create_if_unknown)
580{
581	if (dl_types[0].description == NULL)
582		dl_init_types();
583
584	if (type < __arraycount(dl_types))
585		return &dl_types[type];
586
587	for (size_t i = 0; i < dl_custom_ptype_count; i++)
588		if (dl_custom_ptypes[i].type == type)
589			return &dl_custom_ptypes[i].desc;
590
591	if (create_if_unknown) {
592		struct dl_custom_ptype *nt;
593
594		nt = realloc(dl_custom_ptypes, dl_custom_ptype_count+1);
595		if (nt == NULL)
596			return NULL;
597		dl_custom_ptypes = nt;
598		nt = dl_custom_ptypes + dl_custom_ptype_count;
599		dl_custom_ptype_count++;
600		memset(nt, 0, sizeof(*nt));
601		nt->type = type;
602		snprintf(nt->short_desc, sizeof(nt->short_desc), "%u", type);
603		nt->short_desc[sizeof(nt->short_desc)-1] = 0;
604		snprintf(nt->description, sizeof(nt->description),
605		    "%s (%u)", msg_string(MSG_custom_type), type);
606		nt->description[sizeof(nt->description)-1] = 0;
607		nt->desc.generic_ptype = PT_unknown;
608		nt->desc.short_desc = nt->short_desc;
609		nt->desc.description = nt->description;
610		return &nt->desc;
611	}
612
613	return NULL;
614}
615
616static const struct part_type_desc *
617disklabel_create_custom_part_type(const char *custom, const char **err_msg)
618{
619	char *endp;
620	unsigned long fstype;
621
622	fstype = strtoul(custom, &endp, 10);
623	if (*endp != 0) {
624		if (err_msg)
625			*err_msg = msg_string(MSG_dl_type_invalid);
626		return NULL;
627	}
628
629	return disklabel_find_type(fstype, true);
630}
631
632static const struct part_type_desc *
633disklabel_get_fs_part_type(unsigned fstype, unsigned subtype)
634{
635	return disklabel_find_type(fstype, false);
636}
637
638static const struct part_type_desc *
639disklabel_create_unknown_part_type(void)
640{
641	return disklabel_find_type(FS_OTHER, false);
642}
643
644static const struct part_type_desc *
645disklabel_get_generic_type(enum part_type pt)
646{
647	size_t nt;
648
649	if (dl_types[0].description == NULL)
650		dl_init_types();
651
652	switch (pt) {
653	case PT_root:	nt = FS_BSDFFS; break;
654	case PT_swap:	nt = FS_SWAP; break;
655	case PT_FAT:
656	case PT_EFI_SYSTEM:
657			nt = FS_MSDOS; break;
658	default:	nt = FS_UNUSED; break;
659	}
660
661	return disklabel_get_type(nt);
662}
663
664static bool
665disklabel_get_part_info(const struct disk_partitions *arg, part_id id,
666    struct disk_part_info *info)
667{
668	const struct disklabel_disk_partitions *parts =
669	    (const struct disklabel_disk_partitions*)arg;
670	part_id ndx;
671
672	if (dl_types[0].description == NULL)
673		dl_init_types();
674
675	ndx = 0;
676	for (int part = 0; part < parts->l.d_npartitions; part++) {
677		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
678		    && parts->l.d_partitions[part].p_size == 0)
679			continue;
680
681		if (ndx == id) {
682			memset(info, 0, sizeof(*info));
683			info->start = parts->l.d_partitions[part].p_offset;
684			info->size = parts->l.d_partitions[part].p_size;
685			info->nat_type = disklabel_find_type(
686			    parts->l.d_partitions[part].p_fstype, true);
687			if (parts->last_mounted[part][0] != 0)
688				info->last_mounted = parts->last_mounted[part];
689			info->fs_type = parts->l.d_partitions[part].p_fstype;
690			info->fs_sub_type = parts->fs_sub_type[part];
691			if (part == RAW_PART &&
692			    parts->l.d_partitions[part].p_fstype == FS_UNUSED)
693				info->flags |=
694				    PTI_PSCHEME_INTERNAL|PTI_RAW_PART;
695#if RAW_PART > 2
696			if (part == (RAW_PART-1) &&
697			    parts->l.d_partitions[part].p_fstype == FS_UNUSED)
698				info->flags |=
699				    PTI_PSCHEME_INTERNAL|PTI_WHOLE_DISK;
700#endif
701			return true;
702		}
703
704		ndx++;
705		if (ndx > parts->dp.num_part || ndx > id)
706			break;
707	}
708
709	return false;
710}
711
712static bool
713disklabel_set_part_info(struct disk_partitions *arg, part_id id,
714    const struct disk_part_info *info, const char **err_msg)
715{
716	struct disklabel_disk_partitions *parts =
717	    (struct disklabel_disk_partitions*)arg;
718	part_id ndx;
719
720	if (dl_types[0].description == NULL)
721		dl_init_types();
722
723	ndx = 0;
724	for (int part = 0; part < parts->l.d_npartitions; part++) {
725		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
726		    && parts->l.d_partitions[part].p_size == 0)
727			continue;
728
729		if (ndx == id) {
730			parts->l.d_partitions[part].p_offset = info->start;
731			parts->l.d_partitions[part].p_size = info->size;
732			parts->l.d_partitions[part].p_fstype =
733			    dl_part_type_from_generic(info->nat_type);
734			if (info->last_mounted != NULL &&
735			    info->last_mounted != parts->last_mounted[part])
736				strlcpy(parts->last_mounted[part],
737				    info->last_mounted,
738				    sizeof(parts->last_mounted[part]));
739			assert(info->fs_type == 0 || info->fs_type ==
740			    parts->l.d_partitions[part].p_fstype);
741			if (info->fs_sub_type != 0)
742				parts->fs_sub_type[part] = info->fs_sub_type;
743			return true;
744		}
745
746		ndx++;
747		if (ndx > parts->dp.num_part || ndx > id)
748			break;
749	}
750
751	return false;
752}
753
754static size_t
755disklabel_get_free_spaces_internal(const struct
756    disklabel_disk_partitions *parts,
757    struct disk_part_free_space *result, size_t max_num_result,
758    daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
759{
760	size_t cnt = 0, i;
761	daddr_t s, e, from, size, end_of_disk;
762
763	if (start < parts->dp.disk_start)
764		start = parts->dp.disk_start;
765	if (min_space_size < 1)
766		min_space_size = 1;
767	if (align > 1 && (start % align) != 0)
768		start = max(roundup(start, align), align);
769	end_of_disk = parts->dp.disk_start + parts->dp.disk_size;
770	from = start;
771	while (from < end_of_disk && cnt < max_num_result) {
772again:
773		size = parts->dp.disk_start + parts->dp.disk_size - from;
774		start = from;
775		for (i = 0; i < parts->l.d_npartitions; i++) {
776			if (i == RAW_PART)
777				continue;
778			if (parts->l.d_partitions[i].p_fstype == FS_UNUSED)
779				continue;
780			if (parts->l.d_partitions[i].p_size == 0)
781				continue;
782
783			s = parts->l.d_partitions[i].p_offset;
784			e = parts->l.d_partitions[i].p_size + s;
785			if (s == ignore)
786				continue;
787			if (e < from)
788				continue;
789			if (s <= from && e > from) {
790				if (e - 1 >= end_of_disk)
791					return cnt;
792
793				from = e + 1;
794				if (align > 1) {
795					from = max(roundup(from, align), align);
796					if (from >= end_of_disk) {
797						size = 0;
798						break;
799					}
800				}
801				goto again;
802			}
803			if (s > from && s - from < size) {
804				size = s - from;
805			}
806		}
807		if (size >= min_space_size) {
808			result->start = start;
809			result->size = size;
810			result++;
811			cnt++;
812		}
813		from += size + 1;
814		if (align > 1)
815			from = max(roundup(from, align), align);
816	}
817
818	return cnt;
819}
820
821static bool
822disklabel_can_add_partition(const struct disk_partitions *arg)
823{
824	const struct disklabel_disk_partitions *parts =
825	    (const struct disklabel_disk_partitions*)arg;
826	struct disk_part_free_space space;
827	int i;
828
829	if (dl_maxpart == 0)
830		dl_maxpart = getmaxpartitions();
831	if (parts->dp.free_space < parts->ptn_alignment)
832		return false;
833	if (parts->dp.num_part >= dl_maxpart)
834		return false;
835	if (disklabel_get_free_spaces_internal(parts, &space, 1,
836	    parts->ptn_alignment, parts->ptn_alignment, 0, -1) < 1)
837		return false;
838
839	for (i = 0; i < parts->l.d_npartitions; i++) {
840		if (i == RAW_PART)
841			continue;
842#if RAW_PART > 2
843		if (i == RAW_PART-1)
844			continue;
845#endif
846		if (parts->l.d_partitions[i].p_fstype == FS_UNUSED)
847			return true;
848	}
849	return false;
850}
851
852static bool
853disklabel_get_disk_pack_name(const struct disk_partitions *arg,
854    char *buf, size_t len)
855{
856	const struct disklabel_disk_partitions *parts =
857	    (const struct disklabel_disk_partitions*)arg;
858
859	strlcpy(buf, parts->l.d_packname, min(len,
860	    sizeof(parts->l.d_packname)+1));
861	return true;
862}
863
864static bool
865disklabel_set_disk_pack_name(struct disk_partitions *arg, const char *pack)
866{
867	struct disklabel_disk_partitions *parts =
868	    (struct disklabel_disk_partitions*)arg;
869
870	strncpy(parts->l.d_packname, pack, sizeof(parts->l.d_packname));
871	return true;
872}
873
874static bool
875disklabel_get_part_device(const struct disk_partitions *arg,
876    part_id ptn, char *devname, size_t max_devname_len, int *part,
877    enum dev_name_usage which_name, bool with_path)
878{
879	const struct disklabel_disk_partitions *parts =
880	    (const struct disklabel_disk_partitions*)arg;
881	part_id id;
882	int part_index;
883	char pname;
884
885	if (ptn >= parts->l.d_npartitions)
886		return false;
887
888	for (id = part_index = 0; part_index < parts->l.d_npartitions;
889	    part_index++) {
890		if (parts->l.d_partitions[part_index].p_fstype == FS_UNUSED &&
891		    parts->l.d_partitions[part_index].p_size == 0)
892			continue;
893		if (id == ptn)
894			break;
895		id++;
896		if (id > ptn)
897			return false;
898	}
899
900	if (part != 0)
901		*part = part_index;
902
903	pname = 'a'+ part_index;
904
905	switch (which_name) {
906	case parent_device_only:
907		strlcpy(devname, arg->disk, max_devname_len);
908		return true;
909	case logical_name:
910	case plain_name:
911		if (with_path)
912			snprintf(devname, max_devname_len, _PATH_DEV "%s%c",
913			    arg->disk, pname);
914		else
915			snprintf(devname, max_devname_len, "%s%c",
916			    arg->disk, pname);
917		return true;
918	case raw_dev_name:
919		if (with_path)
920			snprintf(devname, max_devname_len, _PATH_DEV "r%s%c",
921			    arg->disk, pname);
922		else
923			snprintf(devname, max_devname_len, "r%s%c",
924			    arg->disk, pname);
925		return true;
926	}
927
928	return false;
929}
930
931static part_id
932disklabel_add_partition(struct disk_partitions *arg,
933    const struct disk_part_info *info, const char **err_msg)
934{
935	struct disklabel_disk_partitions *parts =
936	    (struct disklabel_disk_partitions*)arg;
937	int i, part = -1;
938	part_id new_id;
939	struct disk_part_free_space space;
940	struct disk_part_info data = *info;
941
942	if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 1,
943	    info->start, -1) < 1) {
944		if (err_msg)
945			*err_msg = msg_string(MSG_No_free_space);
946		return NO_PART;
947	}
948	if (data.size > space.size)
949		data.size = space.size;
950	daddr_t dend = data.start+data.size;
951	if (space.start > data.start)
952		data.start = space.start;
953	if (space.start + space.size < dend)
954		data.size = space.start+space.size-data.start;
955
956	if (dl_maxpart == 0)
957		dl_maxpart = getmaxpartitions();
958
959	for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) {
960		if (parts->l.d_partitions[i].p_size > 0)
961			new_id++;
962		if (info->nat_type->generic_ptype != PT_root &&
963		    info->nat_type->generic_ptype != PT_swap && i < RAW_PART)
964			continue;
965		if (i == 0 && info->nat_type->generic_ptype != PT_root)
966			continue;
967		if (i == 1 && info->nat_type->generic_ptype != PT_swap)
968			continue;
969		if (i == RAW_PART)
970			continue;
971#if RAW_PART > 2
972		if (i == RAW_PART-1)
973			continue;
974#endif
975		if (parts->l.d_partitions[i].p_size > 0)
976			continue;
977		part = i;
978		break;
979	}
980
981	if (part < 0) {
982		if (parts->l.d_npartitions >= dl_maxpart) {
983			if (err_msg)
984				*err_msg =
985				    msg_string(MSG_err_too_many_partitions);
986			return NO_PART;
987		}
988
989		part = parts->l.d_npartitions++;
990	}
991	parts->l.d_partitions[part].p_offset = data.start;
992	parts->l.d_partitions[part].p_size = data.size;
993	parts->l.d_partitions[part].p_fstype =
994	     dl_part_type_from_generic(info->nat_type);
995	if (info->last_mounted && info->last_mounted[0])
996		strlcpy(parts->last_mounted[part], info->last_mounted,
997		    sizeof(parts->last_mounted[part]));
998	else
999		parts->last_mounted[part][0] = 0;
1000	parts->fs_sub_type[part] = info->fs_sub_type;
1001	parts->dp.num_part++;
1002	if (data.size <= parts->dp.free_space)
1003		parts->dp.free_space -= data.size;
1004	else
1005		parts->dp.free_space = 0;
1006
1007	return new_id;
1008}
1009
1010static part_id
1011disklabel_add_outer_partition(struct disk_partitions *arg,
1012    const struct disk_part_info *info, const char **err_msg)
1013{
1014	struct disklabel_disk_partitions *parts =
1015	    (struct disklabel_disk_partitions*)arg;
1016	int i, part = -1;
1017	part_id new_id;
1018
1019	if (dl_maxpart == 0)
1020		dl_maxpart = getmaxpartitions();
1021
1022	for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) {
1023		if (parts->l.d_partitions[i].p_size > 0)
1024			new_id++;
1025		if (info->nat_type->generic_ptype != PT_root &&
1026		    info->nat_type->generic_ptype != PT_swap && i < RAW_PART)
1027			continue;
1028		if (i == 0 && info->nat_type->generic_ptype != PT_root)
1029			continue;
1030		if (i == 1 && info->nat_type->generic_ptype != PT_swap)
1031			continue;
1032		if (i == RAW_PART)
1033			continue;
1034#if RAW_PART > 2
1035		if (i == RAW_PART-1)
1036			continue;
1037#endif
1038		if (parts->l.d_partitions[i].p_size > 0)
1039			continue;
1040		part = i;
1041		break;
1042	}
1043
1044	if (part < 0) {
1045		if (parts->l.d_npartitions >= dl_maxpart) {
1046			if (err_msg)
1047				*err_msg =
1048				    msg_string(MSG_err_too_many_partitions);
1049			return NO_PART;
1050		}
1051
1052		part = parts->l.d_npartitions++;
1053	}
1054	parts->l.d_partitions[part].p_offset = info->start;
1055	parts->l.d_partitions[part].p_size = info->size;
1056	parts->l.d_partitions[part].p_fstype =
1057	     dl_part_type_from_generic(info->nat_type);
1058	if (info->last_mounted && info->last_mounted[0])
1059		strlcpy(parts->last_mounted[part], info->last_mounted,
1060		    sizeof(parts->last_mounted[part]));
1061	else
1062		parts->last_mounted[part][0] = 0;
1063	parts->fs_sub_type[part] = info->fs_sub_type;
1064	parts->dp.num_part++;
1065
1066	return new_id;
1067}
1068
1069static size_t
1070disklabel_get_free_spaces(const struct disk_partitions *arg,
1071    struct disk_part_free_space *result, size_t max_num_result,
1072    daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
1073{
1074	const struct disklabel_disk_partitions *parts =
1075	    (const struct disklabel_disk_partitions*)arg;
1076
1077	return disklabel_get_free_spaces_internal(parts, result,
1078	    max_num_result, min_space_size, align, start, ignore);
1079}
1080
1081static daddr_t
1082disklabel_max_free_space_at(const struct disk_partitions *arg, daddr_t start)
1083{
1084	const struct disklabel_disk_partitions *parts =
1085	    (const struct disklabel_disk_partitions*)arg;
1086	struct disk_part_free_space space;
1087
1088	if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 0,
1089	    start, start) == 1)
1090		return space.size;
1091
1092	return 0;
1093}
1094
1095static daddr_t
1096disklabel_get_alignment(const struct disk_partitions *arg)
1097{
1098	const struct disklabel_disk_partitions *parts =
1099	    (const struct disklabel_disk_partitions*)arg;
1100
1101	return parts->ptn_alignment;
1102}
1103
1104static part_id
1105disklabel_find_by_name(struct disk_partitions *arg, const char *name)
1106{
1107	const struct disklabel_disk_partitions *parts =
1108	    (const struct disklabel_disk_partitions*)arg;
1109	char *sl, part;
1110	ptrdiff_t n;
1111	part_id pno, id, i;
1112
1113	sl = strrchr(name, '/');
1114	if (sl == NULL)
1115		return NO_PART;
1116	n = sl - name;
1117	if (strncmp(name, parts->l.d_packname, n) != 0)
1118		return NO_PART;
1119	part = name[n+1];
1120	if (part < 'a')
1121		return NO_PART;
1122	pno = part - 'a';
1123	if (pno >= parts->l.d_npartitions)
1124		return NO_PART;
1125	if (parts->l.d_partitions[pno].p_fstype == FS_UNUSED)
1126		return NO_PART;
1127	for (id = 0, i = 0; i < pno; i++)
1128		if (parts->l.d_partitions[i].p_fstype != FS_UNUSED ||
1129		    parts->l.d_partitions[i].p_size != 0)
1130			id++;
1131	return id;
1132}
1133
1134static void
1135disklabel_free(struct disk_partitions *arg)
1136{
1137
1138	assert(arg != NULL);
1139	free(__UNCONST(arg->disk));
1140	free(arg);
1141}
1142
1143const struct disk_partitioning_scheme
1144disklabel_parts = {
1145	.name = MSG_parttype_disklabel,
1146	.short_name = MSG_parttype_disklabel_short,
1147	.new_type_prompt = MSG_dl_get_custom_fstype,
1148	.size_limit = (daddr_t)UINT32_MAX,
1149	.write_to_disk = disklabel_write_to_disk,
1150	.read_from_disk = disklabel_parts_read,
1151	.create_new_for_disk = disklabel_parts_new,
1152	.change_disk_geom = disklabel_change_geom,
1153	.find_by_name = disklabel_find_by_name,
1154	.get_disk_pack_name = disklabel_get_disk_pack_name,
1155	.set_disk_pack_name = disklabel_set_disk_pack_name,
1156	.delete_all_partitions = disklabel_delete_all,
1157	.delete_partitions_in_range = disklabel_delete_range,
1158	.delete_partition = disklabel_delete,
1159	.get_part_types_count = disklabel_type_count,
1160	.get_part_type = disklabel_get_type,
1161	.get_generic_part_type = disklabel_get_generic_type,
1162	.get_fs_part_type = disklabel_get_fs_part_type,
1163	.create_custom_part_type = disklabel_create_custom_part_type,
1164	.create_unknown_part_type = disklabel_create_unknown_part_type,
1165	.get_part_alignment = disklabel_get_alignment,
1166	.adapt_foreign_part_info = generic_adapt_foreign_part_info,
1167	.get_part_info = disklabel_get_part_info,
1168	.can_add_partition = disklabel_can_add_partition,
1169	.set_part_info = disklabel_set_part_info,
1170	.add_partition = disklabel_add_partition,
1171	.add_outer_partition = disklabel_add_outer_partition,
1172	.max_free_space_at = disklabel_max_free_space_at,
1173	.get_free_spaces = disklabel_get_free_spaces,
1174	.get_part_device = disklabel_get_part_device,
1175	.free = disklabel_free,
1176};
1177