diff --git a/libmount/biosboot.c b/libmount/biosboot.c index ae18deea..8abb5711 100644 --- a/libmount/biosboot.c +++ b/libmount/biosboot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -69,6 +69,7 @@ static enum filesystem_error biosboot_inspect(struct filesystem** fs_ptr, fs->handler = &biosboot_handler; fs->handler_private = NULL; fs->fstype_name = "biosboot"; + fs->flags = FILESYSTEM_FLAG_NOT_FILESYSTEM; return *fs_ptr = fs, FILESYSTEM_ERROR_NONE; } diff --git a/libmount/extended.c b/libmount/extended.c index 70569ecf..16f0937f 100644 --- a/libmount/extended.c +++ b/libmount/extended.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -67,6 +67,7 @@ static enum filesystem_error extended_inspect(struct filesystem** fs_ptr, fs->handler = &extended_handler; fs->handler_private = NULL; fs->fstype_name = "extended"; + fs->flags = FILESYSTEM_FLAG_NOT_FILESYSTEM; return *fs_ptr = fs, FILESYSTEM_ERROR_NONE; } diff --git a/libmount/include/mount/filesystem.h b/libmount/include/mount/filesystem.h index 04394c35..b23f22b4 100644 --- a/libmount/include/mount/filesystem.h +++ b/libmount/include/mount/filesystem.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -35,6 +35,7 @@ struct filesystem_handler; #define FILESYSTEM_FLAG_UUID (1 << 0) #define FILESYSTEM_FLAG_FSCK_SHOULD (1 << 1) #define FILESYSTEM_FLAG_FSCK_MUST (1 << 2) +#define FILESYSTEM_FLAG_NOT_FILESYSTEM (1 << 3) struct filesystem { diff --git a/share/man/man7/installation.7 b/share/man/man7/installation.7 index 519303a1..a4dc47e5 100644 --- a/share/man/man7/installation.7 +++ b/share/man/man7/installation.7 @@ -148,7 +148,8 @@ bootloader to boot the operating system. You will be offered the choice of installing GRUB as the bootloader. Note however that this GRUB is not able to detect other operating systems and you will have to configure it manually if you wish to use it in a dual boot scheme. The answer will default to yes if no -existing partitions are found, and will default to no if some are found. +existing partitions are found, or if an existing Sortix installation is found +that uses the provided bootloader; and will otherwise default to no. .Pp Single-boot configurations should use the offered bootloader. Dual-boot configurations should refuse it and arrange for bootloading by other means. The diff --git a/sysinstall/Makefile b/sysinstall/Makefile index 5eb6b6a2..f74c9a45 100644 --- a/sysinstall/Makefile +++ b/sysinstall/Makefile @@ -27,7 +27,7 @@ release.o \ OBJS=$(MAIN_OBJS) $(UTIL_OBJS) -SYSINSTALL_DEPS=devices execute fileops interactive manifest +SYSINSTALL_DEPS=conf devices execute fileops interactive manifest release SYSMERGE_DEPS=conf fileops execute hooks manifest release SYSUPGRADE_DEPS=conf devices execute fileops hooks interactive manifest release diff --git a/sysinstall/devices.c b/sysinstall/devices.c index 6cc48f67..bdc3acf5 100644 --- a/sysinstall/devices.c +++ b/sysinstall/devices.c @@ -173,19 +173,6 @@ struct filesystem* search_for_filesystem_by_spec(const char* spec) return NULL; } -bool check_existing_systems(void) -{ - for ( size_t di = 0; di < hds_count; di++ ) - { - struct blockdevice* dbdev = &hds[di]->bdev; - if ( dbdev->fs ) - return true; - else if ( dbdev->pt ) - return 1 <= dbdev->pt->partitions_count; - } - return false; -} - bool check_lacking_partition_table(void) { for ( size_t di = 0; di < hds_count; di++ ) @@ -324,7 +311,7 @@ bool load_mountpoints(const char* fstab_path, return true; } -void mountpoint_mount(struct mountpoint* mountpoint) +bool mountpoint_mount(struct mountpoint* mountpoint) { struct filesystem* fs = mountpoint->fs; // TODO: It would be ideal to get an exclusive lock so that no other @@ -333,17 +320,29 @@ void mountpoint_mount(struct mountpoint* mountpoint) const char* bdev_path = bdev->p ? bdev->p->path : bdev->hd->path; assert(bdev_path); if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST && !fsck(fs) ) - errx(2, "Failed to fsck %s", bdev_path); + { + warnx("Failed to fsck %s", bdev_path); + return false; + } if ( !fs->driver ) - errx(2, "%s: Don't know how to mount a %s filesystem", - bdev_path, fs->fstype_name); + { + warnx("%s: Don't know how to mount a %s filesystem", + bdev_path, fs->fstype_name); + return false; + } const char* pretend_where = mountpoint->entry.fs_file; const char* where = mountpoint->absolute; struct stat st; if ( stat(where, &st) < 0 ) - err(2, "stat: %s", where); + { + warn("stat: %s", where); + return false; + } if ( (mountpoint->pid = fork()) < 0 ) - err(2, "%s: Unable to mount: fork", bdev_path); + { + warn("%s: Unable to mount: fork", bdev_path); + return false; + } // TODO: This design is broken. The filesystem should tell us when it is // ready instead of having to poll like this. if ( mountpoint->pid == 0 ) @@ -366,36 +365,41 @@ void mountpoint_mount(struct mountpoint* mountpoint) int code; waitpid(mountpoint->pid, &code, 0); mountpoint->pid = -1; - exit(2); + return false; } if ( newst.st_dev != st.st_dev || newst.st_ino != st.st_ino ) break; int code; pid_t child = waitpid(mountpoint->pid, &code, WNOHANG); if ( child < 0 ) - err(2, "waitpid"); + { + warn("waitpid"); + return false; + } if ( child != 0 ) { mountpoint->pid = -1; if ( WIFSIGNALED(code) ) - errx(2, "%s: Mount failed: %s: %s", bdev_path, fs->driver, - strsignal(WTERMSIG(code))); + warnx("%s: Mount failed: %s: %s", bdev_path, fs->driver, + strsignal(WTERMSIG(code))); else if ( !WIFEXITED(code) ) - errx(2, "%s: Mount failed: %s: %s", bdev_path, fs->driver, - "Unexpected unusual termination"); + warnx("%s: Mount failed: %s: %s", bdev_path, fs->driver, + "Unexpected unusual termination"); else if ( WEXITSTATUS(code) == 127 ) - errx(2, "%s: Mount failed: %s: %s", bdev_path, fs->driver, - "Filesystem driver is absent"); + warnx("%s: Mount failed: %s: %s", bdev_path, fs->driver, + "Filesystem driver is absent"); else if ( WEXITSTATUS(code) == 0 ) - errx(2, "%s: Mount failed: %s: Unexpected successful exit", - bdev_path, fs->driver); + warnx("%s: Mount failed: %s: Unexpected successful exit", + bdev_path, fs->driver); else - errx(2, "%s: Mount failed: %s: Exited with status %i", bdev_path, - fs->driver, WEXITSTATUS(code)); + warnx("%s: Mount failed: %s: Exited with status %i", bdev_path, + fs->driver, WEXITSTATUS(code)); + return false; } struct timespec delay = timespec_make(0, 50L * 1000L * 1000L); nanosleep(&delay, NULL); } + return true; } void mountpoint_unmount(struct mountpoint* mountpoint) diff --git a/sysinstall/devices.h b/sysinstall/devices.h index faf4b20d..faa7aa0a 100644 --- a/sysinstall/devices.h +++ b/sysinstall/devices.h @@ -42,14 +42,13 @@ void unscan_devices(void); void scan_devices(void); struct filesystem* search_for_filesystem_by_uuid(const unsigned char* uuid); struct filesystem* search_for_filesystem_by_spec(const char* spec); -bool check_existing_systems(void); bool check_lacking_partition_table(void); bool fsck(struct filesystem* fs); void free_mountpoints(struct mountpoint* mnts, size_t mnts_count); bool load_mountpoints(const char* fstab_path, struct mountpoint** mountpoints_out, size_t* mountpoints_used_out); -void mountpoint_mount(struct mountpoint* mountpoint); +bool mountpoint_mount(struct mountpoint* mountpoint); void mountpoint_unmount(struct mountpoint* mountpoint); #endif diff --git a/sysinstall/sysinstall.c b/sysinstall/sysinstall.c index bc8df5f4..79d5f142 100644 --- a/sysinstall/sysinstall.c +++ b/sysinstall/sysinstall.c @@ -51,11 +51,13 @@ #include #include +#include "conf.h" #include "devices.h" #include "execute.h" #include "fileops.h" #include "interactive.h" #include "manifest.h" +#include "release.h" const char* prompt_man_section = "7"; const char* prompt_man_page = "installation"; @@ -105,6 +107,116 @@ static bool missing_bios_boot_partition(struct filesystem* root_fs) return !search_bios_boot_search(pt); } +static bool should_install_bootloader_path(const char* mnt, + struct blockdevice* bdev) +{ + char* release_errpath; + if ( asprintf(&release_errpath, "%s: /etc/sortix-release", + path_of_blockdevice(bdev)) < 0 ) + { + warn("malloc"); + return false; + } + char* release_path; + if ( asprintf(&release_path, "%s/etc/sortix-release", mnt) < 0 ) + { + warn("malloc"); + free(release_errpath); + return false; + } + struct release release; + if ( !os_release_load(&release, release_path, release_errpath) ) + { + free(release_path); + free(release_errpath); + return false; + } + free(release_path); + free(release_errpath); + char* conf_path; + if ( asprintf(&conf_path, "%s/etc/upgrade.conf", mnt) < 0 ) + { + warn("malloc"); + return false; + } + // TODO: The load_upgrade_conf function might exit the process on failure, + // but we don't want that. Redesign the mountpoint code so the caller + // controls this. + pid_t pid = fork(); + if ( pid < 0 ) + { + warn("fork"); + free(conf_path); + return false; + } + if ( !pid ) + { + struct conf conf; + load_upgrade_conf(&conf, conf_path); + bool should = conf.grub; + _exit(should ? 0 : 1); + } + int status; + if ( waitpid(pid, &status, 0) < 0 ) + return false; + return WIFEXITED(status) && WEXITSTATUS(status) == 0; +} + +static bool should_install_bootloader_bdev(struct blockdevice* bdev) +{ + if ( !bdev->fs ) + return false; + if ( bdev->fs->flags & FILESYSTEM_FLAG_NOT_FILESYSTEM ) + return false; + if ( !bdev->fs->driver ) + return false; + char mnt[] = "/tmp/fs.XXXXXX"; + if ( !mkdtemp(mnt) ) + { + warn("mkdtemp: %s", "/tmp/fs.XXXXXX"); + return false; + } + struct mountpoint mp = { 0 }; + mp.absolute = mnt; + mp.fs = bdev->fs; + mp.entry.fs_file = mnt; + if ( !mountpoint_mount(&mp) ) + { + rmdir(mnt); + return false; + } + bool should = should_install_bootloader_path(mnt, bdev); + mountpoint_unmount(&mp); + rmdir(mnt); + return should; +} + +static bool should_install_bootloader(void) +{ + bool any_systems = false; + for ( size_t i = 0; i < hds_count; i++ ) + { + struct harddisk* hd = hds[i]; + if ( hd->bdev.pt ) + { + for ( size_t n = 0; n < hd->bdev.pt->partitions_count; n++ ) + { + any_systems = true; + struct partition* p = hd->bdev.pt->partitions[n]; + if ( should_install_bootloader_bdev(&p->bdev) ) + return true; + } + } + else if ( hd->bdev.fs ) + { + any_systems = true; + if ( should_install_bootloader_bdev(&hd->bdev) ) + return true; + } + } + return !any_systems; +} + static bool passwd_check(const char* passwd_path, bool (*check)(struct passwd*, void*), void* check_ctx) @@ -460,7 +572,10 @@ int main(void) text("\n"); } + text("Searching for existing installations...\n"); scan_devices(); + bool bootloader_default = should_install_bootloader(); + text("\n"); textf("You need a bootloader to start the operating system. GRUB is the " "standard %s bootloader and this installer comes with a copy. " @@ -476,9 +591,7 @@ int main(void) char grub_password[512]; while ( true ) { - const char* def = "yes"; - if ( check_existing_systems() ) - def = "no"; + const char* def = bootloader_default ? "yes" : "no"; prompt(accept_grub, sizeof(accept_grub), "Install a new GRUB bootloader?", def); if ( strcasecmp(accept_grub, "no") == 0 || @@ -704,7 +817,8 @@ int main(void) mnt->absolute = absolute; if ( mkdir_p(mnt->absolute, 0755) < 0 ) err(2, "mkdir: %s", mnt->absolute); - mountpoint_mount(mnt); + if ( !mountpoint_mount(mnt) ) + exit(2); } if ( chdir(fs) < 0 )