]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - src/installer/hw.c
installer: Fix umounting destination
[ipfire-2.x.git] / src / installer / hw.c
index 2caf2afded06de590b0e92f7b6d6b335d9e7c639..ce9777500d5cbd28ecabdb10082f7fbd04760841 100644 (file)
 
 #include <assert.h>
 #include <blkid/blkid.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <libudev.h>
+#include <linux/loop.h>
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
+#include <sys/stat.h>
 #include <sys/swap.h>
+#include <sys/sysinfo.h>
 #include <unistd.h>
 
 #include <linux/fs.h>
@@ -48,12 +52,12 @@ const char* other_filesystems[] = {
        NULL
 };
 
-static int system_chroot(const char* path, const char* cmd) {
+static int system_chroot(const char* output, const char* path, const char* cmd) {
        char chroot_cmd[STRING_SIZE];
 
        snprintf(chroot_cmd, sizeof(chroot_cmd), "/usr/sbin/chroot %s %s", path, cmd);
 
-       return mysystem(chroot_cmd);
+       return mysystem(output, chroot_cmd);
 }
 
 struct hw* hw_init() {
@@ -81,16 +85,70 @@ static int strstartswith(const char* a, const char* b) {
        return (strncmp(a, b, strlen(b)) == 0);
 }
 
+static char loop_device[STRING_SIZE];
+
+static int setup_loop_device(const char* source, const char* device) {
+       int file_fd = open(source, O_RDWR);
+       if (file_fd < 0)
+               goto ERROR;
+
+       int device_fd = -1;
+       if ((device_fd = open(device, O_RDWR)) < 0)
+               goto ERROR;
+
+       if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0)
+               goto ERROR;
+
+       close(file_fd);
+       close(device_fd);
+
+       return 0;
+
+ERROR:
+       if (file_fd >= 0)
+               close(file_fd);
+
+       if (device_fd >= 0) {
+               ioctl(device_fd, LOOP_CLR_FD, 0);
+               close(device_fd);
+       }
+
+       return -1;
+}
+
 int hw_mount(const char* source, const char* target, const char* fs, int flags) {
+       const char* loop_device = "/dev/loop0";
+
        // Create target if it does not exist
        if (access(target, X_OK) != 0)
                mkdir(target, S_IRWXU|S_IRWXG|S_IRWXO);
 
+       struct stat st;
+       stat(source, &st);
+
+       if (S_ISREG(st.st_mode)) {
+               int r = setup_loop_device(source, loop_device);
+               if (r == 0) {
+                       source = loop_device;
+               } else {
+                       return -1;
+               }
+       }
+
        return mount(source, target, fs, flags, NULL);
 }
 
 int hw_umount(const char* target) {
-       return umount2(target, 0);
+       int r = umount2(target, 0);
+
+       if (r && errno == EBUSY) {
+               // Give it a moment to settle
+               sleep(1);
+
+               r = umount2(target, MNT_FORCE);
+       }
+
+       return r;
 }
 
 static int hw_test_source_medium(const char* path) {
@@ -165,7 +223,7 @@ static unsigned long long hw_block_device_get_size(const char* dev) {
        return size;
 }
 
-struct hw_disk** hw_find_disks(struct hw* hw) {
+struct hw_disk** hw_find_disks(struct hw* hw, const char* sourcedrive) {
        struct hw_disk** ret = hw_create_disks();
        struct hw_disk** disks = ret;
 
@@ -192,15 +250,15 @@ struct hw_disk** hw_find_disks(struct hw* hw) {
                        continue;
                }
 
-               // DEVTYPE must be disk (otherwise we will see all sorts of partitions here)
-               const char* devtype = udev_device_get_property_value(dev, "DEVTYPE");
-               if (devtype && (strcmp(devtype, "disk") != 0)) {
+               // Skip sourcedrive if we need to
+               if (sourcedrive && (strcmp(dev_path, sourcedrive) == 0)) {
                        udev_device_unref(dev);
                        continue;
                }
 
-               // Skip all source mediums
-               if (hw_test_source_medium(dev_path) == 0) {
+               // DEVTYPE must be disk (otherwise we will see all sorts of partitions here)
+               const char* devtype = udev_device_get_property_value(dev, "DEVTYPE");
+               if (devtype && (strcmp(devtype, "disk") != 0)) {
                        udev_device_unref(dev);
                        continue;
                }
@@ -219,6 +277,7 @@ struct hw_disk** hw_find_disks(struct hw* hw) {
                disk->ref = 1;
 
                strncpy(disk->path, dev_path, sizeof(disk->path));
+               const char* p = disk->path + 5;
 
                disk->size = size;
 
@@ -252,15 +311,15 @@ struct hw_disk** hw_find_disks(struct hw* hw) {
 
                if (*disk->vendor && *disk->model) {
                        snprintf(disk->description, sizeof(disk->description),
-                               "%s - %s - %s", size_str, disk->vendor, disk->model);
+                               "%s - %s - %s - %s", size_str, p, disk->vendor, disk->model);
 
                } else if (*disk->vendor || *disk->model) {
                        snprintf(disk->description, sizeof(disk->description),
-                               "%s - %s", size_str, (*disk->vendor) ? disk->vendor : disk->model);
+                               "%s - %s - %s", size_str, p, (*disk->vendor) ? disk->vendor : disk->model);
 
                } else {
                        snprintf(disk->description, sizeof(disk->description),
-                               "%s - N/A", size_str);
+                               "%s - %s", size_str, p);
                }
 
                *disks++ = disk;
@@ -291,7 +350,7 @@ void hw_free_disks(struct hw_disk** disks) {
        free(disks);
 }
 
-unsigned int hw_count_disks(struct hw_disk** disks) {
+unsigned int hw_count_disks(const struct hw_disk** disks) {
        unsigned int ret = 0;
 
        while (*disks++)
@@ -304,10 +363,10 @@ struct hw_disk** hw_select_disks(struct hw_disk** disks, int* selection) {
        struct hw_disk** ret = hw_create_disks();
        struct hw_disk** selected_disks = ret;
 
-       unsigned int num_disks = hw_count_disks(disks);
+       unsigned int num_disks = hw_count_disks((const struct hw_disk**)disks);
 
        for (unsigned int i = 0; i < num_disks; i++) {
-               if (selection && selection[i]) {
+               if (!selection || selection[i]) {
                        struct hw_disk *selected_disk = disks[i];
                        selected_disk->ref++;
 
@@ -321,6 +380,27 @@ struct hw_disk** hw_select_disks(struct hw_disk** disks, int* selection) {
        return ret;
 }
 
+struct hw_disk** hw_select_first_disk(const struct hw_disk** disks) {
+       struct hw_disk** ret = hw_create_disks();
+       struct hw_disk** selected_disks = ret;
+
+       unsigned int num_disks = hw_count_disks(disks);
+       assert(num_disks > 0);
+
+       for (unsigned int i = 0; i < num_disks; i++) {
+               struct hw_disk *disk = disks[i];
+               disk->ref++;
+
+               *selected_disks++ = disk;
+               break;
+       }
+
+       // Set sentinel
+       *selected_disks = NULL;
+
+       return ret;
+}
+
 static unsigned long long hw_swap_size(struct hw_destination* dest) {
        unsigned long long memory = hw_memory();
 
@@ -356,11 +436,26 @@ static unsigned long long hw_boot_size(struct hw_destination* dest) {
        return MB2BYTES(64);
 }
 
-static int hw_calculate_partition_table(struct hw_destination* dest) {
+static int hw_device_has_p_suffix(const struct hw_destination* dest) {
+       // All RAID devices have the p suffix.
+       if (dest->is_raid)
+               return 1;
+
+       // Devices with a number at the end have the p suffix, too.
+       // e.g. mmcblk0, cciss0
+       unsigned int last_char = strlen(dest->path) - 1;
+       if ((dest->path[last_char] >= '0') && (dest->path[last_char] <= '9'))
+               return 1;
+
+       return 0;
+}
+
+static int hw_calculate_partition_table(struct hw_destination* dest, int disable_swap) {
        char path[DEV_SIZE];
        int part_idx = 1;
 
-       snprintf(path, sizeof(path), "%s%s", dest->path, (dest->is_raid) ? "p" : "");
+       snprintf(path, sizeof(path), "%s%s", dest->path,
+               hw_device_has_p_suffix(dest) ? "p" : "");
        dest->part_boot_idx = 0;
 
        // Determine the size of the target block device
@@ -408,9 +503,14 @@ static int hw_calculate_partition_table(struct hw_destination* dest) {
        }
 
        dest->size_boot = hw_boot_size(dest);
-       dest->size_swap = hw_swap_size(dest);
        dest->size_root = hw_root_size(dest);
 
+       // Should we use swap?
+       if (disable_swap)
+               dest->size_swap = 0;
+       else
+               dest->size_swap = hw_swap_size(dest);
+
        // Determine the size of the data partition.
        unsigned long long used_space = dest->size_bootldr + dest->size_boot
                + dest->size_swap + dest->size_root;
@@ -455,7 +555,7 @@ static int hw_calculate_partition_table(struct hw_destination* dest) {
        return 0;
 }
 
-struct hw_destination* hw_make_destination(int part_type, struct hw_disk** disks) {
+struct hw_destination* hw_make_destination(int part_type, struct hw_disk** disks, int disable_swap) {
        struct hw_destination* dest = malloc(sizeof(*dest));
 
        if (part_type == HW_PART_TYPE_NORMAL) {
@@ -475,7 +575,7 @@ struct hw_destination* hw_make_destination(int part_type, struct hw_disk** disks
        // Is this a RAID device?
        dest->is_raid = (part_type > HW_PART_TYPE_NORMAL);
 
-       int r = hw_calculate_partition_table(dest);
+       int r = hw_calculate_partition_table(dest, disable_swap);
        if (r)
                return NULL;
 
@@ -486,23 +586,13 @@ struct hw_destination* hw_make_destination(int part_type, struct hw_disk** disks
 }
 
 unsigned long long hw_memory() {
-       FILE* handle = NULL;
-       char line[STRING_SIZE];
-
-       unsigned long long memory = 0;
+       struct sysinfo si;
 
-       /* Calculate amount of memory in machine */
-       if ((handle = fopen("/proc/meminfo", "r"))) {
-               while (fgets(line, sizeof(line), handle)) {
-                       if (!sscanf (line, "MemTotal: %llu kB", &memory)) {
-                               memory = 0;
-                       }
-               }
-
-               fclose(handle);
-       }
+       int r = sysinfo(&si);
+       if (r < 0)
+               return 0;
 
-       return memory * 1024;
+       return si.totalram;
 }
 
 static int hw_zero_out_device(const char* path, int bytes) {
@@ -526,7 +616,17 @@ static int hw_zero_out_device(const char* path, int bytes) {
        return bytes_written;
 }
 
-int hw_create_partitions(struct hw_destination* dest) {
+static int try_open(const char* path) {
+       FILE* f = fopen(path, "r");
+       if (f) {
+               fclose(f);
+               return 0;
+       }
+
+       return -1;
+}
+
+int hw_create_partitions(struct hw_destination* dest, const char* output) {
        // Before we write a new partition table to the disk, we will erase
        // the first couple of megabytes at the beginning of the device to
        // get rid of all left other things like bootloaders and partition tables.
@@ -587,17 +687,17 @@ int hw_create_partitions(struct hw_destination* dest) {
                part_start += dest->size_data;
        }
 
-       if (dest->part_table == HW_PART_TABLE_MSDOS && dest->part_boot_idx > 0) {
+       if (dest->part_boot_idx > 0)
                asprintf(&cmd, "%s set %d boot on", cmd, dest->part_boot_idx);
 
-       } else if (dest->part_table == HW_PART_TABLE_GPT) {
+       if (dest->part_table == HW_PART_TABLE_GPT) {
                if (*dest->part_bootldr) {
                        asprintf(&cmd, "%s set %d bios_grub on", cmd, dest->part_boot_idx);
                }
                asprintf(&cmd, "%s disk_set pmbr_boot on", cmd);
        }
 
-       int r = mysystem(cmd);
+       r = mysystem(output, cmd);
 
        // Wait until the system re-read the partition table
        if (r == 0) {
@@ -606,19 +706,19 @@ int hw_create_partitions(struct hw_destination* dest) {
                while (counter-- > 0) {
                        sleep(1);
 
-                       if (*dest->part_bootldr && (access(dest->part_bootldr, R_OK) != 0))
+                       if (*dest->part_bootldr && (try_open(dest->part_bootldr) != 0))
                                continue;
 
-                       if (*dest->part_boot && (access(dest->part_boot, R_OK) != 0))
+                       if (*dest->part_boot && (try_open(dest->part_boot) != 0))
                                continue;
 
-                       if (*dest->part_swap && (access(dest->part_swap, R_OK) != 0))
+                       if (*dest->part_swap && (try_open(dest->part_swap) != 0))
                                continue;
 
-                       if (*dest->part_root && (access(dest->part_root, R_OK) != 0))
+                       if (*dest->part_root && (try_open(dest->part_root) != 0))
                                continue;
 
-                       if (*dest->part_data && (access(dest->part_data, R_OK) != 0))
+                       if (*dest->part_data && (try_open(dest->part_data) != 0))
                                continue;
 
                        // All partitions do exist, exiting the loop.
@@ -632,7 +732,7 @@ int hw_create_partitions(struct hw_destination* dest) {
        return r;
 }
 
-static int hw_format_filesystem(const char* path, int fs) {
+static int hw_format_filesystem(const char* path, int fs, const char* output) {
        char cmd[STRING_SIZE] = "\0";
 
        // Swap
@@ -657,36 +757,36 @@ static int hw_format_filesystem(const char* path, int fs) {
 
        assert(*cmd);
 
-       int r = mysystem(cmd);
+       int r = mysystem(output, cmd);
 
        return r;
 }
 
-int hw_create_filesystems(struct hw_destination* dest) {
+int hw_create_filesystems(struct hw_destination* dest, const char* output) {
        int r;
 
        // boot
        if (*dest->part_boot) {
-               r = hw_format_filesystem(dest->part_boot, dest->filesystem);
+               r = hw_format_filesystem(dest->part_boot, dest->filesystem, output);
                if (r)
                        return r;
        }
 
        // swap
        if (*dest->part_swap) {
-               r = hw_format_filesystem(dest->part_swap, HW_FS_SWAP);
+               r = hw_format_filesystem(dest->part_swap, HW_FS_SWAP, output);
                if (r)
                        return r;
        }
 
        // root
-       r = hw_format_filesystem(dest->part_root, dest->filesystem);
+       r = hw_format_filesystem(dest->part_root, dest->filesystem, output);
        if (r)
                return r;
 
        // data
        if (*dest->part_data) {
-               r = hw_format_filesystem(dest->part_data, dest->filesystem);
+               r = hw_format_filesystem(dest->part_data, dest->filesystem, output);
                if (r)
                        return r;
        }
@@ -779,42 +879,82 @@ int hw_mount_filesystems(struct hw_destination* dest, const char* prefix) {
 }
 
 int hw_umount_filesystems(struct hw_destination* dest, const char* prefix) {
+       int r;
+       char target[STRING_SIZE];
+
+       // Write all buffers to disk before umounting
+       hw_sync();
+
        // boot
        if (*dest->part_boot) {
-               hw_umount(dest->part_boot);
+               snprintf(target, sizeof(target), "%s%s", prefix, HW_PATH_BOOT);
+               r = hw_umount(target);
+               if (r)
+                       return -1;
        }
 
        // data
        if (*dest->part_data) {
-               hw_umount(dest->part_data);
+               snprintf(target, sizeof(target), "%s%s", prefix, HW_PATH_DATA);
+               r = hw_umount(target);
+               if (r)
+                       return -1;
        }
 
-       // root
-       hw_umount(dest->part_root);
-
        // swap
        if (*dest->part_swap) {
                swapoff(dest->part_swap);
        }
 
        // misc filesystems
-       char target[STRING_SIZE];
        char** otherfs = other_filesystems;
-
        while (*otherfs) {
                snprintf(target, sizeof(target), "%s%s", prefix, *otherfs++);
-               hw_umount(target);
+               r = hw_umount(target);
+               if (r)
+                       return -1;
        }
 
+       // root
+       r = hw_umount(prefix);
+       if (r)
+               return -1;
+
        return 0;
 }
 
-int hw_setup_raid(struct hw_destination* dest) {
+int hw_destroy_raid_superblocks(const struct hw_destination* dest, const char* output) {
+       char cmd[STRING_SIZE];
+
+       hw_stop_all_raid_arrays(output);
+       hw_stop_all_raid_arrays(output);
+
+       if (dest->disk1) {
+               snprintf(cmd, sizeof(cmd), "/sbin/mdadm --zero-superblock %s", dest->disk1->path);
+               mysystem(output, cmd);
+       }
+
+       if (dest->disk2) {
+               snprintf(cmd, sizeof(cmd), "/sbin/mdadm --zero-superblock %s", dest->disk2->path);
+               mysystem(output, cmd);
+       }
+
+       return 0;
+}
+
+int hw_setup_raid(struct hw_destination* dest, const char* output) {
        char* cmd = NULL;
+       int r;
 
        assert(dest->is_raid);
 
-       asprintf(&cmd, "echo \"y\" | /sbin/mdadm --create --verbose --metadata=1.2 %s", dest->path);
+       // Stop all RAID arrays that might be around (again).
+       // It seems that there is some sort of race-condition with udev re-enabling
+       // the raid arrays and therefore locking the disks.
+       r = hw_destroy_raid_superblocks(dest, output);
+
+       asprintf(&cmd, "echo \"y\" | /sbin/mdadm --create --verbose --metadata=%s --auto=mdp %s",
+               RAID_METADATA, dest->path);
 
        switch (dest->raid_level) {
                case 1:
@@ -843,7 +983,7 @@ int hw_setup_raid(struct hw_destination* dest) {
                        return r;
        }
 
-       int r = mysystem(cmd);
+       r = mysystem(output, cmd);
        free(cmd);
 
        // Wait a moment until the device has been properly brought up
@@ -855,47 +995,46 @@ int hw_setup_raid(struct hw_destination* dest) {
                        // If the raid device has not yet been properly brought up,
                        // opening it will fail with the message: Device or resource busy
                        // Hence we will wait a bit until it becomes usable.
-                       FILE* f = fopen(dest->path, "r");
-                       if (f) {
-                               fclose(f);
+                       if (try_open(dest->path) == 0)
                                break;
-                       }
                }
        }
 
        return r;
 }
 
-int hw_stop_all_raid_arrays() {
-       return mysystem("/sbin/mdadm --stop --scan");
+int hw_stop_all_raid_arrays(const char* output) {
+       return mysystem(output, "/sbin/mdadm --stop --scan --verbose");
 }
 
-int hw_install_bootloader(struct hw_destination* dest) {
+int hw_install_bootloader(struct hw_destination* dest, const char* output) {
        char cmd[STRING_SIZE];
        int r;
 
        // Generate configuration file
        snprintf(cmd, sizeof(cmd), "/usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg");
-       r = system_chroot(DESTINATION_MOUNT_PATH, cmd);
+       r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
        if (r)
                return r;
 
        char cmd_grub[STRING_SIZE];
        snprintf(cmd_grub, sizeof(cmd_grub), "/usr/sbin/grub-install --no-floppy --recheck");
 
-       if (dest->is_raid && (dest->part_table == HW_PART_TABLE_MSDOS)) {
+       if (dest->is_raid) {
                snprintf(cmd, sizeof(cmd), "%s %s", cmd_grub, dest->disk1->path);
-               r = system_chroot(DESTINATION_MOUNT_PATH, cmd);
+               r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
                if (r)
                        return r;
 
                snprintf(cmd, sizeof(cmd), "%s %s", cmd_grub, dest->disk2->path);
-               r = system_chroot(DESTINATION_MOUNT_PATH, cmd);
+               r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
        } else {
                snprintf(cmd, sizeof(cmd), "%s %s", cmd_grub, dest->path);
-               r = system_chroot(DESTINATION_MOUNT_PATH, cmd);
+               r = system_chroot(output, DESTINATION_MOUNT_PATH, cmd);
        }
 
+       hw_sync();
+
        return r;
 }
 
@@ -918,12 +1057,13 @@ static char* hw_get_uuid(const char* dev) {
        return uuid;
 }
 
+#define FSTAB_FMT "UUID=%s %-8s %-4s %-10s %d %d\n"
+
 int hw_write_fstab(struct hw_destination* dest) {
        FILE* f = fopen(DESTINATION_MOUNT_PATH "/etc/fstab", "w");
        if (!f)
                return -1;
 
-       const char* fmt = "UUID=%s %-8s %-4s %-10s %d %d\n";
        char* uuid = NULL;
 
        // boot
@@ -931,7 +1071,7 @@ int hw_write_fstab(struct hw_destination* dest) {
                uuid = hw_get_uuid(dest->part_boot);
 
                if (uuid) {
-                       fprintf(f, fmt, uuid, "/boot", "auto", "defaults", 1, 2);
+                       fprintf(f, FSTAB_FMT, uuid, "/boot", "auto", "defaults", 1, 2);
                        free(uuid);
                }
        }
@@ -941,7 +1081,7 @@ int hw_write_fstab(struct hw_destination* dest) {
                uuid = hw_get_uuid(dest->part_swap);
 
                if (uuid) {
-                       fprintf(f, fmt, uuid, "swap", "swap", "defaults,pri=1", 0, 0);
+                       fprintf(f, FSTAB_FMT, uuid, "swap", "swap", "defaults,pri=1", 0, 0);
                        free(uuid);
                }
        }
@@ -949,7 +1089,7 @@ int hw_write_fstab(struct hw_destination* dest) {
        // root
        uuid = hw_get_uuid(dest->part_root);
        if (uuid) {
-               fprintf(f, fmt, uuid, "/", "auto", "defaults", 1, 1);
+               fprintf(f, FSTAB_FMT, uuid, "/", "auto", "defaults", 1, 1);
                free(uuid);
        }
 
@@ -958,7 +1098,7 @@ int hw_write_fstab(struct hw_destination* dest) {
                uuid = hw_get_uuid(dest->part_data);
 
                if (uuid) {
-                       fprintf(f, fmt, uuid, "/var", "auto", "defaults", 1, 1);
+                       fprintf(f, FSTAB_FMT, uuid, "/var", "auto", "defaults", 1, 1);
                        free(uuid);
                }
        }
@@ -967,3 +1107,37 @@ int hw_write_fstab(struct hw_destination* dest) {
 
        return 0;
 }
+
+void hw_sync() {
+       sync();
+       sync();
+       sync();
+}
+
+int hw_start_networking(const char* output) {
+       return mysystem(output, "/usr/bin/start-networking.sh");
+}
+
+char* hw_find_backup_file(const char* output, const char* search_path) {
+       char path[STRING_SIZE];
+
+       snprintf(path, sizeof(path), "%s/backup.ipf", search_path);
+       int r = access(path, R_OK);
+
+       if (r == 0)
+               return strdup(path);
+
+       return NULL;
+}
+
+int hw_restore_backup(const char* output, const char* backup_path, const char* destination) {
+       char command[STRING_SIZE];
+
+       snprintf(command, sizeof(command), "/bin/tar xzpf %s -C %s", backup_path, destination);
+       int rc = mysystem(output, command);
+
+       if (rc)
+               return -1;
+
+       return 0;
+}