Merge from vendor branch NETGRAPH:
[dragonfly.git] / contrib / bsdinstaller-1.1.6 / src / lib / libinstaller / diskutil.c
1 /*
2  * Copyright (c)2004 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 <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include "aura/mem.h"
46 #include "aura/fspred.h"
47 #include "aura/popen.h"
48
49 #include "dfui/dfui.h"
50 #include "dfui/dump.h"
51
52 #define NEEDS_DISKUTIL_STRUCTURE_DEFINITIONS
53 #include "diskutil.h"
54 #undef NEEDS_DISKUTIL_STRUCTURE_DEFINITIONS
55
56 #include "commands.h"
57 #include "functions.h"
58 #include "sysids.h"
59 #include "uiutil.h"
60
61 static int      disk_description_is_better(const char *, const char *);
62
63 /** STORAGE DESCRIPTORS **/
64
65 struct storage *
66 storage_new(void)
67 {
68         struct storage *s;
69
70         AURA_MALLOC(s, storage);
71
72         s->disk_head = NULL;
73         s->disk_tail = NULL;
74         s->selected_disk = NULL;
75         s->selected_slice = NULL;
76         s->ram = -1;
77
78         return(s);
79 }
80
81 int 
82 storage_get_mfs_status(const char *mountpoint, struct storage *s)
83 {
84         struct subpartition *sp;
85         sp = NULL;
86         for (sp = slice_subpartition_first(s->selected_slice); 
87                 sp != NULL; sp = subpartition_next(sp)) {
88                 if(strcmp(subpartition_get_mountpoint(sp), mountpoint) == 0) {
89                         if(subpartition_is_mfsbacked(sp) == 1) {
90                                 return 1;
91                         } else {
92                                 return 0;
93                         }
94                 }
95         }
96         return 0;
97 }
98
99 void
100 storage_free(struct storage *s)
101 {
102         disks_free(s);
103         AURA_FREE(s, storage);
104 }
105
106 void
107 storage_set_memsize(struct storage *s, unsigned long memsize)
108 {
109         s->ram = memsize;
110 }
111
112 unsigned long
113 storage_get_memsize(const struct storage *s)
114 {
115         return(s->ram);
116 }
117
118 struct disk *
119 storage_disk_first(const struct storage *s)
120 {
121         return(s->disk_head);
122 }
123
124 void
125 storage_set_selected_disk(struct storage *s, struct disk *d)
126 {
127         s->selected_disk = d;
128 }
129
130 struct disk *
131 storage_get_selected_disk(const struct storage *s)
132 {
133         return(s->selected_disk);
134 }
135
136 void
137 storage_set_selected_slice(struct storage *s, struct slice *sl)
138 {
139         s->selected_slice = sl;
140 }
141
142 struct slice *
143 storage_get_selected_slice(const struct storage *s)
144 {
145         return(s->selected_slice);
146 }
147
148 /*
149  * Create a new disk description structure.
150  */
151 struct disk *
152 disk_new(struct storage *s, const char *dev_name)
153 {
154         struct disk *d;
155
156         if (disk_find(s, dev_name) != NULL) {
157                 /* Already discovered */
158                 return(NULL);
159         }
160
161         AURA_MALLOC(d, disk);
162
163         d->device = aura_strdup(dev_name);
164         d->desc = NULL;
165         d->we_formatted = 0;
166         d->capacity = 0;
167
168         d->cylinders = -1;      /* -1 indicates "we don't know" */
169         d->heads = -1;
170         d->sectors = -1;
171
172         d->slice_head = NULL;
173         d->slice_tail = NULL;
174
175         d->next = NULL;
176         if (s->disk_head == NULL)
177                 s->disk_head = d;
178         else
179                 s->disk_tail->next = d;
180
181         d->prev = s->disk_tail;
182         s->disk_tail = d;
183
184         return(d);
185 }
186
187 static int
188 disk_description_is_better(const char *existing, const char *new_desc __unused)
189 {
190         if (existing == NULL)
191                 return(1);
192         return(0);
193 }
194
195 const char *
196 disk_get_desc(const struct disk *d)
197 {
198         return(d->desc);
199 }
200
201 void
202 disk_set_desc(struct disk *d, const char *desc)
203 {
204         char *c;
205
206         if (!disk_description_is_better(d->desc, desc))
207                 return;
208         if (d->desc != NULL)
209                 free(d->desc);
210         d->desc = aura_strdup(desc);
211
212         /*
213          * Get the disk's total capacity.
214          * XXX we should do this with C/H/S ?
215          */
216         c = d->desc;
217         while (*c != ':' && *c != '\0')
218                 c++;
219         if (*c == '\0')
220                 d->capacity = 0;
221         else
222                 d->capacity = atoi(c + 1);
223 }
224
225 /*
226  * Returns the name of the device node used to represent the disk.
227  * Note that the storage used for the returned string is static,
228  * and the string is overwritten each time this function is called.
229  */
230 const char *
231 disk_get_device_name(const struct disk *d)
232 {
233         static char tmp_dev_name[256];
234
235         /*
236          * for OpenBSD, this has a "c" suffixed to it.
237          */
238 #ifdef __OpenBSD__
239         snprintf(tmp_dev_name, 256, "%sc", d->device);
240 #else
241         snprintf(tmp_dev_name, 256, "%s", d->device);
242 #endif
243         return(tmp_dev_name);
244 }
245
246 /*
247  * Returns the name of the device node used to represent the
248  * raw disk device.
249  * Note that the storage used for the returned string is static,
250  * and the string is overwritten each time this function is called.
251  */
252 const char *
253 disk_get_raw_device_name(const struct disk *d)
254 {
255         static char tmp_dev_name[256];
256
257         /*
258          * for OpenBSD, this has an "r" prepended to it,
259          * and a "c" suffixed to it.
260          */
261 #ifdef __OpenBSD__
262         snprintf(tmp_dev_name, 256, "r%sc", d->device);
263 #else
264         snprintf(tmp_dev_name, 256, "%s", d->device);
265 #endif
266         return(tmp_dev_name);
267 }
268
269 /*
270  * Find the first disk description structure in the given
271  * storage description which matches the given device name
272  * prefix.  Note that this means that if a storage
273  * description s contains disks named "ad0" and "ad1",
274  * disk_find(s, "ad0s1c") will return a pointer to the disk
275  * structure for "ad0".
276  */
277 struct disk *
278 disk_find(const struct storage *s, const char *device)
279 {
280         struct disk *d = s->disk_head;
281
282         while (d != NULL) {
283                 if (strncmp(device, d->device, strlen(d->device)) == 0)
284                         return(d);
285                 d = d->next;
286         }
287
288         return(NULL);
289 }
290
291 struct disk *
292 disk_next(const struct disk *d)
293 {
294         return(d->next);
295 }
296
297 struct slice *
298 disk_slice_first(const struct disk *d)
299 {
300         return(d->slice_head);
301 }
302
303 void
304 disk_set_formatted(struct disk *d, int formatted)
305 {
306         d->we_formatted = formatted;
307 }
308
309 int
310 disk_get_formatted(const struct disk *d)
311 {
312         return(d->we_formatted);
313 }
314
315 void
316 disk_set_geometry(struct disk *d, int cyl, int hd, int sec)
317 {
318         d->cylinders = cyl;
319         d->heads = hd;
320         d->sectors = sec;
321 }
322
323 void
324 disk_get_geometry(const struct disk *d, int *cyl, int *hd, int *sec)
325 {
326         *cyl = d->cylinders;
327         *hd = d->heads;
328         *sec = d->sectors;
329 }
330
331 /*
332  * Free the memory allocated to hold the set of disk descriptions.
333  */
334 void
335 disks_free(struct storage *s)
336 {
337         struct disk *d = s->disk_head, *next;
338
339         while (d != NULL) {
340                 next = d->next;
341                 slices_free(d->slice_head);
342                 free(d->desc);
343                 free(d->device);
344                 AURA_FREE(d, disk);
345                 d = next;
346         }
347
348         s->disk_head = NULL;
349         s->disk_tail = NULL;
350 }
351
352 /*
353  * Create a new slice description and add it to a disk description.
354  */
355 struct slice *
356 slice_new(struct disk *d, int number, int type, int flags,
357           unsigned long start, unsigned long size)
358 {
359         struct slice *s;
360         const char *sysid_desc = NULL;
361         char unknown[256];
362         int i;
363
364         dfui_debug("** adding slice %d (start %ld, size %ld, sysid %d) "
365             "to disk %s\n", number, start, size, type, d->device);
366
367         AURA_MALLOC(s, slice);
368
369         s->parent = d;
370
371         s->subpartition_head = NULL;
372         s->subpartition_tail = NULL;
373         s->number = number;
374
375         s->type = type;
376         s->flags = flags;
377         s->start = start;
378         s->size = size;
379
380         for (i = 0; ; i++) {
381                 if (part_types[i].type == type) {
382                         sysid_desc = part_types[i].name;
383                         break;
384                 }
385                 if (part_types[i].type == 255)
386                         break;
387         }
388         if (sysid_desc == NULL) {
389                 snprintf(unknown, 256, "??? Unknown, sysid = %d", type);
390                 sysid_desc = unknown;
391         }
392
393         asprintf(&s->desc, "%ldM - %ldM: %s",
394             start / 2048, (start + size) / 2048, sysid_desc);
395         s->capacity = size / 2048;
396
397         s->next = NULL;
398         if (d->slice_head == NULL)
399                 d->slice_head = s;
400         else
401                 d->slice_tail->next = s;
402
403         s->prev = d->slice_tail;
404         d->slice_tail = s;
405
406         return(s);
407 }
408
409 /*
410  * Find a slice description on a given disk description given the
411  * slice number.
412  */
413 struct slice *
414 slice_find(const struct disk *d, int number)
415 {
416         struct slice *s = d->slice_head;
417
418         while (s != NULL) {
419                 if (s->number == number)
420                         return(s);
421                 s = s->next;
422         }
423         
424         return(NULL);
425 }
426
427 struct slice *
428 slice_next(const struct slice *s)
429 {
430         return(s->next);
431 }
432
433 /*
434  * Returns the name of the device node used to represent the slice.
435  * Note that the storage used for the returned string is static,
436  * and the string is overwritten each time this function is called.
437  */
438 const char *
439 slice_get_device_name(const struct slice *s)
440 {
441         static char tmp_dev_name[256];
442
443         /*
444          * XXX for OpenBSD, this appears to be meaningless?
445          * i.e. the number of the current slice is hidden in-core
446          * and not accessible (or needed) from userland.
447          */
448 #ifdef __OpenBSD__
449         snprintf(tmp_dev_name, 256, "%sc", s->parent->device);
450 #else
451         snprintf(tmp_dev_name, 256, "%ss%d", s->parent->device, s->number);
452 #endif
453         return(tmp_dev_name);
454 }
455
456 /*
457  * Returns the name of the device node used to represent
458  * the raw slice.
459  * Note that the storage used for the returned string is static,
460  * and the string is overwritten each time this function is called.
461  */
462 const char *
463 slice_get_raw_device_name(const struct slice *s)
464 {
465         static char tmp_dev_name[256];
466
467         /*
468          * XXX for OpenBSD, this needs an "r" prepended to it.
469          * XXX for OpenBSD, this appears to be meaningless?
470          */
471 #ifdef __OpenBSD__
472         snprintf(tmp_dev_name, 256, "r%sc", s->parent->device);
473 #else
474         snprintf(tmp_dev_name, 256, "%ss%d", s->parent->device, s->number);
475 #endif
476         return(tmp_dev_name);
477 }
478
479 int
480 slice_get_number(const struct slice *s)
481 {
482         return(s->number);
483 }
484
485 const char *
486 slice_get_desc(const struct slice *s)
487 {
488         return(s->desc);
489 }
490
491 unsigned long
492 slice_get_capacity(const struct slice *s)
493 {
494         return(s->capacity);
495 }
496
497 unsigned long
498 slice_get_start(const struct slice *s)
499 {
500         return(s->start);
501 }
502
503 unsigned long
504 slice_get_size(const struct slice *s)
505 {
506         return(s->size);
507 }
508
509 int
510 slice_get_type(const struct slice *s)
511 {
512         return(s->type);
513 }
514
515 int
516 slice_get_flags(const struct slice *s)
517 {
518         return(s->flags);
519 }
520
521 struct subpartition *
522 slice_subpartition_first(const struct slice *s)
523 {
524         return(s->subpartition_head);
525 }
526
527 /*
528  * Free all memory for a list of slice descriptions.
529  */
530 void
531 slices_free(struct slice *head)
532 {
533         struct slice *next;
534
535         while (head != NULL) {
536                 next = head->next;
537                 subpartitions_free(head);
538                 free(head->desc);
539                 AURA_FREE(head, slice);
540                 head = next;
541         }
542 }
543
544 /*
545  * NOTE: arguments to this function are not checked for sanity.
546  *
547  * fsize and/or bsize may both be -1, indicating
548  * "choose a reasonable default."
549  */
550 struct subpartition *
551 subpartition_new(struct slice *s, const char *mountpoint, long capacity,
552                  int softupdates, long fsize, long bsize, int mfsbacked)
553 {
554         struct subpartition *sp;
555
556         AURA_MALLOC(sp, subpartition);
557
558         sp->parent = s;
559
560         if (mfsbacked) {
561                 sp->letter = '@';
562         } else {
563                 struct subpartition *last = s->subpartition_tail;
564                 while (last != NULL && last->mfsbacked) {
565                         last = last->prev;
566                 }
567                 if (last == NULL) {
568                         sp->letter = 'a';
569                 } else if (last->letter == 'b') {
570                         sp->letter = 'd';
571                 } else {
572                         sp->letter = (char)(last->letter + 1);
573                 }
574         }
575
576         sp->mountpoint = aura_strdup(mountpoint);
577         sp->capacity = capacity;
578
579         if (fsize == -1) {
580                 if (sp->capacity < 1024)
581                         sp->fsize = 1024;
582                 else
583                         sp->fsize = 2048;
584         } else {
585                 sp->fsize = fsize;
586         }
587
588         if (bsize == -1) {
589                 if (sp->capacity < 1024)
590                         sp->bsize = 8192;
591                 else
592                         sp->bsize = 16384;
593         } else {
594                 sp->bsize = bsize;
595         }
596
597         if (softupdates == -1) {
598                 if (strcmp(mountpoint, "/") == 0)
599                         sp->softupdates = 0;
600                 else
601                         sp->softupdates = 1;
602         } else {
603                 sp->softupdates = softupdates;
604         }
605
606         sp->mfsbacked = mfsbacked;
607
608         sp->is_swap = 0;
609         if (strcasecmp(mountpoint, "swap") == 0)
610                 sp->is_swap = 1;
611
612         sp->next = NULL;
613         if (s->subpartition_head == NULL)
614                 s->subpartition_head = sp;
615         else
616                 s->subpartition_tail->next = sp;
617
618         sp->prev = s->subpartition_tail;
619         s->subpartition_tail = sp;
620
621         return(sp);
622 }
623
624 /*
625  * Find the subpartition description in the given storage
626  * description whose mountpoint matches the given string exactly.
627  */
628 struct subpartition *
629 subpartition_find(const struct slice *s, const char *fmt, ...)
630 {
631         struct subpartition *sp = s->subpartition_head;
632         char *mountpoint;
633         va_list args;
634
635         va_start(args, fmt);
636         vasprintf(&mountpoint, fmt, args);
637         va_end(args);
638
639         while (sp != NULL) {
640                 if (strcmp(mountpoint, sp->mountpoint) == 0) {
641                         free(mountpoint);
642                         return(sp);
643                 }
644                 sp = sp->next;
645         }
646
647         free(mountpoint);
648         return(NULL);
649 }
650
651 /*
652  * Find the subpartition description in the given storage
653  * description where the given filename would presumably
654  * reside.  This is the subpartition whose mountpoint is
655  * the longest match for the given filename.
656  */
657 struct subpartition *
658 subpartition_of(const struct slice *s, const char *fmt, ...)
659 {
660         struct subpartition *sp = s->subpartition_head;
661         struct subpartition *csp = NULL;
662         size_t len = 0;
663         char *filename;
664         va_list args;
665
666         va_start(args, fmt);
667         vasprintf(&filename, fmt, args);
668         va_end(args);
669
670         while (sp != NULL) {
671                 if (strlen(sp->mountpoint) > len &&
672                     strlen(sp->mountpoint) <= strlen(filename) &&
673                     strncmp(filename, sp->mountpoint, strlen(sp->mountpoint)) == 0) {
674                                 csp = sp;
675                                 len = strlen(csp->mountpoint);
676                 }
677                 sp = sp->next;
678         }
679
680         free(filename);
681         return(csp);
682 }
683
684 struct subpartition *
685 subpartition_find_capacity(const struct slice *s, long capacity)
686 {
687         struct subpartition *sp = s->subpartition_head;
688
689         while (sp != NULL) {
690                 if (sp->capacity == capacity)
691                         return(sp);
692                 sp = sp->next;
693         }
694
695         return(NULL);
696 }
697
698 struct subpartition *
699 subpartition_next(const struct subpartition *sp)
700 {
701         return(sp->next);
702 }
703
704 /*
705  * Returns the name of the device node used to represent
706  * the subpartition.
707  * Note that the storage used for the returned string is static,
708  * and the string is overwritten each time this function is called.
709  */
710 const char *
711 subpartition_get_device_name(const struct subpartition *sp)
712 {
713         static char tmp_dev_name[256];
714
715 #ifdef __OpenBSD__
716         snprintf(tmp_dev_name, 256, "%s%c", sp->parent->parent->device,
717             sp->letter);
718 #else
719         snprintf(tmp_dev_name, 256, "%ss%d%c", sp->parent->parent->device,
720             sp->parent->number, sp->letter);
721 #endif
722         return(tmp_dev_name);
723 }
724
725 /*
726  * Returns the name of the device node used to represent
727  * the raw subpartition.
728  * Note that the storage used for the returned string is static,
729  * and the string is overwritten each time this function is called.
730  */
731 const char *
732 subpartition_get_raw_device_name(const struct subpartition *sp)
733 {
734         static char tmp_dev_name[256];
735
736         /*
737          * for OpenBSD, this has an "r" prepended to it.
738          */
739 #ifdef __OpenBSD__
740         snprintf(tmp_dev_name, 256, "r%s%c", sp->parent->parent->device,
741             sp->letter);
742 #else
743         snprintf(tmp_dev_name, 256, "r%ss%d%c", sp->parent->parent->device,
744             sp->parent->number, sp->letter);
745 #endif
746         return(tmp_dev_name);
747 }
748
749 const char *
750 subpartition_get_mountpoint(const struct subpartition *sp)
751 {
752         return(sp->mountpoint);
753 }
754
755 char
756 subpartition_get_letter(const struct subpartition *sp)
757 {
758         return(sp->letter);
759 }
760
761 unsigned long
762 subpartition_get_fsize(const struct subpartition *sp)
763 {
764         return(sp->fsize);
765 }
766
767 unsigned long
768 subpartition_get_bsize(const struct subpartition *sp)
769 {
770         return(sp->bsize);
771 }
772
773 unsigned long
774 subpartition_get_capacity(const struct subpartition *sp)
775 {
776         return(sp->capacity);
777 }
778
779 int
780 subpartition_is_swap(const struct subpartition *sp)
781 {
782         return(sp->is_swap);
783 }
784
785 int
786 subpartition_is_softupdated(const struct subpartition *sp)
787 {
788         return(sp->softupdates);
789 }
790 int 
791 subpartition_is_mfsbacked(const struct subpartition *sp)
792 {
793         return(sp->mfsbacked);
794 }
795
796 int
797 subpartition_count(const struct slice *s)
798 {
799         struct subpartition *sp = s->subpartition_head;
800         int count = 0;
801
802         while (sp != NULL) {
803                 count++;
804                 sp = sp->next;
805         }
806
807         return(count);
808 }
809
810 void
811 subpartitions_free(struct slice *s)
812 {
813         struct subpartition *sp = s->subpartition_head, *next;
814
815         while (sp != NULL) {
816                 next = sp->next;
817                 free(sp->mountpoint);
818                 AURA_FREE(sp, subpartition);
819                 sp = next;
820         }
821
822         s->subpartition_head = NULL;
823         s->subpartition_tail = NULL;
824 }
825
826 long
827 measure_activated_swap(const struct i_fn_args *a)
828 {
829         FILE *p;
830         char line[256];
831         char *word;
832         long swap = 0;
833
834         if ((p = aura_popen("%s%s -k", "r", a->os_root, cmd_name(a, "SWAPINFO"))) == NULL)
835                 return(0);
836         while (fgets(line, 255, p) != NULL) {
837                 if ((word = strtok(line, " \t")) == NULL)
838                         continue;
839                 if (strcmp(word, "Device") == 0)
840                         continue;
841                 if ((word = strtok(NULL, " \t")) == NULL)
842                         continue;
843                 swap += atol(word);
844         }
845         aura_pclose(p);
846
847         return(swap / 1024);
848 }
849
850 long
851 measure_activated_swap_from_slice(const struct i_fn_args *a,
852     const struct disk *d, const struct slice *s)
853 {
854         FILE *p;
855         char *dev, *word;
856         char line[256];
857         long swap = 0;
858
859         if ((p = aura_popen("%s%s -k", "r", a->os_root, cmd_name(a, "SWAPINFO"))) == NULL)
860                 return(0);
861
862         asprintf(&dev, "/dev/%ss%d", d->device, s->number);
863
864         while (fgets(line, 255, p) != NULL) {
865                 if ((word = strtok(line, " \t")) == NULL)
866                         continue;
867                 if (strcmp(word, "Device") == 0)
868                         continue;
869                 if (strstr(word, dev) != word)
870                         continue;
871                 if ((word = strtok(NULL, " \t")) == NULL)
872                         continue;
873                 swap += atol(word);
874         }
875         aura_pclose(p);
876         free(dev);
877
878         return(swap / 1024);
879 }
880
881 long
882 measure_activated_swap_from_disk(const struct i_fn_args *a,
883                                  const struct disk *d)
884 {
885         struct slice *s;
886         long swap = 0;
887
888         for (s = d->slice_head; s != NULL; s = s->next)
889                 swap += measure_activated_swap_from_slice(a, d, s);
890
891         return(swap);
892 }