]> git.ipfire.org Git - ipfire-2.x.git/blame - src/installer/hw.c
installer: Make hw_mkdir static
[ipfire-2.x.git] / src / installer / hw.c
CommitLineData
f0fa1795
MT
1/*#############################################################################
2# #
3# IPFire - An Open Source Firewall Distribution #
e404dab5 4# Copyright (C) 2007-2022 IPFire Team <info@ipfire.org> #
f0fa1795
MT
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
25fcce25
MT
21#ifndef _GNU_SOURCE
22#define _GNU_SOURCE
23#endif
24
f0fa1795 25#include <assert.h>
d7dd283b 26#include <blkid/blkid.h>
ade96ba8 27#include <errno.h>
d7dd283b 28#include <fcntl.h>
f0fa1795 29#include <libudev.h>
c0511f3a 30#include <linux/loop.h>
d7dd283b 31#include <math.h>
f0fa1795
MT
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
d7dd283b 35#include <sys/ioctl.h>
f0fa1795 36#include <sys/mount.h>
c0511f3a 37#include <sys/stat.h>
25fcce25 38#include <sys/swap.h>
5be66d81 39#include <sys/sysinfo.h>
92e78233 40#include <sys/utsname.h>
f0fa1795
MT
41#include <unistd.h>
42
5315fae6
MT
43#include <libsmooth.h>
44
f0fa1795 45#include "hw.h"
25fcce25 46
8e3b022a
SS
47extern FILE* flog;
48
46b56e20 49static int system_chroot(const char* output, const char* path, const char* cmd) {
f5007e9c
MT
50 char chroot_cmd[STRING_SIZE];
51
52 snprintf(chroot_cmd, sizeof(chroot_cmd), "/usr/sbin/chroot %s %s", path, cmd);
53
46b56e20 54 return mysystem(output, chroot_cmd);
f5007e9c
MT
55}
56
0e0346cc
MT
57static int hw_mkdir(const char *dir) {
58 char tmp[STRING_SIZE];
59 char *p = NULL;
60 size_t len;
61 int r;
62
63 snprintf(tmp, sizeof(tmp),"%s",dir);
64 len = strlen(tmp);
65
66 if (tmp[len - 1] == '/') {
67 tmp[len - 1] = 0;
68 }
69
70 for (p = tmp + 1; *p; p++) {
71 if (*p == '/') {
72 *p = 0;
73
74 // Create target if it does not exist
75 if (access(tmp, X_OK) != 0) {
76 r = mkdir(tmp, S_IRWXU|S_IRWXG|S_IRWXO);
77
78 if (r) {
79 return r;
80 }
81 }
82
83 *p = '/';
84 }
85 }
86
87 // Create target if it does not exist
88 if (access(tmp, X_OK) != 0) {
89 r = mkdir(tmp, S_IRWXU|S_IRWXG|S_IRWXO);
90
91 if (r) {
92 return r;
93 }
94 }
95
96 return 0;
97}
98
f0fa1795 99struct hw* hw_init() {
5fb499f1 100 struct hw* hw = calloc(1, sizeof(*hw));
f0fa1795
MT
101 assert(hw);
102
103 // Initialize libudev
104 hw->udev = udev_new();
105 if (!hw->udev) {
106 fprintf(stderr, "Could not create udev instance\n");
107 exit(1);
108 }
109
92e78233
MT
110 // What architecture are we running on?
111 struct utsname uname_data;
112 int ret = uname(&uname_data);
113 if (ret == 0)
114 snprintf(hw->arch, sizeof(hw->arch), "%s", uname_data.machine);
115
8fbadfc7 116 // Should we install in EFI mode?
7dc7880d
MT
117 if ((strcmp(hw->arch, "x86_64") == 0) || (strcmp(hw->arch, "aarch64") == 0))
118 hw->efi = 1;
5fb499f1 119
f0fa1795
MT
120 return hw;
121}
122
123void hw_free(struct hw* hw) {
124 if (hw->udev)
125 udev_unref(hw->udev);
126
127 free(hw);
128}
129
130static int strstartswith(const char* a, const char* b) {
131 return (strncmp(a, b, strlen(b)) == 0);
132}
133
c0511f3a
MT
134static char loop_device[STRING_SIZE];
135
136static int setup_loop_device(const char* source, const char* device) {
137 int file_fd = open(source, O_RDWR);
138 if (file_fd < 0)
139 goto ERROR;
140
141 int device_fd = -1;
142 if ((device_fd = open(device, O_RDWR)) < 0)
143 goto ERROR;
144
145 if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0)
146 goto ERROR;
147
148 close(file_fd);
149 close(device_fd);
150
151 return 0;
152
153ERROR:
154 if (file_fd >= 0)
155 close(file_fd);
156
157 if (device_fd >= 0) {
158 ioctl(device_fd, LOOP_CLR_FD, 0);
159 close(device_fd);
160 }
161
162 return -1;
163}
164
25fcce25 165int hw_mount(const char* source, const char* target, const char* fs, int flags) {
c0511f3a
MT
166 const char* loop_device = "/dev/loop0";
167
85f2892b
MT
168 // Create target if it does not exist
169 if (access(target, X_OK) != 0)
170 mkdir(target, S_IRWXU|S_IRWXG|S_IRWXO);
171
c0511f3a
MT
172 struct stat st;
173 stat(source, &st);
174
175 if (S_ISREG(st.st_mode)) {
176 int r = setup_loop_device(source, loop_device);
177 if (r == 0) {
178 source = loop_device;
179 } else {
180 return -1;
181 }
182 }
183
366b40c7 184 return mount(source, target, fs, flags, NULL);
f0fa1795
MT
185}
186
ced15fdf
MT
187static int hw_bind_mount(const char* source, const char* prefix) {
188 if (!source || !prefix) {
189 errno = EINVAL;
190 return 1;
191 }
192
193 char target[PATH_MAX];
194 int r;
195
196 // Format target
197 r = snprintf(target, sizeof(target) - 1, "%s/%s", prefix, source);
198 if (r < 0)
199 return 1;
200
201 // Ensure target exists
202 mkdir(target, S_IRWXU|S_IRWXG|S_IRWXO);
ade96ba8 203
ced15fdf
MT
204 return hw_mount(source, target, NULL, MS_BIND);
205}
206
207int hw_umount(const char* source, const char* prefix) {
208 char target[PATH_MAX];
209 int r;
ade96ba8 210
ced15fdf
MT
211 if (prefix)
212 r = snprintf(target, sizeof(target) - 1, "%s/%s", prefix, source);
213 else
214 r = snprintf(target, sizeof(target) - 1, "%s", source);
215 if (r < 0)
216 return r;
217
218 // Perform umount
219 r = umount2(target, 0);
220 if (r) {
221 switch (errno) {
222 // Try again with force if umount wasn't successful
223 case EBUSY:
224 sleep(1);
225
226 r = umount2(target, MNT_FORCE);
227 break;
228
229 // target wasn't a mountpoint. Ignore.
230 case EINVAL:
231 r = 0;
232 break;
91588cb4
MT
233
234 // target doesn't exist
235 case ENOENT:
236 r = 0;
237 break;
ced15fdf 238 }
ade96ba8
MT
239 }
240
241 return r;
f0fa1795
MT
242}
243
244static int hw_test_source_medium(const char* path) {
25fcce25 245 int ret = hw_mount(path, SOURCE_MOUNT_PATH, "iso9660", MS_RDONLY);
f0fa1795 246
68a50dd1
AF
247 if (ret != 0) {
248 // 2nd try, ntfs for a rufus converted usb key
249 ret = hw_mount(path, SOURCE_MOUNT_PATH, "ntfs3", MS_RDONLY);
250 }
251 if (ret != 0) {
252 // 3rd try, vfat for a rufus converted usb key
253 ret = hw_mount(path, SOURCE_MOUNT_PATH, "vfat", MS_RDONLY);
254 }
255
f0fa1795
MT
256 // If the source could not be mounted we
257 // cannot proceed.
5315fae6 258 if (ret != 0)
f0fa1795
MT
259 return ret;
260
261 // Check if the test file exists.
5315fae6 262 ret = access(SOURCE_TEST_FILE, R_OK);
f0fa1795
MT
263
264 // Umount the test device.
ced15fdf 265 hw_umount(SOURCE_MOUNT_PATH, NULL);
f0fa1795 266
fb76fc51 267 return ret;
f0fa1795
MT
268}
269
270char* hw_find_source_medium(struct hw* hw) {
271 char* ret = NULL;
272
273 struct udev_enumerate* enumerate = udev_enumerate_new(hw->udev);
274
275 udev_enumerate_add_match_subsystem(enumerate, "block");
276 udev_enumerate_scan_devices(enumerate);
277
278 struct udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
279
280 struct udev_list_entry* dev_list_entry;
281 udev_list_entry_foreach(dev_list_entry, devices) {
282 const char* path = udev_list_entry_get_name(dev_list_entry);
283 struct udev_device* dev = udev_device_new_from_syspath(hw->udev, path);
284
285 const char* dev_path = udev_device_get_devnode(dev);
286
287 // Skip everything what we cannot work with
288 if (strstartswith(dev_path, "/dev/loop") || strstartswith(dev_path, "/dev/fd") ||
4a0d9bef 289 strstartswith(dev_path, "/dev/ram") || strstartswith(dev_path, "/dev/md"))
f0fa1795
MT
290 continue;
291
5315fae6 292 if (hw_test_source_medium(dev_path) == 0) {
f0fa1795
MT
293 ret = strdup(dev_path);
294 }
295
296 udev_device_unref(dev);
297
298 // If a suitable device was found the search will end.
299 if (ret)
300 break;
301 }
302
303 udev_enumerate_unref(enumerate);
304
305 return ret;
306}
d7dd283b
MT
307
308static struct hw_disk** hw_create_disks() {
309 struct hw_disk** ret = malloc(sizeof(*ret) * (HW_MAX_DISKS + 1));
310
311 return ret;
312}
313
314static unsigned long long hw_block_device_get_size(const char* dev) {
315 int fd = open(dev, O_RDONLY);
316 if (fd < 0)
317 return 0;
318
319 unsigned long long size = blkid_get_dev_size(fd);
320 close(fd);
321
322 return size;
323}
324
ee00d203 325struct hw_disk** hw_find_disks(struct hw* hw, const char* sourcedrive) {
d7dd283b
MT
326 struct hw_disk** ret = hw_create_disks();
327 struct hw_disk** disks = ret;
328
68a50dd1 329 // Determine the disk device of source if it is a partition
16640487 330 const char* sourcedisk = NULL;
68a50dd1
AF
331 char syssource[PATH_MAX];
332 (void)snprintf(syssource, sizeof(syssource) - 1, "/sys/class/block/%s", sourcedrive + 5);
333 struct udev_device* s_dev = udev_device_new_from_syspath(hw->udev, syssource);
334 const char* s_devtype = udev_device_get_property_value(s_dev, "DEVTYPE");
335 if (s_devtype && (strcmp(s_devtype, "partition") == 0)) {
336 struct udev_device* p_dev = udev_device_get_parent_with_subsystem_devtype(s_dev,"block","disk");
337 if (p_dev) {
338 sourcedisk = udev_device_get_devnode(p_dev);
339 }
340 }
341 if (!sourcedisk) sourcedisk = sourcedrive;
342
d7dd283b
MT
343 struct udev_enumerate* enumerate = udev_enumerate_new(hw->udev);
344
345 udev_enumerate_add_match_subsystem(enumerate, "block");
346 udev_enumerate_scan_devices(enumerate);
347
348 struct udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
349
350 struct udev_list_entry* dev_list_entry;
351 unsigned int i = HW_MAX_DISKS;
352 udev_list_entry_foreach(dev_list_entry, devices) {
353 const char* path = udev_list_entry_get_name(dev_list_entry);
354 struct udev_device* dev = udev_device_new_from_syspath(hw->udev, path);
355
356 const char* dev_path = udev_device_get_devnode(dev);
357
358 // Skip everything what we cannot work with
359 if (strstartswith(dev_path, "/dev/loop") || strstartswith(dev_path, "/dev/fd") ||
4a0d9bef
MT
360 strstartswith(dev_path, "/dev/ram") || strstartswith(dev_path, "/dev/sr") ||
361 strstartswith(dev_path, "/dev/md")) {
d7dd283b
MT
362 udev_device_unref(dev);
363 continue;
364 }
365
68a50dd1
AF
366 // Skip sourcedisk if we need to
367 if (sourcedisk && (strcmp(dev_path, sourcedisk) == 0)) {
d7dd283b
MT
368 udev_device_unref(dev);
369 continue;
370 }
371
ee00d203
MT
372 // DEVTYPE must be disk (otherwise we will see all sorts of partitions here)
373 const char* devtype = udev_device_get_property_value(dev, "DEVTYPE");
374 if (devtype && (strcmp(devtype, "disk") != 0)) {
d7dd283b
MT
375 udev_device_unref(dev);
376 continue;
377 }
378
379 // Skip devices with a size of zero
380 unsigned long long size = hw_block_device_get_size(dev_path);
381 if (size == 0) {
382 udev_device_unref(dev);
383 continue;
384 }
385
386 struct hw_disk* disk = malloc(sizeof(*disk));
387 if (disk == NULL)
388 return NULL;
389
390 disk->ref = 1;
391
392 strncpy(disk->path, dev_path, sizeof(disk->path));
0c8b2d99 393 const char* p = disk->path + 5;
d7dd283b
MT
394
395 disk->size = size;
396
397 // Vendor
398 const char* vendor = udev_device_get_property_value(dev, "ID_VENDOR");
399 if (!vendor)
400 vendor = udev_device_get_sysattr_value(dev, "vendor");
401 if (!vendor)
402 vendor = udev_device_get_sysattr_value(dev, "manufacturer");
d7dd283b 403
4c812463
MT
404 if (vendor)
405 strncpy(disk->vendor, vendor, sizeof(disk->vendor));
406 else
407 *disk->vendor = '\0';
d7dd283b
MT
408
409 // Model
410 const char* model = udev_device_get_property_value(dev, "ID_MODEL");
411 if (!model)
412 model = udev_device_get_sysattr_value(dev, "model");
413 if (!model)
414 model = udev_device_get_sysattr_value(dev, "product");
d7dd283b 415
4c812463
MT
416 if (model)
417 strncpy(disk->model, model, sizeof(disk->model));
418 else
419 *disk->model = '\0';
420
421 // Format description
422 char size_str[STRING_SIZE];
423 snprintf(size_str, sizeof(size_str), "%4.1fGB", (double)disk->size / pow(1024, 3));
d7dd283b 424
4c812463
MT
425 if (*disk->vendor && *disk->model) {
426 snprintf(disk->description, sizeof(disk->description),
0c8b2d99 427 "%s - %s - %s - %s", size_str, p, disk->vendor, disk->model);
4c812463
MT
428
429 } else if (*disk->vendor || *disk->model) {
430 snprintf(disk->description, sizeof(disk->description),
0c8b2d99 431 "%s - %s - %s", size_str, p, (*disk->vendor) ? disk->vendor : disk->model);
4c812463
MT
432
433 } else {
434 snprintf(disk->description, sizeof(disk->description),
0c8b2d99 435 "%s - %s", size_str, p);
4c812463 436 }
d7dd283b 437
29afd408
MT
438 // Cut off the description string after 40 characters
439 disk->description[41] = '\0';
440
d7dd283b
MT
441 *disks++ = disk;
442
443 if (--i == 0)
444 break;
445
446 udev_device_unref(dev);
447 }
448
449 udev_enumerate_unref(enumerate);
450
451 *disks = NULL;
452
453 return ret;
454}
455
456void hw_free_disks(struct hw_disk** disks) {
457 struct hw_disk** disk = disks;
458
459 while (*disk != NULL) {
460 if (--(*disk)->ref == 0)
461 free(*disk);
462
463 disk++;
464 }
465
466 free(disks);
467}
468
16640487 469unsigned int hw_count_disks(struct hw_disk** disks) {
d7dd283b
MT
470 unsigned int ret = 0;
471
472 while (*disks++)
473 ret++;
474
475 return ret;
476}
477
478struct hw_disk** hw_select_disks(struct hw_disk** disks, int* selection) {
479 struct hw_disk** ret = hw_create_disks();
480 struct hw_disk** selected_disks = ret;
481
16640487 482 unsigned int num_disks = hw_count_disks(disks);
d7dd283b
MT
483
484 for (unsigned int i = 0; i < num_disks; i++) {
ee43f517 485 if (!selection || selection[i]) {
d7dd283b
MT
486 struct hw_disk *selected_disk = disks[i];
487 selected_disk->ref++;
488
489 *selected_disks++ = selected_disk;
490 }
491 }
492
493 // Set sentinel
494 *selected_disks = NULL;
495
496 return ret;
497}
498
16640487 499struct hw_disk** hw_select_first_disk(struct hw_disk** disks) {
a3e135c8
MT
500 struct hw_disk** ret = hw_create_disks();
501 struct hw_disk** selected_disks = ret;
502
503 unsigned int num_disks = hw_count_disks(disks);
504 assert(num_disks > 0);
505
506 for (unsigned int i = 0; i < num_disks; i++) {
507 struct hw_disk *disk = disks[i];
508 disk->ref++;
509
510 *selected_disks++ = disk;
511 break;
512 }
513
514 // Set sentinel
515 *selected_disks = NULL;
516
517 return ret;
518}
519
25fcce25
MT
520static unsigned long long hw_swap_size(struct hw_destination* dest) {
521 unsigned long long memory = hw_memory();
522
523 unsigned long long swap_size = memory / 4;
524
525 // Min. swap size is 128MB
526 if (swap_size < MB2BYTES(128))
527 swap_size = MB2BYTES(128);
528
529 // Cap swap size to 1GB
530 else if (swap_size > MB2BYTES(1024))
531 swap_size = MB2BYTES(1024);
532
533 return swap_size;
534}
535
25fcce25 536static unsigned long long hw_boot_size(struct hw_destination* dest) {
ffdc6fbb 537 return MB2BYTES(512);
25fcce25
MT
538}
539
a691d4b3
MT
540static int hw_device_has_p_suffix(const struct hw_destination* dest) {
541 // All RAID devices have the p suffix.
542 if (dest->is_raid)
543 return 1;
544
545 // Devices with a number at the end have the p suffix, too.
546 // e.g. mmcblk0, cciss0
126d3570 547 unsigned int last_char = strlen(dest->path) - 1;
a691d4b3
MT
548 if ((dest->path[last_char] >= '0') && (dest->path[last_char] <= '9'))
549 return 1;
550
551 return 0;
552}
553
92e78233 554static int hw_calculate_partition_table(struct hw* hw, struct hw_destination* dest, int disable_swap) {
48d6a112
MT
555 char path[DEV_SIZE];
556 int part_idx = 1;
557
a691d4b3
MT
558 snprintf(path, sizeof(path), "%s%s", dest->path,
559 hw_device_has_p_suffix(dest) ? "p" : "");
48d6a112
MT
560 dest->part_boot_idx = 0;
561
25fcce25
MT
562 // Determine the size of the target block device
563 if (dest->is_raid) {
564 dest->size = (dest->disk1->size >= dest->disk2->size) ?
5315fae6 565 dest->disk2->size : dest->disk1->size;
22581f51
MT
566
567 // The RAID will install some metadata at the end of the disk
568 // and we will save up some space for that.
569 dest->size -= MB2BYTES(2);
25fcce25
MT
570 } else {
571 dest->size = dest->disk1->size;
572 }
573
802a123b
MT
574 // As we add some extra space before the beginning of the first
575 // partition, we need to substract that here.
576 dest->size -= MB2BYTES(1);
577
578 // Add some more space for partition tables, etc.
579 dest->size -= MB2BYTES(1);
580
c577571e
MT
581 // The disk has to have at least 2GB
582 if (dest->size <= MB2BYTES(2048))
583 return -1;
584
48d6a112
MT
585 // Determine partition table
586 dest->part_table = HW_PART_TABLE_MSDOS;
587
588 // Disks over 2TB need to use GPT
589 if (dest->size >= MB2BYTES(2047 * 1024))
590 dest->part_table = HW_PART_TABLE_GPT;
591
592 // We also use GPT on raid disks by default
593 else if (dest->is_raid)
594 dest->part_table = HW_PART_TABLE_GPT;
595
596 // When using GPT, GRUB2 needs a little bit of space to put
597 // itself in.
5689fc72 598 if (dest->part_table == HW_PART_TABLE_GPT) {
48d6a112
MT
599 snprintf(dest->part_bootldr, sizeof(dest->part_bootldr),
600 "%s%d", path, part_idx);
601
602 dest->size_bootldr = MB2BYTES(4);
603
604 dest->part_boot_idx = part_idx++;
605 } else {
606 *dest->part_bootldr = '\0';
607 dest->size_bootldr = 0;
608 }
609
58a46f0b
SS
610 // Disable seperate boot partition for BTRFS installations.
611 if(dest->filesystem == HW_FS_BTRFS) {
612 dest->size_boot = 0;
613 } else {
614 dest->size_boot = hw_boot_size(dest);
615 }
c577571e 616
92e78233
MT
617 // Create an EFI partition when running in EFI mode
618 if (hw->efi)
619 dest->size_boot_efi = MB2BYTES(32);
620 else
621 dest->size_boot_efi = 0;
622
c577571e
MT
623 // Determine the size of the data partition.
624 unsigned long long space_left = dest->size - \
92e78233 625 (dest->size_bootldr + dest->size_boot + dest->size_boot_efi);
c577571e
MT
626
627 // If we have less than 2GB left, we disable swap
628 if (space_left <= MB2BYTES(2048))
629 disable_swap = 1;
25fcce25 630
a8fca245
MT
631 // Should we use swap?
632 if (disable_swap)
633 dest->size_swap = 0;
634 else
635 dest->size_swap = hw_swap_size(dest);
636
c577571e
MT
637 // Subtract swap
638 space_left -= dest->size_swap;
25fcce25 639
c577571e
MT
640 // Root is getting what ever is left
641 dest->size_root = space_left;
25fcce25
MT
642
643 // Set partition names
25fcce25 644 if (dest->size_boot > 0) {
48d6a112
MT
645 if (dest->part_boot_idx == 0)
646 dest->part_boot_idx = part_idx;
25fcce25
MT
647
648 snprintf(dest->part_boot, sizeof(dest->part_boot), "%s%d", path, part_idx++);
649 } else
650 *dest->part_boot = '\0';
651
92e78233
MT
652 if (dest->size_boot_efi > 0) {
653 dest->part_boot_efi_idx = part_idx;
654
655 snprintf(dest->part_boot_efi, sizeof(dest->part_boot_efi),
656 "%s%d", path, part_idx++);
58e840bd 657 } else {
92e78233 658 *dest->part_boot_efi = '\0';
58e840bd
MT
659 dest->part_boot_efi_idx = 0;
660 }
92e78233 661
25fcce25
MT
662 if (dest->size_swap > 0)
663 snprintf(dest->part_swap, sizeof(dest->part_swap), "%s%d", path, part_idx++);
664 else
665 *dest->part_swap = '\0';
666
667 // There is always a root partition
48d6a112 668 if (dest->part_boot_idx == 0)
25fcce25
MT
669 dest->part_boot_idx = part_idx;
670
671 snprintf(dest->part_root, sizeof(dest->part_root), "%s%d", path, part_idx++);
672
25fcce25
MT
673 return 0;
674}
675
ad73749d
SS
676struct hw_destination* hw_make_destination(struct hw* hw, int part_type, struct hw_disk** disks,
677 int disable_swap, int filesystem) {
d7dd283b
MT
678 struct hw_destination* dest = malloc(sizeof(*dest));
679
ad73749d
SS
680 // Assign filesystem
681 dest->filesystem = filesystem;
682
d7dd283b
MT
683 if (part_type == HW_PART_TYPE_NORMAL) {
684 dest->disk1 = *disks;
685 dest->disk2 = NULL;
686
687 strncpy(dest->path, dest->disk1->path, sizeof(dest->path));
688
689 } else if (part_type == HW_PART_TYPE_RAID1) {
690 dest->disk1 = *disks++;
691 dest->disk2 = *disks;
4a0d9bef 692 dest->raid_level = 1;
d7dd283b
MT
693
694 snprintf(dest->path, sizeof(dest->path), "/dev/md0");
695 }
696
697 // Is this a RAID device?
698 dest->is_raid = (part_type > HW_PART_TYPE_NORMAL);
699
92e78233 700 int r = hw_calculate_partition_table(hw, dest, disable_swap);
25fcce25
MT
701 if (r)
702 return NULL;
d7dd283b 703
d7dd283b
MT
704 return dest;
705}
c4e96674
MT
706
707unsigned long long hw_memory() {
5be66d81 708 struct sysinfo si;
c4e96674 709
5be66d81
MT
710 int r = sysinfo(&si);
711 if (r < 0)
712 return 0;
c4e96674 713
5be66d81 714 return si.totalram;
c4e96674 715}
25fcce25 716
d2f993a7
MT
717static int hw_zero_out_device(const char* path, int bytes) {
718 char block[512];
719 memset(block, 0, sizeof(block));
720
721 int blocks = bytes / sizeof(block);
722
723 int fd = open(path, O_WRONLY);
724 if (fd < 0)
725 return -1;
726
727 unsigned int bytes_written = 0;
728 while (blocks-- > 0) {
729 bytes_written += write(fd, block, sizeof(block));
730 }
731
732 fsync(fd);
733 close(fd);
734
735 return bytes_written;
736}
737
0e491487
MT
738static int try_open(const char* path) {
739 FILE* f = fopen(path, "r");
740 if (f) {
741 fclose(f);
742 return 0;
743 }
744
745 return -1;
746}
747
46b56e20 748int hw_create_partitions(struct hw_destination* dest, const char* output) {
d2f993a7
MT
749 // Before we write a new partition table to the disk, we will erase
750 // the first couple of megabytes at the beginning of the device to
751 // get rid of all left other things like bootloaders and partition tables.
752 // This solves some problems when changing from MBR to GPT partitions or
753 // the other way around.
754 int r = hw_zero_out_device(dest->path, MB2BYTES(10));
755 if (r <= 0)
756 return r;
25fcce25 757
d2f993a7 758 char* cmd = NULL;
25fcce25
MT
759 asprintf(&cmd, "/usr/sbin/parted -s %s -a optimal", dest->path);
760
761 // Set partition type
762 if (dest->part_table == HW_PART_TABLE_MSDOS)
763 asprintf(&cmd, "%s mklabel msdos", cmd);
764 else if (dest->part_table == HW_PART_TABLE_GPT)
765 asprintf(&cmd, "%s mklabel gpt", cmd);
766
802a123b 767 unsigned long long part_start = MB2BYTES(1);
25fcce25 768
48d6a112 769 if (*dest->part_bootldr) {
802a123b 770 asprintf(&cmd, "%s mkpart %s ext2 %lluB %lluB", cmd,
48d6a112 771 (dest->part_table == HW_PART_TABLE_GPT) ? "BOOTLDR" : "primary",
802a123b 772 part_start, part_start + dest->size_bootldr - 1);
48d6a112
MT
773
774 part_start += dest->size_bootldr;
775 }
776
25fcce25 777 if (*dest->part_boot) {
802a123b 778 asprintf(&cmd, "%s mkpart %s ext2 %lluB %lluB", cmd,
25fcce25 779 (dest->part_table == HW_PART_TABLE_GPT) ? "BOOT" : "primary",
802a123b 780 part_start, part_start + dest->size_boot - 1);
25fcce25
MT
781
782 part_start += dest->size_boot;
783 }
784
92e78233
MT
785 if (*dest->part_boot_efi) {
786 asprintf(&cmd, "%s mkpart %s fat32 %lluB %lluB", cmd,
787 (dest->part_table == HW_PART_TABLE_GPT) ? "ESP" : "primary",
788 part_start, part_start + dest->size_boot_efi - 1);
789
790 part_start += dest->size_boot_efi;
791 }
792
25fcce25 793 if (*dest->part_swap) {
802a123b 794 asprintf(&cmd, "%s mkpart %s linux-swap %lluB %lluB", cmd,
25fcce25 795 (dest->part_table == HW_PART_TABLE_GPT) ? "SWAP" : "primary",
802a123b 796 part_start, part_start + dest->size_swap - 1);
25fcce25
MT
797
798 part_start += dest->size_swap;
799 }
800
801 if (*dest->part_root) {
802a123b 802 asprintf(&cmd, "%s mkpart %s ext2 %lluB %lluB", cmd,
25fcce25 803 (dest->part_table == HW_PART_TABLE_GPT) ? "ROOT" : "primary",
802a123b 804 part_start, part_start + dest->size_root - 1);
25fcce25
MT
805
806 part_start += dest->size_root;
807 }
808
e9cf574d 809 if (dest->part_boot_idx > 0)
25fcce25
MT
810 asprintf(&cmd, "%s set %d boot on", cmd, dest->part_boot_idx);
811
92e78233
MT
812 if (dest->part_boot_efi_idx > 0)
813 asprintf(&cmd, "%s set %d esp on", cmd, dest->part_boot_efi_idx);
814
e9cf574d 815 if (dest->part_table == HW_PART_TABLE_GPT) {
48d6a112
MT
816 if (*dest->part_bootldr) {
817 asprintf(&cmd, "%s set %d bios_grub on", cmd, dest->part_boot_idx);
818 }
25fcce25
MT
819 }
820
46b56e20 821 r = mysystem(output, cmd);
25fcce25 822
268090a8
MT
823 // Wait until the system re-read the partition table
824 if (r == 0) {
825 unsigned int counter = 10;
826
827 while (counter-- > 0) {
828 sleep(1);
829
0e491487 830 if (*dest->part_bootldr && (try_open(dest->part_bootldr) != 0))
48d6a112
MT
831 continue;
832
0e491487 833 if (*dest->part_boot && (try_open(dest->part_boot) != 0))
268090a8
MT
834 continue;
835
92e78233
MT
836 if (*dest->part_boot_efi && (try_open(dest->part_boot_efi) != 0))
837 continue;
838
0e491487 839 if (*dest->part_swap && (try_open(dest->part_swap) != 0))
268090a8
MT
840 continue;
841
0e491487 842 if (*dest->part_root && (try_open(dest->part_root) != 0))
268090a8
MT
843 continue;
844
268090a8
MT
845 // All partitions do exist, exiting the loop.
846 break;
847 }
848 }
849
25fcce25
MT
850 if (cmd)
851 free(cmd);
852
853 return r;
854}
855
42849040
MT
856static int hw_create_btrfs_subvolume(const char* output, const char* subvolume) {
857 char command [STRING_SIZE];
858 int r;
859
860 // Abort if the command could not be assigned.
861 r = snprintf(command, sizeof(command), "/usr/bin/btrfs subvolume create %s/%s", DESTINATION_MOUNT_PATH, subvolume);
862 if (r < 0)
863 return r;
864
865 // Create the subvolume
866 r = mysystem(output, command);
867 if (r)
868 return r;
869
870 return 0;
871}
872
873static int hw_create_btrfs_layout(const char* path, const char* output) {
874 const struct btrfs_subvolumes* subvolume = NULL;
875 char cmd[STRING_SIZE];
876 char volume[STRING_SIZE];
877 int r;
878
879 r = snprintf(cmd, sizeof(cmd), "/usr/bin/mkfs.btrfs -f %s", path);
880 if (r < 0)
881 return r;
882
883 // Create the main BTRFS file system.
884 r = mysystem(output, cmd);
885 if (r)
886 return r;
887
888 // We need to mount the FS in order to create any subvolumes.
889 r = hw_mount(path, DESTINATION_MOUNT_PATH, "btrfs", 0);
890 if (r)
891 return r;
892
893 // Loop through the list of subvolumes to create.
894 for ( subvolume = btrfs_subvolumes; subvolume->name; subvolume++ ) {
895 r = snprintf(volume, sizeof(volume), "%s", subvolume->name);
896
897 // Abort if snprintf fails.
898 if (r < 0)
899 return r;
900
901 // Call function to create the subvolume
902 r = hw_create_btrfs_subvolume(output, volume);
903 if (r)
904 return r;
905 }
906
907 // Umount the main BTRFS after subvolume creation.
908 r = hw_umount(DESTINATION_MOUNT_PATH, 0);
909 if (r)
910 return r;
911
912 return 0;
913}
914
46b56e20 915static int hw_format_filesystem(const char* path, int fs, const char* output) {
25fcce25 916 char cmd[STRING_SIZE] = "\0";
0465449e 917 int r;
25fcce25
MT
918
919 // Swap
920 if (fs == HW_FS_SWAP) {
921 snprintf(cmd, sizeof(cmd), "/sbin/mkswap -v1 %s &>/dev/null", path);
25fcce25
MT
922
923 // EXT4
924 } else if (fs == HW_FS_EXT4) {
5208ceed 925 snprintf(cmd, sizeof(cmd), "/sbin/mke2fs -FF -T ext4 %s", path);
25fcce25
MT
926
927 // EXT4 w/o journal
928 } else if (fs == HW_FS_EXT4_WO_JOURNAL) {
5208ceed 929 snprintf(cmd, sizeof(cmd), "/sbin/mke2fs -FF -T ext4 -O ^has_journal %s", path);
70a44b52
MT
930
931 // XFS
932 } else if (fs == HW_FS_XFS) {
933 snprintf(cmd, sizeof(cmd), "/sbin/mkfs.xfs -f %s", path);
92e78233 934
130815d3
SS
935 // BTRFS
936 } else if (fs == HW_FS_BTRFS) {
0465449e
SS
937 r = hw_create_btrfs_layout(path, output);
938
939 return r;
130815d3 940
92e78233
MT
941 // FAT32
942 } else if (fs == HW_FS_FAT32) {
943 snprintf(cmd, sizeof(cmd), "/sbin/mkfs.vfat %s", path);
25fcce25
MT
944 }
945
946 assert(*cmd);
947
0465449e 948 r = mysystem(output, cmd);
25fcce25
MT
949
950 return r;
951}
952
46b56e20 953int hw_create_filesystems(struct hw_destination* dest, const char* output) {
25fcce25
MT
954 int r;
955
956 // boot
957 if (*dest->part_boot) {
46b56e20 958 r = hw_format_filesystem(dest->part_boot, dest->filesystem, output);
25fcce25
MT
959 if (r)
960 return r;
961 }
962
92e78233
MT
963 // ESP
964 if (*dest->part_boot_efi) {
965 r = hw_format_filesystem(dest->part_boot_efi, HW_FS_FAT32, output);
966 if (r)
967 return r;
968 }
969
25fcce25
MT
970 // swap
971 if (*dest->part_swap) {
46b56e20 972 r = hw_format_filesystem(dest->part_swap, HW_FS_SWAP, output);
25fcce25
MT
973 if (r)
974 return r;
975 }
976
977 // root
46b56e20 978 r = hw_format_filesystem(dest->part_root, dest->filesystem, output);
25fcce25
MT
979 if (r)
980 return r;
981
25fcce25
MT
982 return 0;
983}
984
42849040 985static int hw_mount_btrfs_subvolumes(const char* source) {
0465449e 986 const struct btrfs_subvolumes* subvolume = NULL;
42849040
MT
987 char path[STRING_SIZE];
988 char options[STRING_SIZE];
0465449e
SS
989 int r;
990
42849040
MT
991 // Loop through the list of known subvolumes.
992 for (subvolume = btrfs_subvolumes; subvolume->name; subvolume++) {
993 // Assign subvolume path.
994 r = snprintf(path, sizeof(path), "%s%s", DESTINATION_MOUNT_PATH, subvolume->mount_path);
995 if (r < 0)
996 return r;
0465449e 997
42849040
MT
998 // Assign subvolume name.
999 r = snprintf(options, sizeof(options), "subvol=%s,%s", subvolume->name, BTRFS_MOUNT_OPTIONS);
1000 if (r < 0)
0465449e 1001 return r;
0465449e 1002
42849040 1003 // Create the directory.
0e0346cc 1004 r = hw_mkdir(path);
0465449e 1005
42849040
MT
1006 // Abort if the directory could not be created.
1007 if(r != 0 && errno != EEXIST)
0465449e 1008 return r;
0465449e 1009
42849040
MT
1010 // Print log message
1011 fprintf(flog, "Mounting subvolume %s to %s\n", subvolume->name, subvolume->mount_path);
0465449e 1012
42849040
MT
1013 // Try to mount the subvolume.
1014 r = mount(source, path, "btrfs", NULL, options);
1015 if (r)
1016 return r;
0465449e
SS
1017 }
1018
1019 return 0;
1020}
1021
25fcce25
MT
1022int hw_mount_filesystems(struct hw_destination* dest, const char* prefix) {
1023 char target[STRING_SIZE];
e7740eaf 1024 int r;
25fcce25
MT
1025
1026 assert(*prefix == '/');
1027
1028 const char* filesystem;
1029 switch (dest->filesystem) {
25fcce25
MT
1030 case HW_FS_EXT4:
1031 case HW_FS_EXT4_WO_JOURNAL:
1032 filesystem = "ext4";
1033 break;
1034
70a44b52
MT
1035 case HW_FS_XFS:
1036 filesystem = "xfs";
1037 break;
1038
130815d3
SS
1039 case HW_FS_BTRFS:
1040 filesystem = "btrfs";
1041 break;
1042
92e78233
MT
1043 case HW_FS_FAT32:
1044 filesystem = "vfat";
1045 break;
1046
25fcce25
MT
1047 default:
1048 assert(0);
1049 }
1050
1051 // root
e7740eaf
SS
1052 if (dest->filesystem == HW_FS_BTRFS) {
1053 r = hw_mount_btrfs_subvolumes(dest->part_root);
e7740eaf
SS
1054 if (r)
1055 return r;
1056 } else {
1057 r = hw_mount(dest->part_root, prefix, filesystem, 0);
e7740eaf
SS
1058 if (r)
1059 return r;
1060 }
25fcce25
MT
1061
1062 // boot
fbeac096
SS
1063 snprintf(target, sizeof(target), "%s%s", prefix, HW_PATH_BOOT);
1064 r = mkdir(target, S_IRWXU|S_IRWXG|S_IRWXO);
1065
1066 if (r) {
1067 hw_umount_filesystems(dest, prefix);
25fcce25 1068
fbeac096
SS
1069 return r;
1070 }
1071
1072 if (*dest->part_boot) {
25fcce25
MT
1073 r = hw_mount(dest->part_boot, target, filesystem, 0);
1074 if (r) {
1075 hw_umount_filesystems(dest, prefix);
1076
1077 return r;
1078 }
1079 }
1080
92e78233
MT
1081 // ESP
1082 if (*dest->part_boot_efi) {
1083 snprintf(target, sizeof(target), "%s%s", prefix, HW_PATH_BOOT_EFI);
1084 mkdir(target, S_IRWXU|S_IRWXG|S_IRWXO);
1085
1086 r = hw_mount(dest->part_boot_efi, target, "vfat", 0);
1087 if (r) {
1088 hw_umount_filesystems(dest, prefix);
1089
1090 return r;
1091 }
1092 }
1093
25fcce25
MT
1094 // swap
1095 if (*dest->part_swap) {
1096 r = swapon(dest->part_swap, 0);
1097 if (r) {
1098 hw_umount_filesystems(dest, prefix);
1099
1100 return r;
1101 }
1102 }
1103
1104 // bind-mount misc filesystems
ced15fdf
MT
1105 r = hw_bind_mount("/dev", prefix);
1106 if (r)
1107 return r;
25fcce25 1108
ced15fdf
MT
1109 r = hw_bind_mount("/proc", prefix);
1110 if (r)
1111 return r;
25fcce25 1112
ced15fdf
MT
1113 r = hw_bind_mount("/sys", prefix);
1114 if (r)
1115 return r;
25fcce25 1116
ced15fdf
MT
1117 r = hw_bind_mount("/sys/firmware/efi/efivars", prefix);
1118 if (r && errno != ENOENT)
1119 return r;
25fcce25
MT
1120
1121 return 0;
1122}
1123
42849040 1124static int hw_umount_btrfs_layout() {
e7740eaf
SS
1125 const struct btrfs_subvolumes* subvolume = NULL;
1126 char path[STRING_SIZE];
42849040
MT
1127 int counter = 0;
1128 int retry = 1;
e7740eaf
SS
1129 int r;
1130
42849040
MT
1131 do {
1132 // Reset the retry marker
1133 retry = 0;
e7740eaf 1134
42849040
MT
1135 // Loop through the list of subvolumes
1136 for (subvolume = btrfs_subvolumes; subvolume->name; subvolume++) {
1137 // Abort if the subvolume path could not be assigned.
1138 r = snprintf(path, sizeof(path), "%s%s", DESTINATION_MOUNT_PATH, subvolume->mount_path);
1139 if (r < 0)
1140 return r;
e7740eaf 1141
42849040
MT
1142 // Try to umount the subvolume.
1143 r = umount2(path, 0);
e7740eaf 1144
42849040
MT
1145 // Handle return codes.
1146 if (r) {
1147 switch(errno) {
1148 case EBUSY:
1149 // Set marker to retry the umount.
1150 retry = 1;
e7740eaf 1151
42849040
MT
1152 // Ignore if the subvolume could not be unmounted yet,
1153 // because it is still used.
1154 continue;
e7740eaf 1155
42849040
MT
1156 case EINVAL:
1157 // Ignore if the subvolume already has been unmounted
1158 continue;
1159 case ENOENT:
1160 // Ignore if the directory does not longer exist.
1161 continue;
1162 default:
1163 fprintf(flog, "Could not umount %s from %s - Error: %d\n", subvolume->name, path, r);
1164 return r;
1165 }
1166 }
e7740eaf 1167
42849040
MT
1168 // Print log message
1169 fprintf(flog, "Umounted %s from %s\n", subvolume->name, path);
e7740eaf 1170 }
42849040
MT
1171
1172 // Abort loop if all mountpoins got umounted
1173 if (retry == 0)
1174 return 0;
1175
1176 // Abort after five failed umount attempts
1177 if (counter == 5)
1178 return -1;
1179
1180 // Increment counter.
1181 counter++;
1182 } while (1);
e7740eaf
SS
1183}
1184
25fcce25 1185int hw_umount_filesystems(struct hw_destination* dest, const char* prefix) {
ade96ba8
MT
1186 int r;
1187 char target[STRING_SIZE];
1188
ddd32a5c
MT
1189 // Write all buffers to disk before umounting
1190 hw_sync();
1191
92e78233
MT
1192 // ESP
1193 if (*dest->part_boot_efi) {
ced15fdf 1194 r = hw_umount(HW_PATH_BOOT_EFI, prefix);
92e78233
MT
1195 if (r)
1196 return -1;
1197 }
1198
25fcce25
MT
1199 // boot
1200 if (*dest->part_boot) {
ced15fdf 1201 r = hw_umount(HW_PATH_BOOT, prefix);
ade96ba8
MT
1202 if (r)
1203 return -1;
25fcce25
MT
1204 }
1205
25fcce25
MT
1206 // swap
1207 if (*dest->part_swap) {
1208 swapoff(dest->part_swap);
1209 }
1210
1211 // misc filesystems
ced15fdf
MT
1212 r = hw_umount("/sys/firmware/efi/efivars", prefix);
1213 if (r)
1214 return -1;
1215
1216 r = hw_umount("/sys", prefix);
1217 if (r)
1218 return -1;
1219
1220 r = hw_umount("/proc", prefix);
1221 if (r)
1222 return -1;
1223
1224 r = hw_umount("/dev", prefix);
1225 if (r)
1226 return -1;
25fcce25 1227
ade96ba8 1228 // root
2a981396
SS
1229 if(dest->filesystem == HW_FS_BTRFS) {
1230 r = hw_umount_btrfs_layout();
1231 } else {
1232 r = hw_umount(prefix, NULL);
1233 }
1234
ade96ba8
MT
1235 if (r)
1236 return -1;
1237
25fcce25
MT
1238 return 0;
1239}
4a0d9bef 1240
d78fffa0 1241int hw_destroy_raid_superblocks(const struct hw_destination* dest, const char* output) {
d2fafe03
MT
1242 char cmd[STRING_SIZE];
1243
46b56e20
MT
1244 hw_stop_all_raid_arrays(output);
1245 hw_stop_all_raid_arrays(output);
d2fafe03
MT
1246
1247 if (dest->disk1) {
46b56e20
MT
1248 snprintf(cmd, sizeof(cmd), "/sbin/mdadm --zero-superblock %s", dest->disk1->path);
1249 mysystem(output, cmd);
d2fafe03
MT
1250 }
1251
1252 if (dest->disk2) {
46b56e20
MT
1253 snprintf(cmd, sizeof(cmd), "/sbin/mdadm --zero-superblock %s", dest->disk2->path);
1254 mysystem(output, cmd);
d2fafe03
MT
1255 }
1256
1257 return 0;
1258}
1259
46b56e20 1260int hw_setup_raid(struct hw_destination* dest, const char* output) {
4a0d9bef 1261 char* cmd = NULL;
7b4790d3 1262 int r;
4a0d9bef
MT
1263
1264 assert(dest->is_raid);
1265
d2fafe03
MT
1266 // Stop all RAID arrays that might be around (again).
1267 // It seems that there is some sort of race-condition with udev re-enabling
1268 // the raid arrays and therefore locking the disks.
46b56e20 1269 r = hw_destroy_raid_superblocks(dest, output);
d2fafe03 1270
7b4790d3
MT
1271 asprintf(&cmd, "echo \"y\" | /sbin/mdadm --create --verbose --metadata=%s --auto=mdp %s",
1272 RAID_METADATA, dest->path);
4a0d9bef
MT
1273
1274 switch (dest->raid_level) {
1275 case 1:
1276 asprintf(&cmd, "%s --level=1 --raid-devices=2", cmd);
1277 break;
1278
1279 default:
1280 assert(0);
1281 }
1282
1283 if (dest->disk1) {
1284 asprintf(&cmd, "%s %s", cmd, dest->disk1->path);
d2f993a7
MT
1285
1286 // Clear all data at the beginning
1287 r = hw_zero_out_device(dest->disk1->path, MB2BYTES(10));
1288 if (r <= 0)
1289 return r;
4a0d9bef
MT
1290 }
1291
1292 if (dest->disk2) {
1293 asprintf(&cmd, "%s %s", cmd, dest->disk2->path);
d2f993a7
MT
1294
1295 // Clear all data at the beginning
1296 r = hw_zero_out_device(dest->disk2->path, MB2BYTES(10));
1297 if (r <= 0)
1298 return r;
4a0d9bef
MT
1299 }
1300
46b56e20 1301 r = mysystem(output, cmd);
4a0d9bef
MT
1302 free(cmd);
1303
1304 // Wait a moment until the device has been properly brought up
1305 if (r == 0) {
1306 unsigned int counter = 10;
1307 while (counter-- > 0) {
1308 sleep(1);
1309
fde37387
MT
1310 // If the raid device has not yet been properly brought up,
1311 // opening it will fail with the message: Device or resource busy
1312 // Hence we will wait a bit until it becomes usable.
0e491487 1313 if (try_open(dest->path) == 0)
4a0d9bef
MT
1314 break;
1315 }
1316 }
1317
1318 return r;
1319}
1320
46b56e20
MT
1321int hw_stop_all_raid_arrays(const char* output) {
1322 return mysystem(output, "/sbin/mdadm --stop --scan --verbose");
4a0d9bef 1323}
f5007e9c 1324
92e78233 1325int hw_install_bootloader(struct hw* hw, struct hw_destination* dest, const char* output) {
f5007e9c 1326 char cmd[STRING_SIZE];
f5007e9c 1327
12034118
MT
1328 snprintf(cmd, sizeof(cmd), "/usr/bin/install-bootloader %s", dest->path);
1329 int r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
92e78233
MT
1330 if (r)
1331 return r;
1332
ade96ba8
MT
1333 hw_sync();
1334
92e78233 1335 return 0;
f5007e9c 1336}
7f69d8a4
MT
1337
1338static char* hw_get_uuid(const char* dev) {
1339 blkid_probe p = blkid_new_probe_from_filename(dev);
1340 const char* buffer = NULL;
1341 char* uuid = NULL;
1342
1343 if (!p)
1344 return NULL;
1345
1346 blkid_do_probe(p);
1347 blkid_probe_lookup_value(p, "UUID", &buffer, NULL);
1348
1349 if (buffer)
1350 uuid = strdup(buffer);
1351
1352 blkid_free_probe(p);
1353
1354 return uuid;
1355}
1356
335c5bd1
MT
1357#define FSTAB_FMT "UUID=%s %-8s %-4s %-10s %d %d\n"
1358
7f69d8a4 1359int hw_write_fstab(struct hw_destination* dest) {
15be1e1b 1360 const struct btrfs_subvolumes* subvolume = NULL;
7f69d8a4
MT
1361 FILE* f = fopen(DESTINATION_MOUNT_PATH "/etc/fstab", "w");
1362 if (!f)
1363 return -1;
1364
7f69d8a4 1365 char* uuid = NULL;
15be1e1b 1366 char mount_options[STRING_SIZE];
7f69d8a4
MT
1367
1368 // boot
1369 if (*dest->part_boot) {
1370 uuid = hw_get_uuid(dest->part_boot);
1371
1372 if (uuid) {
e404dab5 1373 fprintf(f, FSTAB_FMT, uuid, "/boot", "auto", "defaults,nodev,noexec,nosuid", 1, 2);
7f69d8a4
MT
1374 free(uuid);
1375 }
1376 }
1377
92e78233
MT
1378 // ESP
1379 if (*dest->part_boot_efi) {
1380 uuid = hw_get_uuid(dest->part_boot_efi);
1381
1382 if (uuid) {
1383 fprintf(f, FSTAB_FMT, uuid, "/boot/efi", "auto", "defaults", 1, 2);
1384 free(uuid);
1385 }
1386 }
1387
1388
7f69d8a4
MT
1389 // swap
1390 if (*dest->part_swap) {
1391 uuid = hw_get_uuid(dest->part_swap);
1392
1393 if (uuid) {
335c5bd1 1394 fprintf(f, FSTAB_FMT, uuid, "swap", "swap", "defaults,pri=1", 0, 0);
7f69d8a4
MT
1395 free(uuid);
1396 }
1397 }
1398
1399 // root
1400 uuid = hw_get_uuid(dest->part_root);
1401 if (uuid) {
15be1e1b
SS
1402 if(dest->filesystem == HW_FS_BTRFS) {
1403 // Loop through the list of subvolumes
1404 for (subvolume = btrfs_subvolumes; subvolume->name; subvolume++) {
1405 // Abort if the mount options could not be assigned
1406 int r = snprintf(mount_options, sizeof(mount_options), "defaults,%s,subvol=%s", BTRFS_MOUNT_OPTIONS, subvolume->name);
1407 if (r < 0) {
1408 return r;
1409 }
1410
1411 // Write the entry to the file
1412 fprintf(f, FSTAB_FMT, uuid, subvolume->mount_path, "btrfs", mount_options, 1, 1);
1413 }
1414 } else {
1415 fprintf(f, FSTAB_FMT, uuid, "/", "auto", "defaults", 1, 1);
1416 }
1417
7f69d8a4
MT
1418 free(uuid);
1419 }
1420
7f69d8a4
MT
1421 fclose(f);
1422
1423 return 0;
1424}
282ec35e
MT
1425
1426void hw_sync() {
1427 sync();
1428 sync();
1429 sync();
1430}
7d114284
MT
1431
1432int hw_start_networking(const char* output) {
1433 return mysystem(output, "/usr/bin/start-networking.sh");
1434}
38c6822d
MT
1435
1436char* hw_find_backup_file(const char* output, const char* search_path) {
1437 char path[STRING_SIZE];
1438
1439 snprintf(path, sizeof(path), "%s/backup.ipf", search_path);
1440 int r = access(path, R_OK);
1441
1442 if (r == 0)
1443 return strdup(path);
1444
1445 return NULL;
1446}
1447
1448int hw_restore_backup(const char* output, const char* backup_path, const char* destination) {
1449 char command[STRING_SIZE];
1450
3f8e70f6
MT
1451 snprintf(command, sizeof(command), "/bin/tar xzpf %s -C %s "
1452 "--exclude-from=%s/var/ipfire/backup/exclude --exclude-from=%s/var/ipfire/backup/exclude.user",
1453 backup_path, destination, destination, destination);
38c6822d
MT
1454 int rc = mysystem(output, command);
1455
1456 if (rc)
1457 return -1;
1458
1459 return 0;
1460}