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