]> git.ipfire.org Git - ipfire-2.x.git/blame - src/installer/hw.c
installer: use sysinfo() for memory detection
[ipfire-2.x.git] / src / installer / hw.c
CommitLineData
f0fa1795
MT
1/*#############################################################################
2# #
3# IPFire - An Open Source Firewall Distribution #
4# Copyright (C) 2014 IPFire development team #
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
MT
26#include <blkid/blkid.h>
27#include <fcntl.h>
f0fa1795 28#include <libudev.h>
d7dd283b 29#include <math.h>
f0fa1795
MT
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
d7dd283b 33#include <sys/ioctl.h>
f0fa1795 34#include <sys/mount.h>
25fcce25 35#include <sys/swap.h>
5be66d81 36#include <sys/sysinfo.h>
f0fa1795
MT
37#include <unistd.h>
38
d7dd283b
MT
39#include <linux/fs.h>
40
5315fae6
MT
41#include <libsmooth.h>
42
f0fa1795 43#include "hw.h"
25fcce25
MT
44
45const char* other_filesystems[] = {
46 "/dev",
47 "/proc",
48 "/sys",
49 NULL
50};
f0fa1795 51
46b56e20 52static int system_chroot(const char* output, const char* path, const char* cmd) {
f5007e9c
MT
53 char chroot_cmd[STRING_SIZE];
54
55 snprintf(chroot_cmd, sizeof(chroot_cmd), "/usr/sbin/chroot %s %s", path, cmd);
56
46b56e20 57 return mysystem(output, chroot_cmd);
f5007e9c
MT
58}
59
f0fa1795
MT
60struct hw* hw_init() {
61 struct hw* hw = malloc(sizeof(*hw));
62 assert(hw);
63
64 // Initialize libudev
65 hw->udev = udev_new();
66 if (!hw->udev) {
67 fprintf(stderr, "Could not create udev instance\n");
68 exit(1);
69 }
70
71 return hw;
72}
73
74void hw_free(struct hw* hw) {
75 if (hw->udev)
76 udev_unref(hw->udev);
77
78 free(hw);
79}
80
81static int strstartswith(const char* a, const char* b) {
82 return (strncmp(a, b, strlen(b)) == 0);
83}
84
25fcce25 85int hw_mount(const char* source, const char* target, const char* fs, int flags) {
85f2892b
MT
86 // Create target if it does not exist
87 if (access(target, X_OK) != 0)
88 mkdir(target, S_IRWXU|S_IRWXG|S_IRWXO);
89
25fcce25 90 return mount(source, target, fs, flags, NULL);
f0fa1795
MT
91}
92
93int hw_umount(const char* target) {
a8d8b557 94 return umount2(target, 0);
f0fa1795
MT
95}
96
97static int hw_test_source_medium(const char* path) {
25fcce25 98 int ret = hw_mount(path, SOURCE_MOUNT_PATH, "iso9660", MS_RDONLY);
f0fa1795
MT
99
100 // If the source could not be mounted we
101 // cannot proceed.
5315fae6 102 if (ret != 0)
f0fa1795
MT
103 return ret;
104
105 // Check if the test file exists.
5315fae6 106 ret = access(SOURCE_TEST_FILE, R_OK);
f0fa1795
MT
107
108 // Umount the test device.
109 hw_umount(SOURCE_MOUNT_PATH);
110
5315fae6 111 return (ret == 0);
f0fa1795
MT
112}
113
114char* hw_find_source_medium(struct hw* hw) {
115 char* ret = NULL;
116
117 struct udev_enumerate* enumerate = udev_enumerate_new(hw->udev);
118
119 udev_enumerate_add_match_subsystem(enumerate, "block");
120 udev_enumerate_scan_devices(enumerate);
121
122 struct udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
123
124 struct udev_list_entry* dev_list_entry;
125 udev_list_entry_foreach(dev_list_entry, devices) {
126 const char* path = udev_list_entry_get_name(dev_list_entry);
127 struct udev_device* dev = udev_device_new_from_syspath(hw->udev, path);
128
129 const char* dev_path = udev_device_get_devnode(dev);
130
131 // Skip everything what we cannot work with
132 if (strstartswith(dev_path, "/dev/loop") || strstartswith(dev_path, "/dev/fd") ||
4a0d9bef 133 strstartswith(dev_path, "/dev/ram") || strstartswith(dev_path, "/dev/md"))
f0fa1795
MT
134 continue;
135
5315fae6 136 if (hw_test_source_medium(dev_path) == 0) {
f0fa1795
MT
137 ret = strdup(dev_path);
138 }
139
140 udev_device_unref(dev);
141
142 // If a suitable device was found the search will end.
143 if (ret)
144 break;
145 }
146
147 udev_enumerate_unref(enumerate);
148
149 return ret;
150}
d7dd283b
MT
151
152static struct hw_disk** hw_create_disks() {
153 struct hw_disk** ret = malloc(sizeof(*ret) * (HW_MAX_DISKS + 1));
154
155 return ret;
156}
157
158static unsigned long long hw_block_device_get_size(const char* dev) {
159 int fd = open(dev, O_RDONLY);
160 if (fd < 0)
161 return 0;
162
163 unsigned long long size = blkid_get_dev_size(fd);
164 close(fd);
165
166 return size;
167}
168
ee00d203 169struct hw_disk** hw_find_disks(struct hw* hw, const char* sourcedrive) {
d7dd283b
MT
170 struct hw_disk** ret = hw_create_disks();
171 struct hw_disk** disks = ret;
172
173 struct udev_enumerate* enumerate = udev_enumerate_new(hw->udev);
174
175 udev_enumerate_add_match_subsystem(enumerate, "block");
176 udev_enumerate_scan_devices(enumerate);
177
178 struct udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
179
180 struct udev_list_entry* dev_list_entry;
181 unsigned int i = HW_MAX_DISKS;
182 udev_list_entry_foreach(dev_list_entry, devices) {
183 const char* path = udev_list_entry_get_name(dev_list_entry);
184 struct udev_device* dev = udev_device_new_from_syspath(hw->udev, path);
185
186 const char* dev_path = udev_device_get_devnode(dev);
187
188 // Skip everything what we cannot work with
189 if (strstartswith(dev_path, "/dev/loop") || strstartswith(dev_path, "/dev/fd") ||
4a0d9bef
MT
190 strstartswith(dev_path, "/dev/ram") || strstartswith(dev_path, "/dev/sr") ||
191 strstartswith(dev_path, "/dev/md")) {
d7dd283b
MT
192 udev_device_unref(dev);
193 continue;
194 }
195
ee00d203
MT
196 // Skip sourcedrive if we need to
197 if (sourcedrive && (strcmp(dev_path, sourcedrive) == 0)) {
d7dd283b
MT
198 udev_device_unref(dev);
199 continue;
200 }
201
ee00d203
MT
202 // DEVTYPE must be disk (otherwise we will see all sorts of partitions here)
203 const char* devtype = udev_device_get_property_value(dev, "DEVTYPE");
204 if (devtype && (strcmp(devtype, "disk") != 0)) {
d7dd283b
MT
205 udev_device_unref(dev);
206 continue;
207 }
208
209 // Skip devices with a size of zero
210 unsigned long long size = hw_block_device_get_size(dev_path);
211 if (size == 0) {
212 udev_device_unref(dev);
213 continue;
214 }
215
216 struct hw_disk* disk = malloc(sizeof(*disk));
217 if (disk == NULL)
218 return NULL;
219
220 disk->ref = 1;
221
222 strncpy(disk->path, dev_path, sizeof(disk->path));
0c8b2d99 223 const char* p = disk->path + 5;
d7dd283b
MT
224
225 disk->size = size;
226
227 // Vendor
228 const char* vendor = udev_device_get_property_value(dev, "ID_VENDOR");
229 if (!vendor)
230 vendor = udev_device_get_sysattr_value(dev, "vendor");
231 if (!vendor)
232 vendor = udev_device_get_sysattr_value(dev, "manufacturer");
d7dd283b 233
4c812463
MT
234 if (vendor)
235 strncpy(disk->vendor, vendor, sizeof(disk->vendor));
236 else
237 *disk->vendor = '\0';
d7dd283b
MT
238
239 // Model
240 const char* model = udev_device_get_property_value(dev, "ID_MODEL");
241 if (!model)
242 model = udev_device_get_sysattr_value(dev, "model");
243 if (!model)
244 model = udev_device_get_sysattr_value(dev, "product");
d7dd283b 245
4c812463
MT
246 if (model)
247 strncpy(disk->model, model, sizeof(disk->model));
248 else
249 *disk->model = '\0';
250
251 // Format description
252 char size_str[STRING_SIZE];
253 snprintf(size_str, sizeof(size_str), "%4.1fGB", (double)disk->size / pow(1024, 3));
d7dd283b 254
4c812463
MT
255 if (*disk->vendor && *disk->model) {
256 snprintf(disk->description, sizeof(disk->description),
0c8b2d99 257 "%s - %s - %s - %s", size_str, p, disk->vendor, disk->model);
4c812463
MT
258
259 } else if (*disk->vendor || *disk->model) {
260 snprintf(disk->description, sizeof(disk->description),
0c8b2d99 261 "%s - %s - %s", size_str, p, (*disk->vendor) ? disk->vendor : disk->model);
4c812463
MT
262
263 } else {
264 snprintf(disk->description, sizeof(disk->description),
0c8b2d99 265 "%s - %s", size_str, p);
4c812463 266 }
d7dd283b
MT
267
268 *disks++ = disk;
269
270 if (--i == 0)
271 break;
272
273 udev_device_unref(dev);
274 }
275
276 udev_enumerate_unref(enumerate);
277
278 *disks = NULL;
279
280 return ret;
281}
282
283void hw_free_disks(struct hw_disk** disks) {
284 struct hw_disk** disk = disks;
285
286 while (*disk != NULL) {
287 if (--(*disk)->ref == 0)
288 free(*disk);
289
290 disk++;
291 }
292
293 free(disks);
294}
295
296unsigned int hw_count_disks(struct hw_disk** disks) {
297 unsigned int ret = 0;
298
299 while (*disks++)
300 ret++;
301
302 return ret;
303}
304
305struct hw_disk** hw_select_disks(struct hw_disk** disks, int* selection) {
306 struct hw_disk** ret = hw_create_disks();
307 struct hw_disk** selected_disks = ret;
308
309 unsigned int num_disks = hw_count_disks(disks);
310
311 for (unsigned int i = 0; i < num_disks; i++) {
ee43f517 312 if (!selection || selection[i]) {
d7dd283b
MT
313 struct hw_disk *selected_disk = disks[i];
314 selected_disk->ref++;
315
316 *selected_disks++ = selected_disk;
317 }
318 }
319
320 // Set sentinel
321 *selected_disks = NULL;
322
323 return ret;
324}
325
25fcce25
MT
326static unsigned long long hw_swap_size(struct hw_destination* dest) {
327 unsigned long long memory = hw_memory();
328
329 unsigned long long swap_size = memory / 4;
330
331 // Min. swap size is 128MB
332 if (swap_size < MB2BYTES(128))
333 swap_size = MB2BYTES(128);
334
335 // Cap swap size to 1GB
336 else if (swap_size > MB2BYTES(1024))
337 swap_size = MB2BYTES(1024);
338
339 return swap_size;
340}
341
342static unsigned long long hw_root_size(struct hw_destination* dest) {
343 unsigned long long root_size;
344
345 if (dest->size < MB2BYTES(2048))
346 root_size = MB2BYTES(1024);
347
348 else if (dest->size >= MB2BYTES(2048) && dest->size <= MB2BYTES(3072))
349 root_size = MB2BYTES(1536);
350
351 else
352 root_size = MB2BYTES(2048);
353
354 return root_size;
355}
356
357static unsigned long long hw_boot_size(struct hw_destination* dest) {
358 return MB2BYTES(64);
359}
360
a691d4b3
MT
361static int hw_device_has_p_suffix(const struct hw_destination* dest) {
362 // All RAID devices have the p suffix.
363 if (dest->is_raid)
364 return 1;
365
366 // Devices with a number at the end have the p suffix, too.
367 // e.g. mmcblk0, cciss0
126d3570 368 unsigned int last_char = strlen(dest->path) - 1;
a691d4b3
MT
369 if ((dest->path[last_char] >= '0') && (dest->path[last_char] <= '9'))
370 return 1;
371
372 return 0;
373}
374
25fcce25 375static int hw_calculate_partition_table(struct hw_destination* dest) {
48d6a112
MT
376 char path[DEV_SIZE];
377 int part_idx = 1;
378
a691d4b3
MT
379 snprintf(path, sizeof(path), "%s%s", dest->path,
380 hw_device_has_p_suffix(dest) ? "p" : "");
48d6a112
MT
381 dest->part_boot_idx = 0;
382
25fcce25
MT
383 // Determine the size of the target block device
384 if (dest->is_raid) {
385 dest->size = (dest->disk1->size >= dest->disk2->size) ?
5315fae6 386 dest->disk2->size : dest->disk1->size;
22581f51
MT
387
388 // The RAID will install some metadata at the end of the disk
389 // and we will save up some space for that.
390 dest->size -= MB2BYTES(2);
25fcce25
MT
391 } else {
392 dest->size = dest->disk1->size;
393 }
394
802a123b
MT
395 // As we add some extra space before the beginning of the first
396 // partition, we need to substract that here.
397 dest->size -= MB2BYTES(1);
398
399 // Add some more space for partition tables, etc.
400 dest->size -= MB2BYTES(1);
401
48d6a112
MT
402 // Determine partition table
403 dest->part_table = HW_PART_TABLE_MSDOS;
404
405 // Disks over 2TB need to use GPT
406 if (dest->size >= MB2BYTES(2047 * 1024))
407 dest->part_table = HW_PART_TABLE_GPT;
408
409 // We also use GPT on raid disks by default
410 else if (dest->is_raid)
411 dest->part_table = HW_PART_TABLE_GPT;
412
413 // When using GPT, GRUB2 needs a little bit of space to put
414 // itself in.
5689fc72 415 if (dest->part_table == HW_PART_TABLE_GPT) {
48d6a112
MT
416 snprintf(dest->part_bootldr, sizeof(dest->part_bootldr),
417 "%s%d", path, part_idx);
418
419 dest->size_bootldr = MB2BYTES(4);
420
421 dest->part_boot_idx = part_idx++;
422 } else {
423 *dest->part_bootldr = '\0';
424 dest->size_bootldr = 0;
425 }
426
25fcce25
MT
427 dest->size_boot = hw_boot_size(dest);
428 dest->size_swap = hw_swap_size(dest);
429 dest->size_root = hw_root_size(dest);
430
431 // Determine the size of the data partition.
48d6a112
MT
432 unsigned long long used_space = dest->size_bootldr + dest->size_boot
433 + dest->size_swap + dest->size_root;
25fcce25
MT
434
435 // Disk is way too small
436 if (used_space >= dest->size)
437 return -1;
438
439 dest->size_data = dest->size - used_space;
440
441 // If it gets too small, we remove the swap space.
442 if (dest->size_data <= MB2BYTES(256)) {
443 dest->size_data += dest->size_swap;
444 dest->size_swap = 0;
445 }
446
447 // Set partition names
25fcce25 448 if (dest->size_boot > 0) {
48d6a112
MT
449 if (dest->part_boot_idx == 0)
450 dest->part_boot_idx = part_idx;
25fcce25
MT
451
452 snprintf(dest->part_boot, sizeof(dest->part_boot), "%s%d", path, part_idx++);
453 } else
454 *dest->part_boot = '\0';
455
456 if (dest->size_swap > 0)
457 snprintf(dest->part_swap, sizeof(dest->part_swap), "%s%d", path, part_idx++);
458 else
459 *dest->part_swap = '\0';
460
461 // There is always a root partition
48d6a112 462 if (dest->part_boot_idx == 0)
25fcce25
MT
463 dest->part_boot_idx = part_idx;
464
465 snprintf(dest->part_root, sizeof(dest->part_root), "%s%d", path, part_idx++);
466
467 if (dest->size_data > 0)
468 snprintf(dest->part_data, sizeof(dest->part_data), "%s%d", path, part_idx++);
469 else
470 *dest->part_data = '\0';
471
25fcce25
MT
472 return 0;
473}
474
d7dd283b
MT
475struct hw_destination* hw_make_destination(int part_type, struct hw_disk** disks) {
476 struct hw_destination* dest = malloc(sizeof(*dest));
477
478 if (part_type == HW_PART_TYPE_NORMAL) {
479 dest->disk1 = *disks;
480 dest->disk2 = NULL;
481
482 strncpy(dest->path, dest->disk1->path, sizeof(dest->path));
483
484 } else if (part_type == HW_PART_TYPE_RAID1) {
485 dest->disk1 = *disks++;
486 dest->disk2 = *disks;
4a0d9bef 487 dest->raid_level = 1;
d7dd283b
MT
488
489 snprintf(dest->path, sizeof(dest->path), "/dev/md0");
490 }
491
492 // Is this a RAID device?
493 dest->is_raid = (part_type > HW_PART_TYPE_NORMAL);
494
25fcce25
MT
495 int r = hw_calculate_partition_table(dest);
496 if (r)
497 return NULL;
d7dd283b 498
25fcce25
MT
499 // Set default filesystem
500 dest->filesystem = HW_FS_DEFAULT;
d7dd283b
MT
501
502 return dest;
503}
c4e96674
MT
504
505unsigned long long hw_memory() {
5be66d81 506 struct sysinfo si;
c4e96674 507
5be66d81
MT
508 int r = sysinfo(&si);
509 if (r < 0)
510 return 0;
c4e96674 511
5be66d81 512 return si.totalram;
c4e96674 513}
25fcce25 514
d2f993a7
MT
515static int hw_zero_out_device(const char* path, int bytes) {
516 char block[512];
517 memset(block, 0, sizeof(block));
518
519 int blocks = bytes / sizeof(block);
520
521 int fd = open(path, O_WRONLY);
522 if (fd < 0)
523 return -1;
524
525 unsigned int bytes_written = 0;
526 while (blocks-- > 0) {
527 bytes_written += write(fd, block, sizeof(block));
528 }
529
530 fsync(fd);
531 close(fd);
532
533 return bytes_written;
534}
535
0e491487
MT
536static int try_open(const char* path) {
537 FILE* f = fopen(path, "r");
538 if (f) {
539 fclose(f);
540 return 0;
541 }
542
543 return -1;
544}
545
46b56e20 546int hw_create_partitions(struct hw_destination* dest, const char* output) {
d2f993a7
MT
547 // Before we write a new partition table to the disk, we will erase
548 // the first couple of megabytes at the beginning of the device to
549 // get rid of all left other things like bootloaders and partition tables.
550 // This solves some problems when changing from MBR to GPT partitions or
551 // the other way around.
552 int r = hw_zero_out_device(dest->path, MB2BYTES(10));
553 if (r <= 0)
554 return r;
25fcce25 555
d2f993a7 556 char* cmd = NULL;
25fcce25
MT
557 asprintf(&cmd, "/usr/sbin/parted -s %s -a optimal", dest->path);
558
559 // Set partition type
560 if (dest->part_table == HW_PART_TABLE_MSDOS)
561 asprintf(&cmd, "%s mklabel msdos", cmd);
562 else if (dest->part_table == HW_PART_TABLE_GPT)
563 asprintf(&cmd, "%s mklabel gpt", cmd);
564
802a123b 565 unsigned long long part_start = MB2BYTES(1);
25fcce25 566
48d6a112 567 if (*dest->part_bootldr) {
802a123b 568 asprintf(&cmd, "%s mkpart %s ext2 %lluB %lluB", cmd,
48d6a112 569 (dest->part_table == HW_PART_TABLE_GPT) ? "BOOTLDR" : "primary",
802a123b 570 part_start, part_start + dest->size_bootldr - 1);
48d6a112
MT
571
572 part_start += dest->size_bootldr;
573 }
574
25fcce25 575 if (*dest->part_boot) {
802a123b 576 asprintf(&cmd, "%s mkpart %s ext2 %lluB %lluB", cmd,
25fcce25 577 (dest->part_table == HW_PART_TABLE_GPT) ? "BOOT" : "primary",
802a123b 578 part_start, part_start + dest->size_boot - 1);
25fcce25
MT
579
580 part_start += dest->size_boot;
581 }
582
583 if (*dest->part_swap) {
802a123b 584 asprintf(&cmd, "%s mkpart %s linux-swap %lluB %lluB", cmd,
25fcce25 585 (dest->part_table == HW_PART_TABLE_GPT) ? "SWAP" : "primary",
802a123b 586 part_start, part_start + dest->size_swap - 1);
25fcce25
MT
587
588 part_start += dest->size_swap;
589 }
590
591 if (*dest->part_root) {
802a123b 592 asprintf(&cmd, "%s mkpart %s ext2 %lluB %lluB", cmd,
25fcce25 593 (dest->part_table == HW_PART_TABLE_GPT) ? "ROOT" : "primary",
802a123b 594 part_start, part_start + dest->size_root - 1);
25fcce25
MT
595
596 part_start += dest->size_root;
597 }
598
599 if (*dest->part_data) {
802a123b 600 asprintf(&cmd, "%s mkpart %s ext2 %lluB %lluB", cmd,
25fcce25 601 (dest->part_table == HW_PART_TABLE_GPT) ? "DATA" : "primary",
802a123b 602 part_start, part_start + dest->size_data - 1);
25fcce25
MT
603
604 part_start += dest->size_data;
605 }
606
e9cf574d 607 if (dest->part_boot_idx > 0)
25fcce25
MT
608 asprintf(&cmd, "%s set %d boot on", cmd, dest->part_boot_idx);
609
e9cf574d 610 if (dest->part_table == HW_PART_TABLE_GPT) {
48d6a112
MT
611 if (*dest->part_bootldr) {
612 asprintf(&cmd, "%s set %d bios_grub on", cmd, dest->part_boot_idx);
613 }
25fcce25
MT
614 asprintf(&cmd, "%s disk_set pmbr_boot on", cmd);
615 }
616
46b56e20 617 r = mysystem(output, cmd);
25fcce25 618
268090a8
MT
619 // Wait until the system re-read the partition table
620 if (r == 0) {
621 unsigned int counter = 10;
622
623 while (counter-- > 0) {
624 sleep(1);
625
0e491487 626 if (*dest->part_bootldr && (try_open(dest->part_bootldr) != 0))
48d6a112
MT
627 continue;
628
0e491487 629 if (*dest->part_boot && (try_open(dest->part_boot) != 0))
268090a8
MT
630 continue;
631
0e491487 632 if (*dest->part_swap && (try_open(dest->part_swap) != 0))
268090a8
MT
633 continue;
634
0e491487 635 if (*dest->part_root && (try_open(dest->part_root) != 0))
268090a8
MT
636 continue;
637
0e491487 638 if (*dest->part_data && (try_open(dest->part_data) != 0))
268090a8
MT
639 continue;
640
641 // All partitions do exist, exiting the loop.
642 break;
643 }
644 }
645
25fcce25
MT
646 if (cmd)
647 free(cmd);
648
649 return r;
650}
651
46b56e20 652static int hw_format_filesystem(const char* path, int fs, const char* output) {
25fcce25
MT
653 char cmd[STRING_SIZE] = "\0";
654
655 // Swap
656 if (fs == HW_FS_SWAP) {
657 snprintf(cmd, sizeof(cmd), "/sbin/mkswap -v1 %s &>/dev/null", path);
658 // ReiserFS
659 } else if (fs == HW_FS_REISERFS) {
660 snprintf(cmd, sizeof(cmd), "/sbin/mkreiserfs -f %s ", path);
661
662 // EXT4
663 } else if (fs == HW_FS_EXT4) {
664 snprintf(cmd, sizeof(cmd), "/sbin/mke2fs -T ext4 %s", path);
665
666 // EXT4 w/o journal
667 } else if (fs == HW_FS_EXT4_WO_JOURNAL) {
668 snprintf(cmd, sizeof(cmd), "/sbin/mke2fs -T ext4 -O ^has_journal %s", path);
70a44b52
MT
669
670 // XFS
671 } else if (fs == HW_FS_XFS) {
672 snprintf(cmd, sizeof(cmd), "/sbin/mkfs.xfs -f %s", path);
25fcce25
MT
673 }
674
675 assert(*cmd);
676
46b56e20 677 int r = mysystem(output, cmd);
25fcce25
MT
678
679 return r;
680}
681
46b56e20 682int hw_create_filesystems(struct hw_destination* dest, const char* output) {
25fcce25
MT
683 int r;
684
685 // boot
686 if (*dest->part_boot) {
46b56e20 687 r = hw_format_filesystem(dest->part_boot, dest->filesystem, output);
25fcce25
MT
688 if (r)
689 return r;
690 }
691
692 // swap
693 if (*dest->part_swap) {
46b56e20 694 r = hw_format_filesystem(dest->part_swap, HW_FS_SWAP, output);
25fcce25
MT
695 if (r)
696 return r;
697 }
698
699 // root
46b56e20 700 r = hw_format_filesystem(dest->part_root, dest->filesystem, output);
25fcce25
MT
701 if (r)
702 return r;
703
704 // data
705 if (*dest->part_data) {
46b56e20 706 r = hw_format_filesystem(dest->part_data, dest->filesystem, output);
25fcce25
MT
707 if (r)
708 return r;
709 }
710
711 return 0;
712}
713
714int hw_mount_filesystems(struct hw_destination* dest, const char* prefix) {
715 char target[STRING_SIZE];
716
717 assert(*prefix == '/');
718
719 const char* filesystem;
720 switch (dest->filesystem) {
721 case HW_FS_REISERFS:
722 filesystem = "reiserfs";
723 break;
724
725 case HW_FS_EXT4:
726 case HW_FS_EXT4_WO_JOURNAL:
727 filesystem = "ext4";
728 break;
729
70a44b52
MT
730 case HW_FS_XFS:
731 filesystem = "xfs";
732 break;
733
25fcce25
MT
734 default:
735 assert(0);
736 }
737
738 // root
739 int r = hw_mount(dest->part_root, prefix, filesystem, 0);
740 if (r)
741 return r;
742
743 // boot
744 if (*dest->part_boot) {
745 snprintf(target, sizeof(target), "%s%s", prefix, HW_PATH_BOOT);
746 mkdir(target, S_IRWXU|S_IRWXG|S_IRWXO);
747
748 r = hw_mount(dest->part_boot, target, filesystem, 0);
749 if (r) {
750 hw_umount_filesystems(dest, prefix);
751
752 return r;
753 }
754 }
755
756 // data
757 if (*dest->part_data) {
758 snprintf(target, sizeof(target), "%s%s", prefix, HW_PATH_DATA);
759 mkdir(target, S_IRWXU|S_IRWXG|S_IRWXO);
760
761 r = hw_mount(dest->part_data, target, filesystem, 0);
762 if (r) {
763 hw_umount_filesystems(dest, prefix);
764
765 return r;
766 }
767 }
768
769 // swap
770 if (*dest->part_swap) {
771 r = swapon(dest->part_swap, 0);
772 if (r) {
773 hw_umount_filesystems(dest, prefix);
774
775 return r;
776 }
777 }
778
779 // bind-mount misc filesystems
780 char** otherfs = other_filesystems;
781 while (*otherfs) {
782 snprintf(target, sizeof(target), "%s%s", prefix, *otherfs);
783
784 mkdir(target, S_IRWXU|S_IRWXG|S_IRWXO);
785 r = hw_mount(*otherfs, target, NULL, MS_BIND);
786 if (r) {
787 hw_umount_filesystems(dest, prefix);
788
789 return r;
790 }
791
792 otherfs++;
793 }
794
795 return 0;
796}
797
798int hw_umount_filesystems(struct hw_destination* dest, const char* prefix) {
ddd32a5c
MT
799 // Write all buffers to disk before umounting
800 hw_sync();
801
25fcce25
MT
802 // boot
803 if (*dest->part_boot) {
804 hw_umount(dest->part_boot);
805 }
806
807 // data
808 if (*dest->part_data) {
809 hw_umount(dest->part_data);
810 }
811
812 // root
813 hw_umount(dest->part_root);
814
815 // swap
816 if (*dest->part_swap) {
817 swapoff(dest->part_swap);
818 }
819
820 // misc filesystems
821 char target[STRING_SIZE];
822 char** otherfs = other_filesystems;
823
824 while (*otherfs) {
825 snprintf(target, sizeof(target), "%s%s", prefix, *otherfs++);
826 hw_umount(target);
827 }
828
829 return 0;
830}
4a0d9bef 831
d78fffa0 832int hw_destroy_raid_superblocks(const struct hw_destination* dest, const char* output) {
d2fafe03
MT
833 char cmd[STRING_SIZE];
834
46b56e20
MT
835 hw_stop_all_raid_arrays(output);
836 hw_stop_all_raid_arrays(output);
d2fafe03
MT
837
838 if (dest->disk1) {
46b56e20
MT
839 snprintf(cmd, sizeof(cmd), "/sbin/mdadm --zero-superblock %s", dest->disk1->path);
840 mysystem(output, cmd);
d2fafe03
MT
841 }
842
843 if (dest->disk2) {
46b56e20
MT
844 snprintf(cmd, sizeof(cmd), "/sbin/mdadm --zero-superblock %s", dest->disk2->path);
845 mysystem(output, cmd);
d2fafe03
MT
846 }
847
848 return 0;
849}
850
46b56e20 851int hw_setup_raid(struct hw_destination* dest, const char* output) {
4a0d9bef 852 char* cmd = NULL;
7b4790d3 853 int r;
4a0d9bef
MT
854
855 assert(dest->is_raid);
856
d2fafe03
MT
857 // Stop all RAID arrays that might be around (again).
858 // It seems that there is some sort of race-condition with udev re-enabling
859 // the raid arrays and therefore locking the disks.
46b56e20 860 r = hw_destroy_raid_superblocks(dest, output);
d2fafe03 861
7b4790d3
MT
862 asprintf(&cmd, "echo \"y\" | /sbin/mdadm --create --verbose --metadata=%s --auto=mdp %s",
863 RAID_METADATA, dest->path);
4a0d9bef
MT
864
865 switch (dest->raid_level) {
866 case 1:
867 asprintf(&cmd, "%s --level=1 --raid-devices=2", cmd);
868 break;
869
870 default:
871 assert(0);
872 }
873
874 if (dest->disk1) {
875 asprintf(&cmd, "%s %s", cmd, dest->disk1->path);
d2f993a7
MT
876
877 // Clear all data at the beginning
878 r = hw_zero_out_device(dest->disk1->path, MB2BYTES(10));
879 if (r <= 0)
880 return r;
4a0d9bef
MT
881 }
882
883 if (dest->disk2) {
884 asprintf(&cmd, "%s %s", cmd, dest->disk2->path);
d2f993a7
MT
885
886 // Clear all data at the beginning
887 r = hw_zero_out_device(dest->disk2->path, MB2BYTES(10));
888 if (r <= 0)
889 return r;
4a0d9bef
MT
890 }
891
46b56e20 892 r = mysystem(output, cmd);
4a0d9bef
MT
893 free(cmd);
894
895 // Wait a moment until the device has been properly brought up
896 if (r == 0) {
897 unsigned int counter = 10;
898 while (counter-- > 0) {
899 sleep(1);
900
fde37387
MT
901 // If the raid device has not yet been properly brought up,
902 // opening it will fail with the message: Device or resource busy
903 // Hence we will wait a bit until it becomes usable.
0e491487 904 if (try_open(dest->path) == 0)
4a0d9bef
MT
905 break;
906 }
907 }
908
909 return r;
910}
911
46b56e20
MT
912int hw_stop_all_raid_arrays(const char* output) {
913 return mysystem(output, "/sbin/mdadm --stop --scan --verbose");
4a0d9bef 914}
f5007e9c 915
46b56e20 916int hw_install_bootloader(struct hw_destination* dest, const char* output) {
f5007e9c
MT
917 char cmd[STRING_SIZE];
918 int r;
919
920 // Generate configuration file
921 snprintf(cmd, sizeof(cmd), "/usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg");
46b56e20 922 r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
f5007e9c
MT
923 if (r)
924 return r;
925
926 char cmd_grub[STRING_SIZE];
927 snprintf(cmd_grub, sizeof(cmd_grub), "/usr/sbin/grub-install --no-floppy --recheck");
928
e9cf574d 929 if (dest->is_raid) {
f5007e9c 930 snprintf(cmd, sizeof(cmd), "%s %s", cmd_grub, dest->disk1->path);
46b56e20 931 r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
f5007e9c
MT
932 if (r)
933 return r;
934
935 snprintf(cmd, sizeof(cmd), "%s %s", cmd_grub, dest->disk2->path);
46b56e20 936 r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
f5007e9c
MT
937 } else {
938 snprintf(cmd, sizeof(cmd), "%s %s", cmd_grub, dest->path);
46b56e20 939 r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
f5007e9c
MT
940 }
941
942 return r;
943}
7f69d8a4
MT
944
945static char* hw_get_uuid(const char* dev) {
946 blkid_probe p = blkid_new_probe_from_filename(dev);
947 const char* buffer = NULL;
948 char* uuid = NULL;
949
950 if (!p)
951 return NULL;
952
953 blkid_do_probe(p);
954 blkid_probe_lookup_value(p, "UUID", &buffer, NULL);
955
956 if (buffer)
957 uuid = strdup(buffer);
958
959 blkid_free_probe(p);
960
961 return uuid;
962}
963
964int hw_write_fstab(struct hw_destination* dest) {
965 FILE* f = fopen(DESTINATION_MOUNT_PATH "/etc/fstab", "w");
966 if (!f)
967 return -1;
968
969 const char* fmt = "UUID=%s %-8s %-4s %-10s %d %d\n";
970 char* uuid = NULL;
971
972 // boot
973 if (*dest->part_boot) {
974 uuid = hw_get_uuid(dest->part_boot);
975
976 if (uuid) {
977 fprintf(f, fmt, uuid, "/boot", "auto", "defaults", 1, 2);
978 free(uuid);
979 }
980 }
981
982 // swap
983 if (*dest->part_swap) {
984 uuid = hw_get_uuid(dest->part_swap);
985
986 if (uuid) {
987 fprintf(f, fmt, uuid, "swap", "swap", "defaults,pri=1", 0, 0);
988 free(uuid);
989 }
990 }
991
992 // root
993 uuid = hw_get_uuid(dest->part_root);
994 if (uuid) {
995 fprintf(f, fmt, uuid, "/", "auto", "defaults", 1, 1);
996 free(uuid);
997 }
998
999 // data
1000 if (*dest->part_data) {
1001 uuid = hw_get_uuid(dest->part_data);
1002
1003 if (uuid) {
1004 fprintf(f, fmt, uuid, "/var", "auto", "defaults", 1, 1);
1005 free(uuid);
1006 }
1007 }
1008
1009 fclose(f);
1010
1011 return 0;
1012}
282ec35e
MT
1013
1014void hw_sync() {
1015 sync();
1016 sync();
1017 sync();
1018}