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