disklabel.c revision 1.41
1/*	$NetBSD: disklabel.c,v 1.41 2020/10/12 16:14:32 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];
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 / 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 partion
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 partion 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			if (part == RAW_PART &&
776			    parts->l.d_partitions[part].p_fstype == FS_UNUSED)
777				info->flags |=
778				    PTI_PSCHEME_INTERNAL|PTI_RAW_PART;
779			if (info->start == parts->install_target)
780				info->flags |= PTI_INSTALL_TARGET;
781#if RAW_PART == 3
782			if (part == (RAW_PART-1) && parts->dp.parent != NULL &&
783			    parts->l.d_partitions[part].p_fstype == FS_UNUSED)
784				info->flags |=
785				    PTI_PSCHEME_INTERNAL|PTI_WHOLE_DISK;
786#endif
787			return true;
788		}
789
790		ndx++;
791		if (ndx > parts->dp.num_part || ndx > id)
792			break;
793	}
794
795	return false;
796}
797
798static bool
799disklabel_set_part_info(struct disk_partitions *arg, part_id id,
800    const struct disk_part_info *info, const char **err_msg)
801{
802	struct disklabel_disk_partitions *parts =
803	    (struct disklabel_disk_partitions*)arg;
804	part_id ndx;
805	bool was_inst_target;
806
807	if (dl_types[0].description == NULL)
808		dl_init_types();
809
810	ndx = 0;
811	for (int part = 0; part < parts->l.d_npartitions; part++) {
812		if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
813		    && parts->l.d_partitions[part].p_size == 0)
814			continue;
815
816		if (ndx == id) {
817			was_inst_target = parts->l.d_partitions[part].p_offset
818			    == parts->install_target;
819			parts->l.d_partitions[part].p_offset = info->start;
820			parts->l.d_partitions[part].p_size = info->size;
821			parts->l.d_partitions[part].p_fstype =
822			    dl_part_type_from_generic(info->nat_type);
823			if (info->last_mounted != NULL &&
824			    info->last_mounted != parts->last_mounted[part])
825				strlcpy(parts->last_mounted[part],
826				    info->last_mounted,
827				    sizeof(parts->last_mounted[part]));
828			if (info->flags & PTI_INSTALL_TARGET)
829				parts->install_target = info->start;
830			else if (was_inst_target)
831				parts->install_target = -1;
832			assert(info->fs_type == 0 || info->fs_type ==
833			    parts->l.d_partitions[part].p_fstype);
834			if (info->fs_sub_type != 0)
835				parts->fs_sub_type[part] = info->fs_sub_type;
836			return true;
837		}
838
839		ndx++;
840		if (ndx > parts->dp.num_part || ndx > id)
841			break;
842	}
843
844	return false;
845}
846
847static size_t
848disklabel_get_free_spaces_internal(const struct
849    disklabel_disk_partitions *parts,
850    struct disk_part_free_space *result, size_t max_num_result,
851    daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
852{
853	size_t cnt = 0, i;
854	daddr_t s, e, from, size, end_of_disk;
855
856	if (start < parts->dp.disk_start)
857		start = parts->dp.disk_start;
858	if (min_space_size < 1)
859		min_space_size = 1;
860	if (align > 1 && (start % align) != 0)
861		start = max(roundup(start, align), align);
862	end_of_disk = parts->dp.disk_start + parts->dp.disk_size;
863	from = start;
864	while (from < end_of_disk && cnt < max_num_result) {
865again:
866		size = parts->dp.disk_start + parts->dp.disk_size - from;
867		start = from;
868		for (i = 0; i < parts->l.d_npartitions; i++) {
869			if (i == RAW_PART)
870				continue;
871			if (parts->l.d_partitions[i].p_fstype == FS_UNUSED)
872				continue;
873			if (parts->l.d_partitions[i].p_size == 0)
874				continue;
875
876			s = parts->l.d_partitions[i].p_offset;
877			e = parts->l.d_partitions[i].p_size + s;
878			if (s == ignore)
879				continue;
880			if (e < from)
881				continue;
882			if (s <= from && e > from) {
883				if (e - 1 >= end_of_disk)
884					return cnt;
885
886				from = e + 1;
887				if (align > 1) {
888					from = max(roundup(from, align), align);
889					if (from >= end_of_disk) {
890						size = 0;
891						break;
892					}
893				}
894				goto again;
895			}
896			if (s > from && s - from < size) {
897				size = s - from;
898			}
899		}
900		if (size >= min_space_size) {
901			result->start = start;
902			result->size = size;
903			result++;
904			cnt++;
905		}
906		from += size + 1;
907		if (align > 1)
908			from = max(roundup(from, align), align);
909	}
910
911	return cnt;
912}
913
914static bool
915disklabel_can_add_partition(const struct disk_partitions *arg)
916{
917	const struct disklabel_disk_partitions *parts =
918	    (const struct disklabel_disk_partitions*)arg;
919	struct disk_part_free_space space;
920	int i;
921
922	if (dl_maxpart == 0)
923		dl_maxpart = getmaxpartitions();
924	if (parts->dp.free_space < parts->ptn_alignment)
925		return false;
926	if (parts->dp.num_part >= dl_maxpart)
927		return false;
928	if (disklabel_get_free_spaces_internal(parts, &space, 1,
929	    parts->ptn_alignment, parts->ptn_alignment, 0, -1) < 1)
930		return false;
931
932	for (i = 0; i < parts->l.d_npartitions; i++) {
933		if (i == RAW_PART)
934			continue;
935#if RAW_PART == 3
936		if (i == RAW_PART-1 && parts->dp.parent != NULL)
937			continue;
938#endif
939		if (parts->l.d_partitions[i].p_fstype == FS_UNUSED)
940			return true;
941	}
942	return false;
943}
944
945static bool
946disklabel_get_disk_pack_name(const struct disk_partitions *arg,
947    char *buf, size_t len)
948{
949	const struct disklabel_disk_partitions *parts =
950	    (const struct disklabel_disk_partitions*)arg;
951
952	strlcpy(buf, parts->l.d_packname, min(len,
953	    sizeof(parts->l.d_packname)+1));
954	return true;
955}
956
957static bool
958disklabel_set_disk_pack_name(struct disk_partitions *arg, const char *pack)
959{
960	struct disklabel_disk_partitions *parts =
961	    (struct disklabel_disk_partitions*)arg;
962
963	strncpy(parts->l.d_packname, pack, sizeof(parts->l.d_packname));
964	return true;
965}
966
967static bool
968disklabel_get_part_device(const struct disk_partitions *arg,
969    part_id ptn, char *devname, size_t max_devname_len, int *part,
970    enum dev_name_usage which_name, bool with_path, bool life)
971{
972	const struct disklabel_disk_partitions *parts =
973	    (const struct disklabel_disk_partitions*)arg;
974	part_id id;
975	int part_index;
976	char pname;
977
978	if (ptn >= parts->l.d_npartitions)
979		return false;
980
981	for (id = part_index = 0; part_index < parts->l.d_npartitions;
982	    part_index++) {
983		if (parts->l.d_partitions[part_index].p_fstype == FS_UNUSED &&
984		    parts->l.d_partitions[part_index].p_size == 0)
985			continue;
986		if (id == ptn)
987			break;
988		id++;
989		if (id > ptn)
990			return false;
991	}
992
993	if (part != 0)
994		*part = part_index;
995
996	pname = 'a'+ part_index;
997
998	switch (which_name) {
999	case parent_device_only:
1000		strlcpy(devname, arg->disk, max_devname_len);
1001		return true;
1002	case logical_name:
1003	case plain_name:
1004		if (with_path)
1005			snprintf(devname, max_devname_len, _PATH_DEV "%s%c",
1006			    arg->disk, pname);
1007		else
1008			snprintf(devname, max_devname_len, "%s%c",
1009			    arg->disk, pname);
1010		return true;
1011	case raw_dev_name:
1012		if (with_path)
1013			snprintf(devname, max_devname_len, _PATH_DEV "r%s%c",
1014			    arg->disk, pname);
1015		else
1016			snprintf(devname, max_devname_len, "r%s%c",
1017			    arg->disk, pname);
1018		return true;
1019	}
1020
1021	return false;
1022}
1023
1024/*
1025 * If the requested partition file system type internally skips
1026 * the disk label sector, we can allow it to start at the beginning
1027 * of the disk. In most cases though we have to move the partition
1028 * to start past the label sector.
1029 */
1030static bool
1031need_to_skip_past_label(const struct disk_part_info *info)
1032{
1033	switch (info->fs_type) {
1034	case FS_BSDFFS:
1035	case FS_RAID:
1036		return false;
1037	}
1038
1039	return true;
1040}
1041
1042static part_id
1043disklabel_add_partition(struct disk_partitions *arg,
1044    const struct disk_part_info *info, const char **err_msg)
1045{
1046	struct disklabel_disk_partitions *parts =
1047	    (struct disklabel_disk_partitions*)arg;
1048	int i, part = -1;
1049	part_id new_id;
1050	struct disk_part_free_space space;
1051	struct disk_part_info data = *info;
1052
1053	if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 1,
1054	    data.start, -1) < 1) {
1055		if (err_msg)
1056			*err_msg = msg_string(MSG_No_free_space);
1057		return NO_PART;
1058	}
1059	if (space.start <= (parts->dp.disk_start + LABELSECTOR) &&
1060	    need_to_skip_past_label(&data)) {
1061		daddr_t new_start = roundup(parts->dp.disk_start + LABELSECTOR,
1062		    parts->ptn_alignment);
1063		daddr_t off = new_start - space.start;
1064		space.start += off;
1065		space.size -= off;
1066	}
1067	if (data.size > space.size)
1068		data.size = space.size;
1069	daddr_t dend = data.start+data.size;
1070	if (space.start > data.start)
1071		data.start = space.start;
1072	if (space.start + space.size < dend)
1073		data.size = space.start+space.size-data.start;
1074
1075	if (dl_maxpart == 0)
1076		dl_maxpart = getmaxpartitions();
1077
1078	for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) {
1079		if (parts->l.d_partitions[i].p_size > 0)
1080			new_id++;
1081		if (data.nat_type->generic_ptype != PT_root &&
1082		    data.nat_type->generic_ptype != PT_swap && i < RAW_PART)
1083			continue;
1084		if (i == 0 && data.nat_type->generic_ptype != PT_root)
1085			continue;
1086		if (i == 1 && data.nat_type->generic_ptype != PT_swap)
1087			continue;
1088		if (i == RAW_PART)
1089			continue;
1090#if RAW_PART == 3
1091		if (i == RAW_PART-1 && parts->dp.parent != NULL)
1092			continue;
1093#endif
1094		if (parts->l.d_partitions[i].p_size > 0)
1095			continue;
1096		part = i;
1097		break;
1098	}
1099
1100	if (part < 0) {
1101		if (parts->l.d_npartitions >= dl_maxpart) {
1102			if (err_msg)
1103				*err_msg =
1104				    msg_string(MSG_err_too_many_partitions);
1105			return NO_PART;
1106		}
1107
1108		part = parts->l.d_npartitions++;
1109	}
1110	parts->l.d_partitions[part].p_offset = data.start;
1111	parts->l.d_partitions[part].p_size = data.size;
1112	parts->l.d_partitions[part].p_fstype =
1113	     dl_part_type_from_generic(data.nat_type);
1114	if (data.last_mounted && data.last_mounted[0])
1115		strlcpy(parts->last_mounted[part], data.last_mounted,
1116		    sizeof(parts->last_mounted[part]));
1117	else
1118		parts->last_mounted[part][0] = 0;
1119	parts->fs_sub_type[part] = data.fs_sub_type;
1120	parts->dp.num_part++;
1121	if (data.size <= parts->dp.free_space)
1122		parts->dp.free_space -= data.size;
1123	else
1124		parts->dp.free_space = 0;
1125
1126	return new_id;
1127}
1128
1129static part_id
1130disklabel_add_outer_partition(struct disk_partitions *arg,
1131    const struct disk_part_info *info, const char **err_msg)
1132{
1133	struct disklabel_disk_partitions *parts =
1134	    (struct disklabel_disk_partitions*)arg;
1135	int i, part = -1;
1136	part_id new_id;
1137
1138	if (dl_maxpart == 0)
1139		dl_maxpart = getmaxpartitions();
1140
1141	for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) {
1142		if (parts->l.d_partitions[i].p_size > 0)
1143			new_id++;
1144		if (info->nat_type->generic_ptype != PT_root &&
1145		    info->nat_type->generic_ptype != PT_swap && i < RAW_PART)
1146			continue;
1147		if (i == 0 && info->nat_type->generic_ptype != PT_root)
1148			continue;
1149		if (i == 1 && info->nat_type->generic_ptype != PT_swap)
1150			continue;
1151		if (i == RAW_PART)
1152			continue;
1153#if RAW_PART == 3
1154		if (i == RAW_PART-1 && parts->dp.parent != NULL)
1155			continue;
1156#endif
1157		if (parts->l.d_partitions[i].p_size > 0)
1158			continue;
1159		part = i;
1160		break;
1161	}
1162
1163	if (part < 0) {
1164		if (parts->l.d_npartitions >= dl_maxpart) {
1165			if (err_msg)
1166				*err_msg =
1167				    msg_string(MSG_err_too_many_partitions);
1168			return NO_PART;
1169		}
1170
1171		part = parts->l.d_npartitions++;
1172	}
1173	parts->l.d_partitions[part].p_offset = info->start;
1174	parts->l.d_partitions[part].p_size = info->size;
1175	parts->l.d_partitions[part].p_fstype =
1176	     dl_part_type_from_generic(info->nat_type);
1177	if (info->last_mounted && info->last_mounted[0])
1178		strlcpy(parts->last_mounted[part], info->last_mounted,
1179		    sizeof(parts->last_mounted[part]));
1180	else
1181		parts->last_mounted[part][0] = 0;
1182	parts->fs_sub_type[part] = info->fs_sub_type;
1183	parts->dp.num_part++;
1184
1185	return new_id;
1186}
1187
1188static size_t
1189disklabel_get_free_spaces(const struct disk_partitions *arg,
1190    struct disk_part_free_space *result, size_t max_num_result,
1191    daddr_t min_space_size, daddr_t align, daddr_t start, daddr_t ignore)
1192{
1193	const struct disklabel_disk_partitions *parts =
1194	    (const struct disklabel_disk_partitions*)arg;
1195
1196	return disklabel_get_free_spaces_internal(parts, result,
1197	    max_num_result, min_space_size, align, start, ignore);
1198}
1199
1200static daddr_t
1201disklabel_max_free_space_at(const struct disk_partitions *arg, daddr_t start)
1202{
1203	const struct disklabel_disk_partitions *parts =
1204	    (const struct disklabel_disk_partitions*)arg;
1205	struct disk_part_free_space space;
1206
1207	if (disklabel_get_free_spaces_internal(parts, &space, 1, 1, 0,
1208	    start, start) == 1)
1209		return space.size;
1210
1211	return 0;
1212}
1213
1214static daddr_t
1215disklabel_get_alignment(const struct disk_partitions *arg)
1216{
1217	const struct disklabel_disk_partitions *parts =
1218	    (const struct disklabel_disk_partitions*)arg;
1219
1220	return parts->ptn_alignment;
1221}
1222
1223static part_id
1224disklabel_find_by_name(struct disk_partitions *arg, const char *name)
1225{
1226	const struct disklabel_disk_partitions *parts =
1227	    (const struct disklabel_disk_partitions*)arg;
1228	char *sl, part;
1229	ptrdiff_t n;
1230	part_id pno, id, i;
1231
1232	sl = strrchr(name, '/');
1233	if (sl == NULL)
1234		return NO_PART;
1235	n = sl - name;
1236	if (strncmp(name, parts->l.d_packname, n) != 0)
1237		return NO_PART;
1238	part = name[n+1];
1239	if (part < 'a')
1240		return NO_PART;
1241	pno = part - 'a';
1242	if (pno >= parts->l.d_npartitions)
1243		return NO_PART;
1244	if (parts->l.d_partitions[pno].p_fstype == FS_UNUSED)
1245		return NO_PART;
1246	for (id = 0, i = 0; i < pno; i++)
1247		if (parts->l.d_partitions[i].p_fstype != FS_UNUSED ||
1248		    parts->l.d_partitions[i].p_size != 0)
1249			id++;
1250	return id;
1251}
1252
1253static void
1254disklabel_free(struct disk_partitions *arg)
1255{
1256
1257	assert(arg != NULL);
1258	free(__UNCONST(arg->disk));
1259	free(arg);
1260}
1261
1262static void
1263disklabel_destroy_part_scheme(struct disk_partitions *arg)
1264{
1265
1266	run_program(RUN_SILENT, "disklabel -D %s", arg->disk);
1267	free(arg);
1268}
1269
1270const struct disk_partitioning_scheme
1271disklabel_parts = {
1272	.name = MSG_parttype_disklabel,
1273	.short_name = MSG_parttype_disklabel_short,
1274	.new_type_prompt = MSG_dl_get_custom_fstype,
1275	.size_limit = (daddr_t)UINT32_MAX,
1276	.write_to_disk = disklabel_write_to_disk,
1277	.read_from_disk = disklabel_parts_read,
1278	.create_new_for_disk = disklabel_parts_new,
1279#ifdef NO_DISKLABEL_BOOT
1280	.have_boot_support = disklabel_non_bootable,
1281#endif
1282	.change_disk_geom = disklabel_change_geom,
1283	.get_cylinder_size = disklabel_cylinder_size,
1284	.find_by_name = disklabel_find_by_name,
1285	.get_disk_pack_name = disklabel_get_disk_pack_name,
1286	.set_disk_pack_name = disklabel_set_disk_pack_name,
1287	.delete_all_partitions = disklabel_delete_all,
1288	.delete_partitions_in_range = disklabel_delete_range,
1289	.delete_partition = disklabel_delete,
1290	.get_part_types_count = disklabel_type_count,
1291	.get_part_type = disklabel_get_type,
1292	.get_generic_part_type = disklabel_get_generic_type,
1293	.get_fs_part_type = disklabel_get_fs_part_type,
1294	.get_default_fstype = disklabel_get_default_fstype,
1295	.create_custom_part_type = disklabel_create_custom_part_type,
1296	.create_unknown_part_type = disklabel_create_unknown_part_type,
1297	.get_part_alignment = disklabel_get_alignment,
1298	.adapt_foreign_part_info = generic_adapt_foreign_part_info,
1299	.get_part_info = disklabel_get_part_info,
1300	.can_add_partition = disklabel_can_add_partition,
1301	.set_part_info = disklabel_set_part_info,
1302	.add_partition = disklabel_add_partition,
1303	.add_outer_partition = disklabel_add_outer_partition,
1304	.max_free_space_at = disklabel_max_free_space_at,
1305	.get_free_spaces = disklabel_get_free_spaces,
1306	.get_part_device = disklabel_get_part_device,
1307	.free = disklabel_free,
1308	.destroy_part_scheme = disklabel_destroy_part_scheme,
1309};
1310