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