1 /*
2  * Copyright (c)2004,2015 The DragonFly Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  *   Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  *
11  *   Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in
13  *   the documentation and/or other materials provided with the
14  *   distribution.
15  *
16  *   Neither the name of the DragonFly Project nor the names of its
17  *   contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * diskutil.c
36  * Disk utility functions for installer.
37  * $Id: diskutil.c,v 1.44 2005/02/07 06:41:42 cpressey Exp $
38  */
39 
40 #include <sys/diskmbr.h>
41 
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 
47 #include "libaura/mem.h"
48 #include "libaura/fspred.h"
49 #include "libaura/popen.h"
50 
51 #include "libdfui/dfui.h"
52 #include "libdfui/dump.h"
53 
54 #define NEEDS_DISKUTIL_STRUCTURE_DEFINITIONS
55 #include "diskutil.h"
56 #undef NEEDS_DISKUTIL_STRUCTURE_DEFINITIONS
57 
58 #include "commands.h"
59 #include "functions.h"
60 #include "uiutil.h"
61 
62 static int	disk_description_is_better(const char *, const char *);
63 
64 /** STORAGE DESCRIPTORS **/
65 
66 struct storage *
67 storage_new(void)
68 {
69 	struct storage *s;
70 
71 	AURA_MALLOC(s, storage);
72 
73 	s->disk_head = NULL;
74 	s->disk_tail = NULL;
75 	s->selected_disk = NULL;
76 	s->selected_slice = NULL;
77 	s->ram = -1;
78 
79 	return(s);
80 }
81 
82 int
83 storage_get_tmpfs_status(const char *mountpoint, struct storage *s)
84 {
85 	struct subpartition *sp;
86 	sp = NULL;
87 	for (sp = slice_subpartition_first(s->selected_slice);
88 		sp != NULL; sp = subpartition_next(sp)) {
89 		if(strcmp(subpartition_get_mountpoint(sp), mountpoint) == 0) {
90 			if(subpartition_is_tmpfsbacked(sp) == 1) {
91 				return 1;
92 			} else {
93 				return 0;
94 			}
95 		}
96 	}
97 	return 0;
98 }
99 
100 void
101 storage_free(struct storage *s)
102 {
103 	disks_free(s);
104 	AURA_FREE(s, storage);
105 }
106 
107 void
108 storage_set_memsize(struct storage *s, unsigned long memsize)
109 {
110 	s->ram = memsize;
111 }
112 
113 long
114 storage_get_memsize(const struct storage *s)
115 {
116 	return(s->ram);
117 }
118 
119 struct disk *
120 storage_disk_first(const struct storage *s)
121 {
122 	return(s->disk_head);
123 }
124 
125 void
126 storage_set_selected_disk(struct storage *s, struct disk *d)
127 {
128 	s->selected_disk = d;
129 }
130 
131 struct disk *
132 storage_get_selected_disk(const struct storage *s)
133 {
134 	return(s->selected_disk);
135 }
136 
137 void
138 storage_set_selected_slice(struct storage *s, struct slice *sl)
139 {
140 	s->selected_slice = sl;
141 }
142 
143 struct slice *
144 storage_get_selected_slice(const struct storage *s)
145 {
146 	return(s->selected_slice);
147 }
148 
149 /*
150  * Create a new disk description structure.
151  */
152 struct disk *
153 disk_new(struct storage *s, const char *dev_name)
154 {
155 	struct disk *d;
156 
157 	AURA_MALLOC(d, disk);
158 
159 	d->device = aura_strdup(dev_name);
160 	d->desc = NULL;
161 	d->serno = NULL;
162 	d->we_formatted = 0;
163 	d->capacity = 0;
164 
165 	d->cylinders = -1;	/* -1 indicates "we don't know" */
166 	d->heads = -1;
167 	d->sectors = -1;
168 
169 	d->slice_head = NULL;
170 	d->slice_tail = NULL;
171 
172 	d->next = NULL;
173 	if (s->disk_head == NULL)
174 		s->disk_head = d;
175 	else
176 		s->disk_tail->next = d;
177 
178 	d->prev = s->disk_tail;
179 	s->disk_tail = d;
180 
181 	return(d);
182 }
183 
184 static int
185 disk_description_is_better(const char *existing, const char *new_desc __unused)
186 {
187 	if (existing == NULL)
188 		return(1);
189 	return(0);
190 }
191 
192 const char *
193 disk_get_desc(const struct disk *d)
194 {
195 	return(d->desc);
196 }
197 
198 unsigned long
199 disk_get_capacity(const struct disk *d)
200 {
201 	return(d->capacity);
202 }
203 
204 
205 void
206 disk_set_desc(struct disk *d, const char *desc)
207 {
208 	char *c;
209 
210 	if (!disk_description_is_better(d->desc, desc))
211 		return;
212 	if (d->desc != NULL)
213 		free(d->desc);
214 	d->desc = aura_strdup(desc);
215 
216 	/*
217 	 * Get the disk's total capacity.
218 	 * XXX we should do this with C/H/S ?
219 	 */
220 	c = d->desc;
221 	while (*c != ':' && *c != '\0')
222 		c++;
223 	if (*c == '\0')
224 		d->capacity = 0;
225 	else
226 		d->capacity = strtoul(c + 1, NULL, 0);
227 }
228 
229 /*
230  * Returns the name of the device node used to represent the disk.
231  * Note that the storage used for the returned string is static,
232  * and the string is overwritten each time this function is called.
233  */
234 const char *
235 disk_get_device_name(const struct disk *d)
236 {
237 	static char tmp_dev_name[256];
238 
239 	snprintf(tmp_dev_name, 256, "%s", d->device);
240 	return(tmp_dev_name);
241 }
242 
243 const char *
244 disk_get_serno(const struct disk *d)
245 {
246 	return(d->serno);
247 }
248 
249 void
250 disk_set_serno(struct disk *d, const char *serno)
251 {
252 	d->serno = aura_strdup(serno);
253 }
254 
255 int
256 disk_get_number(const struct disk *d)
257 {
258 	return(d->number);
259 }
260 
261 void
262 disk_set_number(struct disk *d, const int number)
263 {
264 	d->number = number;
265 }
266 
267 /*
268  * Find the first disk description structure in the given
269  * storage description which matches the given device name
270  * prefix.  Note that this means that if a storage
271  * description s contains disks named "ad0" and "ad1",
272  * disk_find(s, "ad0s1c") will return a pointer to the disk
273  * structure for "ad0".
274  */
275 struct disk *
276 disk_find(const struct storage *s, const char *device)
277 {
278 	struct disk *d = s->disk_head;
279 
280 	while (d != NULL) {
281 		if (strncmp(device, d->device, strlen(d->device)) == 0 &&
282 		    strlen(device) == strlen(d->device))
283 			return(d);
284 		d = d->next;
285 	}
286 
287 	return(NULL);
288 }
289 
290 struct disk *
291 disk_next(const struct disk *d)
292 {
293 	return(d->next);
294 }
295 
296 struct slice *
297 disk_slice_first(const struct disk *d)
298 {
299 	return(d->slice_head);
300 }
301 
302 void
303 disk_set_formatted(struct disk *d, int formatted)
304 {
305 	d->we_formatted = formatted;
306 }
307 
308 int
309 disk_get_formatted(const struct disk *d)
310 {
311 	return(d->we_formatted);
312 }
313 
314 void
315 disk_set_geometry(struct disk *d, int cyl, int hd, int sec)
316 {
317 	d->cylinders = cyl;
318 	d->heads = hd;
319 	d->sectors = sec;
320 }
321 
322 void
323 disk_get_geometry(const struct disk *d, int *cyl, int *hd, int *sec)
324 {
325 	*cyl = d->cylinders;
326 	*hd = d->heads;
327 	*sec = d->sectors;
328 }
329 
330 /*
331  * Free the memory allocated to hold the set of disk descriptions.
332  */
333 void
334 disks_free(struct storage *s)
335 {
336 	struct disk *d = s->disk_head, *next;
337 
338 	while (d != NULL) {
339 		next = d->next;
340 		slices_free(d->slice_head);
341 		free(d->desc);
342 		free(d->device);
343 		AURA_FREE(d, disk);
344 		d = next;
345 	}
346 
347 	s->disk_head = NULL;
348 	s->disk_tail = NULL;
349 }
350 
351 /*
352  * Create a new slice description and add it to a disk description.
353  */
354 struct slice *
355 slice_new(struct disk *d, int number, int type, int flags,
356 	  unsigned long start, unsigned long size)
357 {
358 	struct slice *s;
359 	const char *sysid_desc = NULL;
360 	char unknown[256];
361 	int i;
362 
363 	dfui_debug("** adding slice %d (start %ld, size %ld, sysid %d) "
364 	    "to disk %s\n", number, start, size, type, d->device);
365 
366 	AURA_MALLOC(s, slice);
367 
368 	s->parent = d;
369 
370 	s->subpartition_head = NULL;
371 	s->subpartition_tail = NULL;
372 	s->number = number;
373 
374 	s->type = type;
375 	s->flags = flags;
376 	s->start = start;
377 	s->size = size;
378 
379 	for (i = 0; ; i++) {
380 		if (dos_ptypes[i].type == type) {
381 			sysid_desc = dos_ptypes[i].name;
382 			break;
383 		}
384 		if (dos_ptypes[i].type == 255)
385 			break;
386 	}
387 	if (sysid_desc == NULL) {
388 		snprintf(unknown, 256, "??? Unknown, sysid = %d", type);
389 		sysid_desc = unknown;
390 	}
391 
392 	asprintf(&s->desc, "%ldM - %ldM: %s",
393 	    start / 2048, (start + size) / 2048, sysid_desc);
394 	s->capacity = size / 2048;
395 
396 	s->next = NULL;
397 	if (d->slice_head == NULL)
398 		d->slice_head = s;
399 	else
400 		d->slice_tail->next = s;
401 
402 	s->prev = d->slice_tail;
403 	d->slice_tail = s;
404 
405 	return(s);
406 }
407 
408 /*
409  * Find a slice description on a given disk description given the
410  * slice number.
411  */
412 struct slice *
413 slice_find(const struct disk *d, int number)
414 {
415 	struct slice *s = d->slice_head;
416 
417 	while (s != NULL) {
418 		if (s->number == number)
419 			return(s);
420 		s = s->next;
421 	}
422 
423 	return(NULL);
424 }
425 
426 struct slice *
427 slice_next(const struct slice *s)
428 {
429 	return(s->next);
430 }
431 
432 /*
433  * Returns the name of the device node used to represent the slice.
434  * Note that the storage used for the returned string is static,
435  * and the string is overwritten each time this function is called.
436  */
437 const char *
438 slice_get_device_name(const struct slice *s)
439 {
440 	static char tmp_dev_name[256];
441 
442 	snprintf(tmp_dev_name, 256, "%ss%d", s->parent->device, s->number);
443 	return(tmp_dev_name);
444 }
445 
446 int
447 slice_get_number(const struct slice *s)
448 {
449 	return(s->number);
450 }
451 
452 const char *
453 slice_get_desc(const struct slice *s)
454 {
455 	return(s->desc);
456 }
457 
458 unsigned long
459 slice_get_capacity(const struct slice *s)
460 {
461 	return(s->capacity);
462 }
463 
464 unsigned long
465 slice_get_start(const struct slice *s)
466 {
467 	return(s->start);
468 }
469 
470 unsigned long
471 slice_get_size(const struct slice *s)
472 {
473 	return(s->size);
474 }
475 
476 int
477 slice_get_type(const struct slice *s)
478 {
479 	return(s->type);
480 }
481 
482 int
483 slice_get_flags(const struct slice *s)
484 {
485 	return(s->flags);
486 }
487 
488 struct subpartition *
489 slice_subpartition_first(const struct slice *s)
490 {
491 	return(s->subpartition_head);
492 }
493 
494 /*
495  * Free all memory for a list of slice descriptions.
496  */
497 void
498 slices_free(struct slice *head)
499 {
500 	struct slice *next;
501 
502 	while (head != NULL) {
503 		next = head->next;
504 		subpartitions_free(head);
505 		free(head->desc);
506 		AURA_FREE(head, slice);
507 		head = next;
508 	}
509 }
510 
511 struct subpartition *
512 subpartition_new_hammer(struct slice *s, const char *mountpoint,
513 			long capacity, int encrypted)
514 {
515 	struct subpartition *sp;
516 	struct subpartition *last = s->subpartition_tail;
517 
518 	AURA_MALLOC(sp, subpartition);
519 
520 	sp->parent = s;
521 
522 	if (last == NULL) {
523 		sp->letter = 'a';
524 	} else if (last->letter == 'b') {
525 		sp->letter = 'd';
526 	} else {
527 		sp->letter = (char)(last->letter + 1);
528 	}
529 	if (sp->letter == 'b' && strcmp(mountpoint, "swap") != 0)
530 		sp->letter = 'd';
531 
532 	sp->mountpoint = aura_strdup(mountpoint);
533 	sp->capacity = capacity;
534 	sp->encrypted = encrypted;
535 	sp->type = FS_HAMMER;
536 
537 	/*
538 	 * We need this here, because a UFS /boot needs valid values
539 	 */
540 	if (sp->capacity < 1024)
541 		sp->fsize = 1024;
542 	else
543 		sp->fsize = 2048;
544 
545 	if (sp->capacity < 1024)
546 		sp->bsize = 8192;
547 	else
548 		sp->bsize = 16384;
549 
550 	sp->is_swap = 0;
551 #if 0
552 	sp->pfs = 0;
553 #endif
554 	if (strcasecmp(mountpoint, "swap") == 0)
555 		sp->is_swap = 1;
556 #if 0
557 	if (strcmp(mountpoint, "/") != 0 && strcmp(mountpoint, "/boot") != 0 &&
558 	    strcmp(mountpoint, "swap") != 0)
559 		sp->pfs = 1;
560 #endif
561 
562 	sp->next = NULL;
563 	if (s->subpartition_head == NULL)
564 		s->subpartition_head = sp;
565 	else
566 		s->subpartition_tail->next = sp;
567 
568 	sp->prev = s->subpartition_tail;
569 	s->subpartition_tail = sp;
570 
571 	return(sp);
572 }
573 
574 struct subpartition *
575 subpartition_new_hammer2(struct slice *s, const char *mountpoint,
576 			 long capacity, int encrypted)
577 {
578 	struct subpartition *sp;
579 	struct subpartition *last = s->subpartition_tail;
580 
581 	AURA_MALLOC(sp, subpartition);
582 
583 	sp->parent = s;
584 
585 	if (last == NULL) {
586 		sp->letter = 'a';
587 	} else if (last->letter == 'b') {
588 		sp->letter = 'd';
589 	} else {
590 		sp->letter = (char)(last->letter + 1);
591 	}
592 	if (sp->letter == 'b' && strcmp(mountpoint, "swap") != 0)
593 		sp->letter = 'd';
594 
595 	sp->mountpoint = aura_strdup(mountpoint);
596 	sp->capacity = capacity;
597 	sp->encrypted = encrypted;
598 	sp->type = FS_HAMMER2;
599 
600 	/*
601 	 * We need this here, because a UFS /boot needs valid values
602 	 */
603 	if (sp->capacity < 1024)
604 		sp->fsize = 1024;
605 	else
606 		sp->fsize = 2048;
607 
608 	if (sp->capacity < 1024)
609 		sp->bsize = 8192;
610 	else
611 		sp->bsize = 16384;
612 
613 	sp->is_swap = 0;
614 #if 0
615 	sp->pfs = 0;
616 #endif
617 	if (strcasecmp(mountpoint, "swap") == 0)
618 		sp->is_swap = 1;
619 #if 0
620 	if (strcmp(mountpoint, "/") != 0 && strcmp(mountpoint, "/boot") != 0 &&
621 	    strcmp(mountpoint, "swap") != 0)
622 		sp->pfs = 1;
623 #endif
624 
625 	sp->next = NULL;
626 	if (s->subpartition_head == NULL)
627 		s->subpartition_head = sp;
628 	else
629 		s->subpartition_tail->next = sp;
630 
631 	sp->prev = s->subpartition_tail;
632 	s->subpartition_tail = sp;
633 
634 	return(sp);
635 }
636 
637 /*
638  * NOTE: arguments to this function are not checked for sanity.
639  *
640  * fsize and/or bsize may both be -1, indicating
641  * "choose a reasonable default."
642  */
643 struct subpartition *
644 subpartition_new_ufs(struct slice *s, const char *mountpoint, long capacity,
645     int encrypted, int softupdates, long fsize, long bsize, int tmpfsbacked)
646 {
647 	struct subpartition *sp;
648 	struct subpartition *last = s->subpartition_tail;
649 
650 	AURA_MALLOC(sp, subpartition);
651 
652 	if (tmpfsbacked) {
653 		sp->letter = '@';
654 	} else {
655 		while (last && last->letter == '@')
656 			last = last->prev;
657 		if (last == NULL) {
658 			sp->letter = 'a';
659 		} else if (last->letter == 'b') {
660 			sp->letter = 'd';
661 		} else {
662 			sp->letter = (char)(last->letter + 1);
663 		}
664 		if (sp->letter == 'b' && strcmp(mountpoint, "swap") != 0)
665 			sp->letter = 'd';
666 	}
667 
668 	sp->parent = s;
669 
670 	sp->mountpoint = aura_strdup(mountpoint);
671 	sp->capacity = capacity;
672 	sp->encrypted = encrypted;
673 	sp->type = FS_UFS;
674 
675 	if (fsize == -1) {
676 		if (sp->capacity < 1024)
677 			sp->fsize = 1024;
678 		else
679 			sp->fsize = 2048;
680 	} else {
681 		sp->fsize = fsize;
682 	}
683 
684 	if (bsize == -1) {
685 		if (sp->capacity < 1024)
686 			sp->bsize = 8192;
687 		else
688 			sp->bsize = 16384;
689 	} else {
690 		sp->bsize = bsize;
691 	}
692 
693 	if (softupdates == -1) {
694 		if (strcmp(mountpoint, "/") == 0)
695 			sp->softupdates = 0;
696 		else
697 			sp->softupdates = 1;
698 	} else {
699 		sp->softupdates = softupdates;
700 	}
701 
702 	sp->tmpfsbacked = tmpfsbacked;
703 
704 	sp->is_swap = 0;
705 	if (strcasecmp(mountpoint, "swap") == 0)
706 		sp->is_swap = 1;
707 
708 	/*
709 	 * install
710 	 */
711 	sp->next = NULL;
712 	if (s->subpartition_head == NULL)
713 		s->subpartition_head = sp;
714 	else
715 		s->subpartition_tail->next = sp;
716 
717 	sp->prev = s->subpartition_tail;
718 	s->subpartition_tail = sp;
719 
720 #if 0
721 
722 	for (sptmp = s->subpartition_head; sptmp != NULL;
723 	     sptmp = sptmp->next) {
724 		if (sptmp->tmpfsbacked)
725 			sptmp->letter = '@';
726 		else if (strcmp(sptmp->mountpoint, "/") == 0 ||
727 			 strcmp(sptmp->mountpoint, "/dummy") == 0)
728 			sptmp->letter = 'a';
729 		else if (strcasecmp(sptmp->mountpoint, "swap") == 0)
730 			sptmp->letter = 'b';
731 		else
732 			sptmp->letter = letter++;
733 	}
734 #endif
735 
736 	return(sp);
737 }
738 
739 /*
740  * Find the subpartition description in the given storage
741  * description whose mountpoint matches the given string exactly.
742  */
743 struct subpartition *
744 subpartition_find(const struct slice *s, const char *fmt, ...)
745 {
746 	struct subpartition *sp = s->subpartition_head;
747 	char *mountpoint;
748 	va_list args;
749 
750 	va_start(args, fmt);
751 	vasprintf(&mountpoint, fmt, args);
752 	va_end(args);
753 
754 	while (sp != NULL) {
755 		if (strcmp(mountpoint, sp->mountpoint) == 0) {
756 			free(mountpoint);
757 			return(sp);
758 		}
759 		sp = sp->next;
760 	}
761 
762 	free(mountpoint);
763 	return(NULL);
764 }
765 
766 /*
767  * Find the subpartition description in the given storage
768  * description where the given filename would presumably
769  * reside.  This is the subpartition whose mountpoint is
770  * the longest match for the given filename.
771  */
772 struct subpartition *
773 subpartition_of(const struct slice *s, const char *fmt, ...)
774 {
775 	struct subpartition *sp = s->subpartition_head;
776 	struct subpartition *csp = NULL;
777 	size_t len = 0;
778 	char *filename;
779 	va_list args;
780 
781 	va_start(args, fmt);
782 	vasprintf(&filename, fmt, args);
783 	va_end(args);
784 
785 	while (sp != NULL) {
786 		if (strlen(sp->mountpoint) > len &&
787 		    strlen(sp->mountpoint) <= strlen(filename) &&
788 		    strncmp(filename, sp->mountpoint, strlen(sp->mountpoint)) == 0) {
789 				csp = sp;
790 				len = strlen(csp->mountpoint);
791 		}
792 		sp = sp->next;
793 	}
794 
795 	free(filename);
796 	return(csp);
797 }
798 
799 struct subpartition *
800 subpartition_find_capacity(const struct slice *s, long capacity)
801 {
802 	struct subpartition *sp = s->subpartition_head;
803 
804 	while (sp != NULL) {
805 		if (sp->capacity == capacity)
806 			return(sp);
807 		sp = sp->next;
808 	}
809 
810 	return(NULL);
811 }
812 
813 struct subpartition *
814 subpartition_next(const struct subpartition *sp)
815 {
816 	return(sp->next);
817 }
818 
819 int
820 subpartition_get_pfs(const struct subpartition *sp)
821 {
822 	return(sp->pfs);
823 }
824 
825 /*
826  * Returns the name of the device node used to represent
827  * the subpartition, either by serial number or traditional style.
828  * Note that the storage used for the returned string is static,
829  * and the string is overwritten each time this function is called.
830  */
831 const char *
832 subpartition_get_device_name(const struct subpartition *sp)
833 {
834 	static char tmp_dev_name[256];
835 
836 	if (sp->parent->parent->serno != NULL)
837 		snprintf(tmp_dev_name, 256, "serno/%s.s%d%c",
838 		    sp->parent->parent->serno, sp->parent->number, sp->letter);
839 	else
840 		snprintf(tmp_dev_name, 256, "%ss%d%c",
841 		    sp->parent->parent->device, sp->parent->number, sp->letter);
842 	return(tmp_dev_name);
843 }
844 
845 /*
846  * /dev/mapper/
847  *
848  * (result is persistant until next call)
849  */
850 const char *
851 subpartition_get_mapper_name(const struct subpartition *sp, int withdev)
852 {
853 	const char *src;
854 	static char *save;
855 
856 	src = strrchr(sp->mountpoint, '/');
857 	if (src == NULL || src[1] == 0)
858 		src = "root";
859 	else
860 		++src;
861 
862 	if (save)
863 		free(save);
864 	switch(withdev) {
865 	case -1:
866 		asprintf(&save, "%s", src);
867 		break;
868 	case 0:
869 		asprintf(&save, "mapper/%s", src);
870 		break;
871 	case 1:
872 	default:
873 		asprintf(&save, "/dev/mapper/%s", src);
874 		break;
875 	}
876 	return save;
877 }
878 
879 const char *
880 subpartition_get_mountpoint(const struct subpartition *sp)
881 {
882 	return(sp->mountpoint);
883 }
884 
885 char
886 subpartition_get_letter(const struct subpartition *sp)
887 {
888 	return(sp->letter);
889 }
890 
891 unsigned long
892 subpartition_get_fsize(const struct subpartition *sp)
893 {
894 	return(sp->fsize);
895 }
896 
897 unsigned long
898 subpartition_get_bsize(const struct subpartition *sp)
899 {
900 	return(sp->bsize);
901 }
902 
903 long
904 subpartition_get_capacity(const struct subpartition *sp)
905 {
906 	return(sp->capacity);
907 }
908 
909 void
910 subpartition_clr_encrypted(struct subpartition *sp)
911 {
912 	sp->encrypted = 0;
913 }
914 
915 int
916 subpartition_is_encrypted(const struct subpartition *sp)
917 {
918 	return(sp->encrypted);
919 }
920 
921 int
922 subpartition_is_swap(const struct subpartition *sp)
923 {
924 	return(sp->is_swap);
925 }
926 
927 int
928 subpartition_is_softupdated(const struct subpartition *sp)
929 {
930 	return(sp->softupdates);
931 }
932 int
933 subpartition_is_tmpfsbacked(const struct subpartition *sp)
934 {
935 	return(sp->tmpfsbacked);
936 }
937 
938 int
939 subpartition_count(const struct slice *s)
940 {
941 	struct subpartition *sp = s->subpartition_head;
942 	int count = 0;
943 
944 	while (sp != NULL) {
945 		count++;
946 		sp = sp->next;
947 	}
948 
949 	return(count);
950 }
951 
952 void
953 subpartitions_free(struct slice *s)
954 {
955 	struct subpartition *sp = s->subpartition_head, *next;
956 
957 	while (sp != NULL) {
958 		next = sp->next;
959 		free(sp->mountpoint);
960 		AURA_FREE(sp, subpartition);
961 		sp = next;
962 	}
963 
964 	s->subpartition_head = NULL;
965 	s->subpartition_tail = NULL;
966 }
967 
968 long
969 measure_activated_swap(const struct i_fn_args *a)
970 {
971 	FILE *p;
972 	char line[256];
973 	char *word;
974 	long swap = 0;
975 
976 	if ((p = aura_popen("%s%s -k", "r", a->os_root, cmd_name(a, "SWAPINFO"))) == NULL)
977 		return(0);
978 	while (fgets(line, 255, p) != NULL) {
979 		if ((word = strtok(line, " \t")) == NULL)
980 			continue;
981 		if (strcmp(word, "Device") == 0)
982 			continue;
983 		if ((word = strtok(NULL, " \t")) == NULL)
984 			continue;
985 		swap += atol(word);
986 	}
987 	aura_pclose(p);
988 
989 	return(swap / 1024);
990 }
991 
992 long
993 measure_activated_swap_from_slice(const struct i_fn_args *a,
994     const struct disk *d, const struct slice *s)
995 {
996 	FILE *p;
997 	char *dev, *word;
998 	char line[256];
999 	long swap = 0;
1000 
1001 	if ((p = aura_popen("%s%s -k", "r", a->os_root, cmd_name(a, "SWAPINFO"))) == NULL)
1002 		return(0);
1003 
1004 	asprintf(&dev, "/dev/%ss%d", d->device, s->number);
1005 
1006 	while (fgets(line, 255, p) != NULL) {
1007 		if ((word = strtok(line, " \t")) == NULL)
1008 			continue;
1009 		if (strcmp(word, "Device") == 0)
1010 			continue;
1011 		if (strstr(word, dev) != word)
1012 			continue;
1013 		if ((word = strtok(NULL, " \t")) == NULL)
1014 			continue;
1015 		swap += atol(word);
1016 	}
1017 	aura_pclose(p);
1018 	free(dev);
1019 
1020 	return(swap / 1024);
1021 }
1022 
1023 long
1024 measure_activated_swap_from_disk(const struct i_fn_args *a,
1025 				 const struct disk *d)
1026 {
1027 	struct slice *s;
1028 	long swap = 0;
1029 
1030 	for (s = d->slice_head; s != NULL; s = s->next)
1031 		swap += measure_activated_swap_from_slice(a, d, s);
1032 
1033 	return(swap);
1034 }
1035 
1036 void *
1037 swapoff_all(const struct i_fn_args *a)
1038 {
1039 	FILE *p;
1040 
1041 	if ((p = aura_popen("%s%s off; %s%s | %s%s \"^/dev\" | %s%s '{print $1;}' | %s%s %s%s", "r",
1042 		    a->os_root, cmd_name(a, "DUMPON"),
1043 		    a->os_root, cmd_name(a, "SWAPINFO"),
1044 		    a->os_root, cmd_name(a, "GREP"),
1045 		    a->os_root, cmd_name(a, "AWK"),
1046 		    a->os_root, cmd_name(a, "XARGS"),
1047 		    a->os_root, cmd_name(a, "SWAPOFF"))) != NULL)
1048 		aura_pclose(p);
1049 
1050 	return(p);
1051 }
1052 
1053 void *
1054 remove_all_mappings(const struct i_fn_args *a)
1055 {
1056 	FILE *p;
1057 
1058 	if ((p = aura_popen("%s%s -1 /dev/mapper | %s%s -vw control | %s%s -n 1 %s%s luksClose", "r",
1059 		    a->os_root, cmd_name(a, "LS"),
1060 		    a->os_root, cmd_name(a, "GREP"),
1061 		    a->os_root, cmd_name(a, "XARGS"),
1062 		    a->os_root, cmd_name(a, "CRYPTSETUP"))) != NULL)
1063 		aura_pclose(p);
1064 
1065 	return(p);
1066 }
1067