mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Fix system upgrade leaking files.
This commit is contained in:
parent
29598b4fde
commit
cb590ff205
18 changed files with 2897 additions and 237 deletions
8
Makefile
8
Makefile
|
@ -74,6 +74,14 @@ all: sysroot
|
|||
sysmerge: sysroot
|
||||
sysmerge "$(SYSROOT)"
|
||||
|
||||
.PHONY: sysmerge-full
|
||||
sysmerge-full: sysroot
|
||||
sysmerge --full "$(SYSROOT)"
|
||||
|
||||
.PHONY: sysmerge-full-wait
|
||||
sysmerge-full-wait: sysroot
|
||||
sysmerge --full --wait "$(SYSROOT)"
|
||||
|
||||
.PHONY: sysmerge-wait
|
||||
sysmerge-wait: sysroot
|
||||
sysmerge --wait "$(SYSROOT)"
|
||||
|
|
|
@ -71,6 +71,7 @@ This takes precedence over and disables the behavior described under
|
|||
.Sy src .
|
||||
.It Sy ports Ns "=" Ns Oo Sy no "|" yes Oc (default Sy yes ) .
|
||||
Install the new ports.
|
||||
Ports that don't exist anymore will be removed.
|
||||
.It Sy src Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no ) .
|
||||
Place the new source code in
|
||||
.Pa /src
|
||||
|
|
|
@ -121,10 +121,22 @@ and place it in the current directory as
|
|||
Upgrade the current operating system using the sysroot after making the
|
||||
.Sy all
|
||||
target.
|
||||
.It Sy sysmerge-full
|
||||
Like
|
||||
.Sy sysmerge
|
||||
but do a full operating system upgrade that uninstalls ports not present in the
|
||||
sysroot using
|
||||
.Fl \-full .
|
||||
.It Sy sysmerge-full-wait
|
||||
The combination of
|
||||
.Sy sysmerge-full
|
||||
and
|
||||
.Sy sysmerge-full-wait .
|
||||
.It Sy sysmerge-wait
|
||||
Like
|
||||
.Sy sysmerge
|
||||
but delay the upgrade until the next boot.
|
||||
but delay the upgrade until the next boot using
|
||||
.Fl \-wait .
|
||||
.It Sy sysroot-base-headers
|
||||
Create the sysroot and install only the headers of the standard library and
|
||||
kernel into it.
|
||||
|
@ -190,6 +202,16 @@ For instance, to build and install libc, run as root:
|
|||
make install
|
||||
.Ed
|
||||
.Pp
|
||||
Note the individual makefiles only install the new system files and leak any
|
||||
files that don't exist anymore; and they also don't run any upgrade hooks to
|
||||
migrate the current system.
|
||||
This mechanism isn't supported unless you are building the same source code as
|
||||
the current operating system.
|
||||
The global
|
||||
.Sy sysmerge
|
||||
makefile targets should be used instead as the supported mechanism for operating
|
||||
system upgrades.
|
||||
.Pp
|
||||
System libraries are statically linked and you will have to relink programs with
|
||||
the new library for changes to take effect.
|
||||
Building the whole operating system from the root makefile ensures components
|
||||
|
|
|
@ -69,6 +69,26 @@ releasing Sortix x.y, foo." to allow the maintainer to easily
|
|||
.Xr grep 1
|
||||
for it after a release.
|
||||
.Sh CHANGES
|
||||
.Ss Fix system upgrade leaking files
|
||||
.Xr sysupgrade 8
|
||||
and
|
||||
.Xr sysmerge 8
|
||||
will now delete files that no longer exist in the new system and ports.
|
||||
However, files may already have leaked if a 1.0 installation was upgraded to
|
||||
a development build prior to this change.
|
||||
An upgrade hook will delete any well known leaked files.
|
||||
.Pp
|
||||
Note:
|
||||
You must use the
|
||||
.Fl \-wait
|
||||
option to do a two-stage upgrade if doing a
|
||||
.Xr sysmerge 8
|
||||
upgrade from an installation prior to this change to a version after this
|
||||
change.
|
||||
This requirement is because the old
|
||||
.Xr sysmerge 8
|
||||
will leak files and the upgrade hook only deal with well known files as of this
|
||||
change, and doesn't handle future changes.
|
||||
.Ss Fix /tix/manifest permissions in installations
|
||||
The
|
||||
.Pa /tix/manifest
|
||||
|
|
|
@ -134,6 +134,10 @@ all.
|
|||
You can configure which ports gets loaded using the bootloader menu.
|
||||
The base system is rather lean and can be made quite small.
|
||||
You need some ports to complete an installation.
|
||||
Only the selected ports are loaded into the live environment and installed onto
|
||||
the new installation.
|
||||
If upgrading an existing installation, then any ports not loaded will be removed
|
||||
from the installation being upgraded.
|
||||
.Ss Installer
|
||||
This guide assumes you selected the operating system installation option in the
|
||||
bootloader.
|
||||
|
|
|
@ -122,7 +122,8 @@ changes.
|
|||
.It
|
||||
Updating the system.
|
||||
.It
|
||||
Updating the ports.
|
||||
Updating the ports, installing any new ports, and removing any ports that
|
||||
don't exist anymore or weren't loaded.
|
||||
.It
|
||||
Updating the source code.
|
||||
.It
|
||||
|
|
|
@ -24,12 +24,13 @@ hooks.o \
|
|||
interactive.o \
|
||||
manifest.o \
|
||||
release.o \
|
||||
string_array.o \
|
||||
|
||||
OBJS=$(MAIN_OBJS) $(UTIL_OBJS)
|
||||
|
||||
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
|
||||
SYSINSTALL_DEPS=conf devices execute fileops interactive manifest release string_array
|
||||
SYSMERGE_DEPS=conf fileops execute hooks manifest release string_array
|
||||
SYSUPGRADE_DEPS=conf devices execute fileops hooks interactive manifest release string_array
|
||||
|
||||
SYSINSTALL_OBJS:=sysinstall.o $(SYSINSTALL_DEPS:=.o)
|
||||
SYSMERGE_OBJS:=sysmerge.o $(SYSMERGE_DEPS:=.o)
|
||||
|
@ -52,6 +53,7 @@ install: all
|
|||
# TODO: After releasing Sortix 1.1, remove this compatibility.
|
||||
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-random-seed
|
||||
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-tix-manifest-mode
|
||||
touch $(DESTDIR)$(DATADIR)/sysinstall/hooks/sortix-1.1-leaked-files
|
||||
|
||||
sysinstall: $(SYSINSTALL_OBJS)
|
||||
$(CC) $(SYSINSTALL_OBJS) -o $@ -lmount
|
||||
|
@ -71,10 +73,11 @@ sysupgrade.o: $(SYSUPGRADE_DEPS:=.h)
|
|||
conf.o: conf.h
|
||||
devices.o: devices.h
|
||||
execute.o: execute.h
|
||||
fileops.o: fileops.h
|
||||
hooks.o: fileops.h release.h
|
||||
fileops.o: fileops.h string_array.h
|
||||
string_array.o: string_array.h
|
||||
hooks.o: fileops.h manifest.h release.h string_array.h
|
||||
interactive.o: interactive.h execute.h
|
||||
manifest.o: manifest.h execute.h fileops.h
|
||||
manifest.o: manifest.h fileops.h string_array.h
|
||||
release.o: release.h
|
||||
|
||||
clean:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2016, 2017 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2015, 2016, 2017, 2020 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
|
||||
|
@ -32,6 +32,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "fileops.h"
|
||||
#include "string_array.h"
|
||||
|
||||
char* join_paths(const char* a, const char* b)
|
||||
{
|
||||
|
@ -176,3 +177,45 @@ char* read_string_file(const char* path)
|
|||
content[amount] = '\0';
|
||||
return content;
|
||||
}
|
||||
|
||||
char** read_lines_file(const char* path, size_t* out_count)
|
||||
{
|
||||
FILE* fp = fopen(path, "r");
|
||||
if ( !fp )
|
||||
return NULL;
|
||||
size_t count;
|
||||
size_t length;
|
||||
char** lines;
|
||||
if ( !string_array_init(&lines, &count, &length) )
|
||||
{
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
char* line = NULL;
|
||||
size_t line_size = 0;
|
||||
ssize_t line_length;
|
||||
while ( 0 < (errno = 0, line_length = getline(&line, &line_size, fp)) )
|
||||
{
|
||||
if ( line[line_length-1] == '\n' )
|
||||
line[--line_length] = '\0';
|
||||
if ( !string_array_append_nodup(&lines, &count, &length, line) )
|
||||
{
|
||||
free(line);
|
||||
string_array_free(&lines, &count, &length);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
line = NULL;
|
||||
line_size = 0;
|
||||
}
|
||||
free(line);
|
||||
if ( ferror(fp) )
|
||||
{
|
||||
string_array_free(&lines, &count, &length);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
fclose(fp);
|
||||
*out_count = count;
|
||||
return lines;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2015, 2016, 2020 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
|
||||
|
@ -26,5 +26,6 @@ int access_or_die(const char* path, int mode);
|
|||
void mkdir_or_chmod_or_die(const char* path, mode_t mode);
|
||||
void write_random_seed(const char* path);
|
||||
char* read_string_file(const char* path);
|
||||
char** read_lines_file(const char* path, size_t* out_count);
|
||||
|
||||
#endif
|
||||
|
|
2003
sysinstall/hooks.c
2003
sysinstall/hooks.c
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2018 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2015, 2018, 2020, 2021 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
|
||||
|
@ -27,27 +27,85 @@
|
|||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "execute.h"
|
||||
#include "fileops.h"
|
||||
#include "manifest.h"
|
||||
#include "string_array.h"
|
||||
|
||||
bool has_manifest(const char* manifest)
|
||||
{
|
||||
char* path;
|
||||
if ( asprintf(&path, "/tix/manifest/%s", manifest) < 0 )
|
||||
char* path = join_paths("/tix/manifest", manifest);
|
||||
if ( !path )
|
||||
{
|
||||
warn("asprintf");
|
||||
_exit(2);
|
||||
}
|
||||
bool result = access(path, F_OK) == 0;
|
||||
bool result = access_or_die(path, F_OK) == 0;
|
||||
free(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
char** read_manifest(const char* path, size_t* out_count)
|
||||
{
|
||||
char** files = read_lines_file(path, out_count);
|
||||
if ( !files )
|
||||
return NULL;
|
||||
// TODO: Remove this compatibility after releasing Sortix 1.1. The manifests
|
||||
// in Sortix 1.0 have spurious trailing slashes due to a bug in the
|
||||
// kernel binary package extractor. Remove them here to normalize the
|
||||
// manifests.
|
||||
for ( size_t i = 0; i < *out_count; i++ )
|
||||
{
|
||||
char* file = files[i];
|
||||
size_t len = strlen(file);
|
||||
if ( 2 <= len && file[len - 1] == '/' )
|
||||
file[len - 1] = '\0';
|
||||
}
|
||||
string_array_sort_strcmp(files, *out_count);
|
||||
return files;
|
||||
}
|
||||
|
||||
static void unlink_rename_conflict(const char* path)
|
||||
{
|
||||
if ( !unlink(path) || errno == ENOENT )
|
||||
return;
|
||||
if ( errno != EISDIR )
|
||||
{
|
||||
warn("unlink: %s", path);
|
||||
_exit(2);
|
||||
}
|
||||
if ( !rmdir(path) )
|
||||
return;
|
||||
if ( errno != ENOTEMPTY && errno != EEXIST )
|
||||
{
|
||||
warn("rmdir: %s", path);
|
||||
_exit(2);
|
||||
}
|
||||
char* conflict;
|
||||
if ( asprintf(&conflict, "%s.conflict.XXXXXX", path) < 0 )
|
||||
{
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
if ( !mkdtemp(conflict) )
|
||||
{
|
||||
warn("mkdtemp: %s.conflict.XXXXXX", path);
|
||||
_exit(2);
|
||||
}
|
||||
if ( rename(path, conflict) < 0 )
|
||||
{
|
||||
warn("rename: %s -> %s", path, conflict);
|
||||
rmdir(conflict);
|
||||
_exit(2);
|
||||
}
|
||||
printf("warning: Moving conflicting directory %s to %s\n", path, conflict);
|
||||
free(conflict);
|
||||
}
|
||||
|
||||
struct hardlink
|
||||
{
|
||||
dev_t dev;
|
||||
|
@ -57,9 +115,10 @@ struct hardlink
|
|||
|
||||
void install_manifest(const char* manifest,
|
||||
const char* from_prefix,
|
||||
const char* to_prefix)
|
||||
const char* to_prefix,
|
||||
const char* const* preserved,
|
||||
size_t preserved_count)
|
||||
{
|
||||
printf(" - Installing %s...\n", manifest);
|
||||
struct hardlink* hardlinks = NULL;
|
||||
size_t hardlinks_used = 0;
|
||||
size_t hardlinks_length = 0;
|
||||
|
@ -71,52 +130,141 @@ void install_manifest(const char* manifest,
|
|||
_exit(2);
|
||||
}
|
||||
mode_t old_umask = umask(0000);
|
||||
// Read the input and output manifests if they exist. Consider a manifest
|
||||
// that doesn't exist as being empty.
|
||||
char* inmanifest;
|
||||
if ( asprintf(&inmanifest, "%s/tix/manifest/%s", from_prefix, manifest) < 0 )
|
||||
{
|
||||
warn("asprintf");
|
||||
_exit(2);
|
||||
}
|
||||
char* outmanifest;
|
||||
if ( asprintf(&outmanifest, "%s/tix/manifest/%s", to_prefix, manifest) < 0 )
|
||||
char* outnewmanifest;
|
||||
if ( asprintf(&inmanifest, "%s/tix/manifest/%s", from_prefix,
|
||||
manifest) < 0 ||
|
||||
asprintf(&outmanifest, "%s/tix/manifest/%s", to_prefix,
|
||||
manifest) < 0 ||
|
||||
asprintf(&outnewmanifest, "%s/tix/manifest/%s.new", to_prefix,
|
||||
manifest) < 0 )
|
||||
{
|
||||
warn("asprintf");
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
FILE* fpin = fopen(inmanifest, "r");
|
||||
if ( !fpin )
|
||||
bool in_exists = !access_or_die(inmanifest, F_OK);
|
||||
bool out_exists = !access_or_die(outmanifest, F_OK);
|
||||
const char* action = in_exists && out_exists ? "Upgrading" :
|
||||
in_exists ? "Installing" :
|
||||
"Uninstalling";
|
||||
printf(" - %s %s...\n", action, manifest);
|
||||
char** empty = (char*[]){};
|
||||
char** in_files = empty;
|
||||
size_t in_files_count = 0;
|
||||
if ( in_exists &&
|
||||
!(in_files = read_manifest(inmanifest, &in_files_count)) )
|
||||
{
|
||||
warn("%s", inmanifest);
|
||||
_exit(2);
|
||||
}
|
||||
FILE* fpout = fopen(outmanifest, "w");
|
||||
if ( !fpout )
|
||||
char** out_files = empty;
|
||||
size_t out_files_count = 0;
|
||||
if ( out_exists &&
|
||||
!(out_files = read_manifest(outmanifest, &out_files_count)) )
|
||||
{
|
||||
warn("%s", outmanifest);
|
||||
_exit(2);
|
||||
}
|
||||
char* line = NULL;
|
||||
size_t line_size = 0;
|
||||
ssize_t line_length;
|
||||
while ( 0 < (line_length = getline(&line, &line_size, fpin)) )
|
||||
// Directories to be cleaned up afterwards when they might be empty.
|
||||
size_t rmdirs_count;
|
||||
size_t rmdirs_length;
|
||||
char** rmdirs;
|
||||
if ( !string_array_init(&rmdirs, &rmdirs_count, &rmdirs_length) )
|
||||
{
|
||||
if ( line[line_length-1] == '\n' )
|
||||
line[--line_length] = '\0';
|
||||
if ( fprintf(fpout, "%s\n", line) < 0 )
|
||||
{
|
||||
warn("write: %s", outmanifest);
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
if ( line[0] != '/' )
|
||||
continue;
|
||||
char* in_path;
|
||||
if ( asprintf(&in_path, "%s%s", from_prefix, line) < 0 )
|
||||
// Find the differences by mutually iterating the manifests in sorted
|
||||
// order.
|
||||
size_t in_i = 0;
|
||||
size_t out_i = 0;
|
||||
while ( in_i < in_files_count || out_i < out_files_count )
|
||||
{
|
||||
const char* in = in_i < in_files_count ? in_files[in_i] : NULL;
|
||||
const char* out = out_i < out_files_count ? out_files[out_i] : NULL;
|
||||
if ( !in || (out && strcmp(in, out) > 0) )
|
||||
{
|
||||
out_i++;
|
||||
const char* path = out;
|
||||
char* out_path = join_paths(to_prefix, path);
|
||||
if ( !out_path )
|
||||
{
|
||||
warn("asprintf");
|
||||
_exit(2);
|
||||
}
|
||||
char* out_path = line;
|
||||
if ( asprintf(&out_path, "%s%s", to_prefix, line) < 0 )
|
||||
// Don't delete a path if it will be added in later by another
|
||||
// manifest. This supports files moving from one manifest to another
|
||||
// and directories only being cleaned up when no manifest mentions
|
||||
// them.
|
||||
if ( string_array_contains_bsearch_strcmp(preserved,
|
||||
preserved_count, path) )
|
||||
{
|
||||
// Handle a directory becoming a symbolic link, which will be
|
||||
// renamed to a conflict directory and replaced with a symbolic
|
||||
// link, but we must take care not to delete anything through
|
||||
// the symbolic link. This case happens if the directory becomes
|
||||
// a symlink in another manifest.
|
||||
struct stat outst;
|
||||
if ( !lstat(out_path, &outst) )
|
||||
{
|
||||
if ( S_ISLNK(outst.st_mode) )
|
||||
{
|
||||
size_t path_length = strlen(path);
|
||||
while ( out_i < out_files_count &&
|
||||
!strncmp(path, out_files[out_i], path_length) &&
|
||||
out_files[out_i][path_length] == '/' )
|
||||
out_i++;
|
||||
}
|
||||
}
|
||||
else if ( errno != ENOENT && errno != ENOTDIR )
|
||||
{
|
||||
warn("%s", out_path);
|
||||
_exit(2);
|
||||
}
|
||||
free(out_path);
|
||||
continue;
|
||||
}
|
||||
if ( unlink(out_path) < 0 )
|
||||
{
|
||||
if ( errno == EISDIR )
|
||||
{
|
||||
if ( rmdir(out_path) < 0 )
|
||||
{
|
||||
if ( errno == ENOTEMPTY || errno == EEXIST )
|
||||
{
|
||||
if ( !string_array_append(&rmdirs, &rmdirs_count,
|
||||
&rmdirs_length, path) )
|
||||
{
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
else if ( errno != ENOENT )
|
||||
{
|
||||
warn("unlink: %s", out_path);
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( errno != ENOENT )
|
||||
{
|
||||
warn("unlink: %s", out_path);
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
free(out_path);
|
||||
continue;
|
||||
}
|
||||
in_i++;
|
||||
if ( out && !strcmp(in, out) )
|
||||
out_i++;
|
||||
const char* path = in;
|
||||
char* in_path = join_paths(from_prefix, path);
|
||||
char* out_path = join_paths(to_prefix, path);
|
||||
if ( !in_path || !out_path )
|
||||
{
|
||||
warn("asprintf");
|
||||
_exit(2);
|
||||
|
@ -141,7 +289,7 @@ void install_manifest(const char* manifest,
|
|||
}
|
||||
if ( hardlink )
|
||||
{
|
||||
unlink(out_path);
|
||||
unlink_rename_conflict(out_path);
|
||||
if ( link(hardlink->path, out_path) < 0 )
|
||||
{
|
||||
warn("link: %s -> %s", hardlink->path, out_path);
|
||||
|
@ -150,23 +298,38 @@ void install_manifest(const char* manifest,
|
|||
}
|
||||
else if ( S_ISDIR(inst.st_mode) )
|
||||
{
|
||||
if ( mkdir(out_path, inst.st_mode & 07777) < 0 && errno != EEXIST )
|
||||
if ( unlink(out_path) < 0 && errno != ENOENT && errno != EISDIR )
|
||||
{
|
||||
warn("unlink: %s", out_path);
|
||||
_exit(2);
|
||||
}
|
||||
if ( mkdir(out_path, inst.st_mode & 07777) < 0 )
|
||||
{
|
||||
if ( errno == EEXIST )
|
||||
{
|
||||
if ( chmod(out_path, inst.st_mode & 07777) < 0 )
|
||||
{
|
||||
warn("chmod: %s", out_path);
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("mkdir: %s", out_path);
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( S_ISREG(inst.st_mode) )
|
||||
{
|
||||
|
||||
unlink_rename_conflict(out_path);
|
||||
int in_fd = open(in_path, O_RDONLY);
|
||||
if ( in_fd < 0 )
|
||||
{
|
||||
warn("%s", in_path);
|
||||
_exit(2);
|
||||
}
|
||||
unlink(out_path);
|
||||
int out_fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC,
|
||||
int out_fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
|
||||
inst.st_mode & 07777);
|
||||
if ( out_fd < 0 )
|
||||
{
|
||||
|
@ -183,7 +346,8 @@ void install_manifest(const char* manifest,
|
|||
}
|
||||
if ( amount == 0 )
|
||||
break;
|
||||
if ( writeall(out_fd, buffer, (size_t) amount) < (size_t) amount )
|
||||
if ( writeall(out_fd, buffer, (size_t) amount) <
|
||||
(size_t) amount )
|
||||
{
|
||||
warn("write: %s", out_path);
|
||||
_exit(2);
|
||||
|
@ -195,17 +359,17 @@ void install_manifest(const char* manifest,
|
|||
{
|
||||
if ( hardlinks_used == hardlinks_length )
|
||||
{
|
||||
// TODO: Multiplication overflow.
|
||||
size_t new_length = hardlinks_length ? 2 * hardlinks_length : 16;
|
||||
struct hardlink* new_hardlinks = (struct hardlink*)
|
||||
reallocarray(hardlinks, new_length, sizeof(struct hardlink));
|
||||
size_t new_length = hardlinks_length ? hardlinks_length : 8;
|
||||
struct hardlink* new_hardlinks =
|
||||
reallocarray(hardlinks, new_length,
|
||||
2 * sizeof(struct hardlink));
|
||||
if ( !new_hardlinks )
|
||||
{
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
hardlinks = new_hardlinks;
|
||||
hardlinks_length = new_length;
|
||||
hardlinks_length = 2 * new_length;
|
||||
}
|
||||
hardlinks[hardlinks_used].ino = inst.st_ino;
|
||||
hardlinks[hardlinks_used].dev = inst.st_dev;
|
||||
|
@ -226,12 +390,22 @@ void install_manifest(const char* manifest,
|
|||
_exit(2);
|
||||
}
|
||||
buffer[amount] = '\0';
|
||||
unlink(out_path);
|
||||
unlink_rename_conflict(out_path);
|
||||
if ( symlink(buffer, out_path) < 0 && errno != EEXIST )
|
||||
{
|
||||
warn("symlink: %s", out_path);
|
||||
_exit(2);
|
||||
}
|
||||
// Handle a directory becoming a symbolic link, which will be
|
||||
// renamed to a conflict directory and replaced with a symbolic
|
||||
// link, but we must take care not to delete anything through
|
||||
// the symbolic link. This case happens if the directory becomes a
|
||||
// symlink in the same manifest.
|
||||
size_t path_length = strlen(path);
|
||||
while ( out_i < out_files_count &&
|
||||
!strncmp(path, out_files[out_i], path_length) &&
|
||||
out_files[out_i][path_length] == '/' )
|
||||
out_i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -241,20 +415,218 @@ void install_manifest(const char* manifest,
|
|||
free(in_path);
|
||||
free(out_path);
|
||||
}
|
||||
free(line);
|
||||
if ( ferror(fpin) )
|
||||
// Delete directories that might not be empty in backwards order to ensure
|
||||
// subdirectories are deleted before their parent directories.
|
||||
for ( size_t i = rmdirs_count; i; i-- )
|
||||
{
|
||||
warn("%s", inmanifest);
|
||||
const char* path = rmdirs[i - 1];
|
||||
char* out_path;
|
||||
if ( asprintf(&out_path, "%s%s", to_prefix, path) < 0 )
|
||||
{
|
||||
warn("asprintf");
|
||||
_exit(2);
|
||||
}
|
||||
fclose(fpin);
|
||||
if ( fclose(fpout) == EOF )
|
||||
if ( rmdir(out_path) < 0 &&
|
||||
errno != ENOTEMPTY && errno != EEXIST && errno != ENOENT )
|
||||
{
|
||||
warn("close: %s", outmanifest);
|
||||
warn("unlink: %s", out_path);
|
||||
_exit(2);
|
||||
}
|
||||
free(out_path);
|
||||
(void) path;
|
||||
}
|
||||
string_array_free(&rmdirs, &rmdirs_count, &rmdirs_length);
|
||||
if ( in_exists )
|
||||
{
|
||||
if ( unlink(outnewmanifest) < 0 && errno != ENOENT )
|
||||
{
|
||||
warn("unlink: %s", outnewmanifest);
|
||||
_exit(2);
|
||||
}
|
||||
mode_t temp_umask = umask(0022);
|
||||
FILE* fp = fopen(outnewmanifest, "w");
|
||||
if ( !fp )
|
||||
{
|
||||
warn("%s", outnewmanifest);
|
||||
_exit(2);
|
||||
}
|
||||
umask(temp_umask);
|
||||
for ( size_t i = 0; i < in_files_count; i++ )
|
||||
{
|
||||
const char* path = in_files[i];
|
||||
if ( fputs(path, fp) == EOF || fputc('\n', fp) == EOF )
|
||||
{
|
||||
warn("%s", outnewmanifest);
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
if ( fclose(fp) == EOF )
|
||||
{
|
||||
warn("%s", outnewmanifest);
|
||||
_exit(2);
|
||||
}
|
||||
if ( rename(outnewmanifest, outmanifest) < 0 )
|
||||
{
|
||||
warn("rename: %s -> %s", outnewmanifest, outmanifest);
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
else if ( out_exists )
|
||||
{
|
||||
if ( unlink(outmanifest) < 0 && errno != ENOENT )
|
||||
{
|
||||
warn("unlink: %s", outmanifest);
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
// Write out the new manifests atomically afterwards to ensure no paths are
|
||||
// leaked if the operation is aborted part way.
|
||||
char* in_tixinfo;
|
||||
char* out_tixinfo;
|
||||
if ( asprintf(&in_tixinfo, "%s/tix/tixinfo/%s", from_prefix,
|
||||
manifest) < 0 ||
|
||||
asprintf(&out_tixinfo, "%s/tix/tixinfo/%s", to_prefix,
|
||||
manifest) < 0 )
|
||||
{
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
// Update or delete the tixinfo accordingly.
|
||||
bool is_tix = !access_or_die(in_tixinfo, F_OK);
|
||||
if ( is_tix )
|
||||
{
|
||||
int in_fd = open(in_tixinfo, O_RDONLY);
|
||||
if ( in_fd < 0 )
|
||||
{
|
||||
warn("%s", in_tixinfo);
|
||||
_exit(2);
|
||||
}
|
||||
unlink(out_tixinfo);
|
||||
int out_fd = open(out_tixinfo, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if ( out_fd < 0 )
|
||||
{
|
||||
warn("%s", out_tixinfo);
|
||||
_exit(2);
|
||||
}
|
||||
while ( true )
|
||||
{
|
||||
ssize_t amount = read(in_fd, buffer, buffer_size);
|
||||
if ( amount < 0 )
|
||||
{
|
||||
warn("read: %s", in_tixinfo);
|
||||
_exit(2);
|
||||
}
|
||||
if ( amount == 0 )
|
||||
break;
|
||||
if ( writeall(out_fd, buffer, (size_t) amount) < (size_t) amount )
|
||||
{
|
||||
warn("write: %s", out_tixinfo);
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
close(out_fd);
|
||||
close(in_fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( unlink(out_tixinfo) < 0 && errno != ENOENT )
|
||||
{
|
||||
warn("unlink: %s", out_tixinfo);
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
free(in_tixinfo);
|
||||
free(out_tixinfo);
|
||||
// Likewise write out the new installation list atomically afterwards to
|
||||
// ensure no manifests are leaked if the operation is aborted part way.
|
||||
char* installed_path;
|
||||
char* installed_path_new;
|
||||
if ( asprintf(&installed_path, "%s/tix/installed.list", to_prefix) < 0 ||
|
||||
asprintf(&installed_path_new, "%s/tix/installed.list.new",
|
||||
to_prefix) < 0 )
|
||||
{
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
size_t installed_count;
|
||||
char** installed = read_lines_file(installed_path, &installed_count);
|
||||
if ( !installed )
|
||||
{
|
||||
warn("%s", installed_path);
|
||||
_exit(2);
|
||||
}
|
||||
size_t installed_length = installed_count;
|
||||
if ( is_tix )
|
||||
{
|
||||
bool found = false;
|
||||
for ( size_t i = 0; !found && i < installed_count; i++ )
|
||||
found = !strcmp(installed[i], manifest);
|
||||
if ( !found && !string_array_append(&installed, &installed_count,
|
||||
&installed_length, manifest) )
|
||||
{
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t o = 0;
|
||||
for ( size_t i = 0; i < installed_count; i++ )
|
||||
{
|
||||
if ( !strcmp(installed[i], manifest) )
|
||||
free(installed[i]);
|
||||
else
|
||||
installed[o++] = installed[i];
|
||||
}
|
||||
installed_count = o;
|
||||
}
|
||||
string_array_sort_strcmp(installed, installed_count);
|
||||
mode_t temp_umask = umask(0022);
|
||||
FILE* installed_fp = fopen(installed_path_new, "w");
|
||||
if ( !installed_fp )
|
||||
{
|
||||
warn("%s", installed_path_new);
|
||||
_exit(2);
|
||||
}
|
||||
umask(temp_umask);
|
||||
for ( size_t i = 0; i < installed_count; i++ )
|
||||
{
|
||||
const char* name = installed[i];
|
||||
if ( fputs(name, installed_fp) == EOF ||
|
||||
fputc('\n', installed_fp) == EOF )
|
||||
{
|
||||
warn("%s", installed_path_new);
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
if ( fclose(installed_fp) == EOF )
|
||||
{
|
||||
warn("%s", installed_path_new);
|
||||
_exit(2);
|
||||
}
|
||||
if ( rename(installed_path_new, installed_path) < 0 )
|
||||
{
|
||||
warn("rename: %s -> %s", installed_path_new, installed_path);
|
||||
_exit(2);
|
||||
}
|
||||
string_array_free(&installed, &installed_count, &installed_length);
|
||||
free(installed_path);
|
||||
free(installed_path_new);
|
||||
if ( in_files != empty )
|
||||
{
|
||||
for ( size_t i = 0; i < in_files_count; i++ )
|
||||
free(in_files[i]);
|
||||
free(in_files);
|
||||
}
|
||||
if ( out_files != empty )
|
||||
{
|
||||
for ( size_t i = 0; i < out_files_count; i++ )
|
||||
free(out_files[i]);
|
||||
free(out_files);
|
||||
}
|
||||
free(inmanifest);
|
||||
free(outmanifest);
|
||||
free(outnewmanifest);
|
||||
umask(old_umask);
|
||||
free(buffer);
|
||||
for ( size_t i = 0; i < hardlinks_used; i++ )
|
||||
|
@ -262,148 +634,152 @@ void install_manifest(const char* manifest,
|
|||
free(hardlinks);
|
||||
}
|
||||
|
||||
bool check_installed(const char* path, const char* package)
|
||||
void install_manifests(const char* const* manifests,
|
||||
size_t manifests_count,
|
||||
const char* from_prefix,
|
||||
const char* to_prefix)
|
||||
{
|
||||
FILE* fp = fopen(path, "r");
|
||||
if ( !fp )
|
||||
// Load all the paths mentioned in the new set of manifests, which are used
|
||||
// to ensure no files and directories are deleted part way if they are moved
|
||||
// from one manifest to another.
|
||||
printf(" - Loading manifests...\n");
|
||||
size_t all_count;
|
||||
size_t all_length;
|
||||
char** all;
|
||||
if ( !string_array_init(&all, &all_count, &all_length) )
|
||||
{
|
||||
if ( errno != ENOENT )
|
||||
warn("%s", path);
|
||||
return false;
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
char* line = NULL;
|
||||
size_t line_size = 0;
|
||||
ssize_t line_length;
|
||||
while ( 0 < (line_length = getline(&line, &line_size, fp)) )
|
||||
for ( size_t i = 0; i < manifests_count; i++ )
|
||||
{
|
||||
if ( line[line_length-1] == '\n' )
|
||||
line[--line_length] = '\0';
|
||||
if ( !strcmp(line, package) )
|
||||
// Read the input manifests if they exist. Consider a manifest that
|
||||
// doesn't exist as being empty.
|
||||
const char* manifest = manifests[i];
|
||||
char* inmanifest;
|
||||
if ( asprintf(&inmanifest, "%s/tix/manifest/%s", from_prefix,
|
||||
manifest) < 0 )
|
||||
{
|
||||
free(line);
|
||||
fclose(fp);
|
||||
return true;
|
||||
warn("asprintf");
|
||||
_exit(2);
|
||||
}
|
||||
char** empty = (char*[]){};
|
||||
char** in_files = empty;
|
||||
size_t in_files_count = 0;
|
||||
if ( !access_or_die(inmanifest, F_OK) &&
|
||||
!(in_files = read_manifest(inmanifest, &in_files_count)) )
|
||||
{
|
||||
warn("%s", inmanifest);
|
||||
_exit(2);
|
||||
}
|
||||
// Directories can appear in multiple manifests, so keep track of all
|
||||
// input paths so we later can find duplicates.
|
||||
for ( size_t i = 0; i < in_files_count; i++ )
|
||||
{
|
||||
if ( !string_array_append(&all, &all_count, &all_length,
|
||||
in_files[i]) )
|
||||
{
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
if ( ferror(fp) )
|
||||
warn("%s", path);
|
||||
free(line);
|
||||
fclose(fp);
|
||||
return false;
|
||||
if ( in_files != empty )
|
||||
{
|
||||
for ( size_t i = 0; i < in_files_count; i++ )
|
||||
free(in_files[i]);
|
||||
free(in_files);
|
||||
}
|
||||
free(inmanifest);
|
||||
}
|
||||
string_array_sort_strcmp(all, all_count);
|
||||
all_count = string_array_deduplicate(all, all_count);
|
||||
for ( size_t i = 0; i < manifests_count; i++ )
|
||||
install_manifest(manifests[i], from_prefix, to_prefix,
|
||||
(const char* const*) all, all_count);
|
||||
string_array_free(&all, &all_count, &all_length);
|
||||
}
|
||||
|
||||
static char* shell_single_quote(const char* string)
|
||||
char** read_installed_list(const char* prefix, size_t* out_count)
|
||||
{
|
||||
char* result;
|
||||
size_t result_size;
|
||||
FILE* fp = open_memstream(&result, &result_size);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
fputc('\'', fp);
|
||||
for ( size_t i = 0; string[i]; i++ )
|
||||
char* path;
|
||||
if ( asprintf(&path, "%s/tix/installed.list", prefix) < 0 )
|
||||
{
|
||||
if ( string[i] == '\'' )
|
||||
fputs("\'\\\'\'", fp);
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
char** installed;
|
||||
size_t installed_count;
|
||||
if ( !access_or_die(path, F_OK) )
|
||||
{
|
||||
if ( !(installed = read_lines_file(path, &installed_count)) )
|
||||
{
|
||||
warn("%s", path);
|
||||
_exit(2);
|
||||
}
|
||||
string_array_sort_strcmp(installed, installed_count);
|
||||
}
|
||||
else
|
||||
fputc((unsigned char) string[i], fp);
|
||||
{
|
||||
installed = malloc(1);
|
||||
if ( !installed )
|
||||
{
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
fputc('\'', fp);
|
||||
fflush(fp);
|
||||
int waserr = ferror(fp);
|
||||
fclose(fp);
|
||||
if (waserr) {
|
||||
free(result);
|
||||
return NULL;
|
||||
installed_count = 0;
|
||||
}
|
||||
return result;
|
||||
free(path);
|
||||
*out_count = installed_count;
|
||||
return installed;
|
||||
}
|
||||
|
||||
static char* sort_file_cmd(const char* file)
|
||||
void install_manifests_detect(const char* from_prefix,
|
||||
const char* to_prefix,
|
||||
bool system,
|
||||
bool detect_from,
|
||||
bool detect_to)
|
||||
{
|
||||
char* file_esc = shell_single_quote(file);
|
||||
if ( !file_esc )
|
||||
return NULL;
|
||||
char* cmd;
|
||||
if ( asprintf(&cmd, "sort -- %s", file_esc) < 0 )
|
||||
char** manifests;
|
||||
size_t manifests_count;
|
||||
size_t manifests_length;
|
||||
string_array_init(&manifests, &manifests_count, &manifests_length);
|
||||
if ( system &&
|
||||
!string_array_append(&manifests, &manifests_count, &manifests_length,
|
||||
"system") )
|
||||
{
|
||||
free(file_esc);
|
||||
return NULL;
|
||||
}
|
||||
free(file_esc);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void install_ports(const char* from_prefix, const char* to_prefix)
|
||||
{
|
||||
char* inst_in_path;
|
||||
char* inst_out_path;
|
||||
if ( asprintf(&inst_in_path, "%s/tix/installed.list", from_prefix) < 0 ||
|
||||
asprintf(&inst_out_path, "%s/tix/installed.list", to_prefix) < 0 )
|
||||
{
|
||||
warn("asprintf");
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
if ( access_or_die(inst_in_path, F_OK) < 0 )
|
||||
size_t system_offset = system ? 1 : 0;
|
||||
const char* prefixes[] =
|
||||
{
|
||||
free(inst_in_path);
|
||||
free(inst_out_path);
|
||||
return;
|
||||
}
|
||||
char* cmd = sort_file_cmd(inst_in_path);
|
||||
if ( !cmd )
|
||||
detect_from ? from_prefix : NULL,
|
||||
detect_to ? to_prefix : NULL,
|
||||
};
|
||||
for ( size_t i = 0; i < sizeof(prefixes) / sizeof(prefixes[0]); i++ )
|
||||
{
|
||||
warn("sort_file_cmd");
|
||||
const char* prefix = prefixes[i];
|
||||
if ( !prefix )
|
||||
continue;
|
||||
size_t installed_count;
|
||||
char** installed = read_installed_list(prefix, &installed_count);
|
||||
for ( size_t i = 0; i < installed_count; i++ )
|
||||
{
|
||||
if ( !string_array_append(&manifests, &manifests_count,
|
||||
&manifests_length, installed[i]) )
|
||||
{
|
||||
warn("malloc");
|
||||
_exit(2);
|
||||
}
|
||||
FILE* fp = popen(cmd, "r");
|
||||
if ( !fp )
|
||||
{
|
||||
warn("%s", cmd);
|
||||
_exit(2);
|
||||
free(installed[i]);
|
||||
}
|
||||
char* line = NULL;
|
||||
size_t line_size = 0;
|
||||
ssize_t line_length;
|
||||
while ( 0 < (line_length = getline(&line, &line_size, fp)) )
|
||||
{
|
||||
if ( line[line_length-1] == '\n' )
|
||||
line[--line_length] = '\0';
|
||||
if ( !check_installed(inst_out_path, line) )
|
||||
{
|
||||
FILE* inst_out_fp = fopen(inst_out_path, "a");
|
||||
if ( !inst_out_fp ||
|
||||
fprintf(inst_out_fp, "%s\n", line) < 0 ||
|
||||
fflush(inst_out_fp) == EOF )
|
||||
{
|
||||
warn("%s", inst_out_path);
|
||||
pclose(fp);
|
||||
_exit(2);
|
||||
free(installed);
|
||||
}
|
||||
fclose(inst_out_fp);
|
||||
}
|
||||
char* tixinfo_in;
|
||||
char* tixinfo_out;
|
||||
if ( asprintf(&tixinfo_in, "%s/tix/tixinfo/%s", from_prefix, line) < 0 ||
|
||||
asprintf(&tixinfo_out, "%s/tix/tixinfo/%s", to_prefix, line) < 0 )
|
||||
{
|
||||
warn("asprintf");
|
||||
pclose(fp);
|
||||
_exit(2);
|
||||
}
|
||||
execute((const char*[]) { "cp", "--", tixinfo_in, tixinfo_out, NULL }, "_e");
|
||||
free(tixinfo_in);
|
||||
free(tixinfo_out);
|
||||
install_manifest(line, from_prefix, to_prefix);
|
||||
}
|
||||
free(line);
|
||||
if ( ferror(fp) )
|
||||
{
|
||||
warn("%s", cmd);
|
||||
pclose(fp);
|
||||
_exit(2);
|
||||
}
|
||||
pclose(fp);
|
||||
free(cmd);
|
||||
free(inst_in_path);
|
||||
free(inst_out_path);
|
||||
// Keep the system manifest first and otherwise sort and deduplicate.
|
||||
string_array_sort_strcmp(manifests + system_offset,
|
||||
manifests_count - system_offset);
|
||||
manifests_count = string_array_deduplicate(manifests, manifests_count);
|
||||
install_manifests((const char* const*) manifests, manifests_count,
|
||||
from_prefix, to_prefix);
|
||||
string_array_free(&manifests, &manifests_count, &manifests_length);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2015, 2016, 2020 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
|
||||
|
@ -21,10 +21,21 @@
|
|||
#define MANIFEST_H
|
||||
|
||||
bool has_manifest(const char* manifest);
|
||||
char** read_manifest(const char* path, size_t* out_count);
|
||||
void install_manifest(const char* manifest,
|
||||
const char* from_prefix,
|
||||
const char* to_prefix,
|
||||
const char* const* preserved,
|
||||
size_t preserved_count);
|
||||
void install_manifests(const char* const* manifests,
|
||||
size_t manifests_count,
|
||||
const char* from_prefix,
|
||||
const char* to_prefix);
|
||||
bool check_installed(const char* path, const char* package);
|
||||
void install_ports(const char* from_prefix, const char* to_prefix);
|
||||
char** read_installed_list(const char* prefix, size_t* out_count);
|
||||
void install_manifests_detect(const char* from_prefix,
|
||||
const char* to_prefix,
|
||||
bool system,
|
||||
bool detect_from,
|
||||
bool detect_to);
|
||||
|
||||
#endif
|
||||
|
|
118
sysinstall/string_array.c
Normal file
118
sysinstall/string_array.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* string_array.c
|
||||
* String array utility functions.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "string_array.h"
|
||||
|
||||
bool string_array_init(char*** array, size_t* count, size_t* length)
|
||||
{
|
||||
*count = 0;
|
||||
*length = 0;
|
||||
size_t initial_length = 4;
|
||||
if ( !(*array = calloc(initial_length, sizeof(char*))) )
|
||||
return false;
|
||||
*length = initial_length;
|
||||
return true;
|
||||
}
|
||||
|
||||
void string_array_free(char*** array, size_t* count, size_t* length)
|
||||
{
|
||||
for ( size_t i = 0; i < *count; i++ )
|
||||
free((*array)[i]);
|
||||
free((*array));
|
||||
*array = NULL;
|
||||
*count = 0;
|
||||
*length = 0;
|
||||
}
|
||||
|
||||
bool string_array_append_nodup(char*** array,
|
||||
size_t* count,
|
||||
size_t* length,
|
||||
char* str)
|
||||
{
|
||||
if ( *count == *length )
|
||||
{
|
||||
char** new_array =
|
||||
reallocarray(*array, *length, 2 * sizeof(char*));
|
||||
if ( !new_array )
|
||||
return false;
|
||||
*array = new_array;
|
||||
*length *= 2;
|
||||
}
|
||||
(*array)[(*count)++] = str;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string_array_append(char*** array,
|
||||
size_t* count,
|
||||
size_t* length,
|
||||
const char* str)
|
||||
{
|
||||
char* dup = strdup(str);
|
||||
if ( !dup )
|
||||
return false;
|
||||
if ( !string_array_append_nodup(array, count, length, dup) )
|
||||
{
|
||||
free(dup);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int strcmp_indirect(const void* a_ptr, const void* b_ptr)
|
||||
{
|
||||
const char* a = *(const char* const*) a_ptr;
|
||||
const char* b = *(const char* const*) b_ptr;
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
void string_array_sort_strcmp(char** array, size_t count)
|
||||
{
|
||||
qsort(array, count, sizeof(char*), strcmp_indirect);
|
||||
}
|
||||
|
||||
static int search_strcmp(const void* str_ptr, const void* elem_ptr)
|
||||
{
|
||||
const char* str = (const char*) str_ptr;
|
||||
char* elem = *(char**) elem_ptr;
|
||||
return strcmp(str, elem);
|
||||
}
|
||||
|
||||
bool string_array_contains_bsearch_strcmp(const char* const* array,
|
||||
size_t count,
|
||||
const char* str)
|
||||
{
|
||||
return bsearch(str, array, count, sizeof(char*), search_strcmp);
|
||||
}
|
||||
|
||||
size_t string_array_deduplicate(char** strings, size_t count)
|
||||
{
|
||||
size_t o = 0;
|
||||
for ( size_t i = 0; i < count; i++ )
|
||||
{
|
||||
if ( !o || strcmp(strings[o - 1], strings[i]) != 0 )
|
||||
strings[o++] = strings[i];
|
||||
else
|
||||
free(strings[i]);
|
||||
}
|
||||
return o;
|
||||
}
|
39
sysinstall/string_array.h
Normal file
39
sysinstall/string_array.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* string_array.h
|
||||
* String array utility functions.
|
||||
*/
|
||||
|
||||
#ifndef STRING_ARRAY_H
|
||||
#define STRING_ARRAY_H
|
||||
|
||||
bool string_array_init(char*** array, size_t* count, size_t* length);
|
||||
void string_array_free(char*** array, size_t* count, size_t* length);
|
||||
bool string_array_append_nodup(char*** array,
|
||||
size_t* count,
|
||||
size_t* length,
|
||||
char* str);
|
||||
bool string_array_append(char*** array,
|
||||
size_t* count,
|
||||
size_t* length,
|
||||
const char* str);
|
||||
void string_array_sort_strcmp(char** array, size_t count);
|
||||
bool string_array_contains_bsearch_strcmp(const char* const* array,
|
||||
size_t count,
|
||||
const char* str);
|
||||
size_t string_array_deduplicate(char** strings, size_t count);
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2016, 2020 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2015, 2016, 2020, 2021 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
|
||||
|
@ -884,40 +884,19 @@ int main(void)
|
|||
if ( install_pid == 0 )
|
||||
{
|
||||
printf(" - Populating root filesystem...\n");
|
||||
umask(0000);
|
||||
chmod(".", 0755);
|
||||
mkdir_or_chmod_or_die("bin", 0755);
|
||||
mkdir_or_chmod_or_die("boot", 0755);
|
||||
mkdir_or_chmod_or_die("dev", 0755);
|
||||
mkdir_or_chmod_or_die("etc", 0755);
|
||||
mkdir_or_chmod_or_die("etc/skel", 0755);
|
||||
mkdir_or_chmod_or_die("etc/init", 0755);
|
||||
mkdir_or_chmod_or_die("home", 0755);
|
||||
mkdir_or_chmod_or_die("include", 0755);
|
||||
mkdir_or_chmod_or_die("lib", 0755);
|
||||
mkdir_or_chmod_or_die("mnt", 0755);
|
||||
mkdir_or_chmod_or_die("root", 0700);
|
||||
mkdir_or_chmod_or_die("sbin", 0755);
|
||||
mkdir_or_chmod_or_die("share", 0755);
|
||||
mkdir_or_chmod_or_die("tix", 0755);
|
||||
mkdir_or_chmod_or_die("tix/manifest", 0755);
|
||||
mkdir_or_chmod_or_die("tmp", 01777);
|
||||
mkdir_or_chmod_or_die("var", 0755);
|
||||
mkdir_or_chmod_or_die("var/empty", 0555);
|
||||
umask(0022);
|
||||
if ( access("tix/collection.conf", F_OK) < 0 )
|
||||
execute((const char*[]) { "tix-collection", ".", "create",
|
||||
"--prefix=", NULL }, "_e");
|
||||
install_manifest("system", "", ".");
|
||||
install_manifests_detect("", ".", true, true, true);
|
||||
// TODO: Preserve the existing /src if it exists like in sysupgrade.
|
||||
if ( has_manifest("src") )
|
||||
install_manifest("src", "", ".");
|
||||
install_manifest("src", "", ".", (const char*[]){}, 0);
|
||||
printf(" - Creating configuration files...\n");
|
||||
// TODO: Preserve mode/ownership/timestamps?
|
||||
execute((const char*[]) { "cp", "-RTP", etc, "etc", NULL }, "_e");
|
||||
// TODO: Auto detect appropriate bcrypt rounds and set up etc/login.conf
|
||||
// and use those below instead of blowfish,a.
|
||||
install_ports("", ".");
|
||||
if ( access_or_die("boot/random.seed", F_OK) < 0 )
|
||||
{
|
||||
printf(" - Creating random seed...\n");
|
||||
|
@ -991,6 +970,16 @@ int main(void)
|
|||
}
|
||||
text("\n");
|
||||
|
||||
if ( mkdir("root", 0700) < 0 )
|
||||
{
|
||||
if ( errno == EEXIST )
|
||||
{
|
||||
if ( chmod("root", 0700) < 0 )
|
||||
warn("chmod: root");
|
||||
}
|
||||
else
|
||||
warn("mkdir: root");
|
||||
}
|
||||
if ( passwd_has_uid("etc/passwd", 0) ||
|
||||
passwd_has_name("etc/passwd", "root") )
|
||||
{
|
||||
|
@ -1036,6 +1025,16 @@ int main(void)
|
|||
}
|
||||
text("\n");
|
||||
|
||||
if ( mkdir("etc/init", 0755) < 0 )
|
||||
{
|
||||
if ( errno == EEXIST )
|
||||
{
|
||||
if ( chmod("etc/init", 0755) < 0 )
|
||||
warn("chmod: etc/init");
|
||||
}
|
||||
else
|
||||
warn("mkdir: etc/init");
|
||||
}
|
||||
install_configurationf("etc/init/target", "w", "multi-user\n");
|
||||
|
||||
text("Congratulations, the system is now functional! This is a good time "
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
.Nd upgrade current operating system from a sysroot
|
||||
.Sh SYNOPSIS
|
||||
.Nm sysmerge
|
||||
.Op Fl cw
|
||||
.Op Fl cfw
|
||||
.Op Fl \-booting
|
||||
.Op Fl \-hook-finalize
|
||||
.Op Fl \-hook-prepare
|
||||
|
@ -27,6 +27,7 @@ installs the
|
|||
manifest from the tix repository in the
|
||||
.Ar source
|
||||
directory, as well as all the ports found.
|
||||
If a full upgrade is done, then all ports not found will be uninstalled.
|
||||
Upgrade hooks will be run if further actions are needed to migrate the system to
|
||||
the new version as described in
|
||||
.Xr following-development 7 .
|
||||
|
@ -68,6 +69,10 @@ directory and restore the old
|
|||
.Xr kernel 7
|
||||
and
|
||||
.Xr initrd 7 .
|
||||
.It Fl f , Fl \-full
|
||||
Full system upgrade that uninstalls ports not present in the
|
||||
.Ar source
|
||||
directory.
|
||||
.It Fl \-hook-finalize
|
||||
Run the post-upgrade hooks.
|
||||
This is meant to be used by the old
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2018, 2020 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2016, 2018, 2020, 2021 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
|
||||
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
@ -73,6 +74,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
bool booting = false;
|
||||
bool cancel = false;
|
||||
bool full = false;
|
||||
bool hook_finalize = false;
|
||||
bool hook_prepare = false;
|
||||
bool wait = false;
|
||||
|
@ -92,6 +94,7 @@ int main(int argc, char* argv[])
|
|||
while ( (c = *++arg) ) switch ( c )
|
||||
{
|
||||
case 'c': cancel = true; break;
|
||||
case 'f': full = true; break;
|
||||
case 'w': wait = true; break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||
|
@ -107,6 +110,8 @@ int main(int argc, char* argv[])
|
|||
booting = true;
|
||||
else if ( !strcmp(arg, "--cancel") )
|
||||
cancel = true;
|
||||
else if ( !strcmp(arg, "--full") )
|
||||
full = true;
|
||||
else if ( !strcmp(arg, "--hook-finalize") )
|
||||
hook_finalize = true;
|
||||
else if ( !strcmp(arg, "--hook-prepare") )
|
||||
|
@ -142,6 +147,7 @@ int main(int argc, char* argv[])
|
|||
source = "/sysmerge";
|
||||
if ( 1 < argc )
|
||||
errx(2, "Unexpected extra operand `%s'", argv[1]);
|
||||
full = access_or_die("/sysmerge/tix/sysmerge.full", F_OK) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -307,13 +313,19 @@ int main(int argc, char* argv[])
|
|||
execute((const char*[]) { "tix-collection", "/sysmerge", "create",
|
||||
NULL }, "e");
|
||||
}
|
||||
install_manifest("system", source, target);
|
||||
install_ports(source, target);
|
||||
install_manifests_detect(source, target, true, true, full);
|
||||
}
|
||||
|
||||
if ( wait )
|
||||
{
|
||||
printf(" - Scheduling upgrade on next boot...\n");
|
||||
if ( full )
|
||||
{
|
||||
int fd = open("/sysmerge/tix/sysmerge.full", O_WRONLY | O_CREAT);
|
||||
if ( fd < 0 )
|
||||
err(1, "/sysmerge/tix/sysmerge.full");
|
||||
close(fd);
|
||||
}
|
||||
execute((const char*[]) { "cp", "/boot/sortix.bin",
|
||||
"/boot/sortix.bin.sysmerge.orig", NULL }, "e");
|
||||
execute((const char*[]) { "cp", "/boot/sortix.initrd",
|
||||
|
|
|
@ -803,13 +803,9 @@ int main(void)
|
|||
if ( upgrade_pid == 0 )
|
||||
{
|
||||
umask(0022);
|
||||
// TODO: Use an upgrade manifest system that notices files that are now
|
||||
// untracked or moved from one manifest to another.
|
||||
if ( conf.system )
|
||||
{
|
||||
upgrade_prepare(target_release, &new_release, "", ".");
|
||||
install_manifest("system", "", ".");
|
||||
}
|
||||
install_manifests_detect("", ".", conf.system, conf.ports, conf.ports);
|
||||
if ( has_manifest("src") )
|
||||
{
|
||||
if ( conf.newsrc )
|
||||
|
@ -824,7 +820,7 @@ int main(void)
|
|||
_exit(1);
|
||||
}
|
||||
}
|
||||
install_manifest("src", "", ".");
|
||||
install_manifest("src", "", ".", (const char*[]){}, 0);
|
||||
if ( has_src )
|
||||
{
|
||||
if ( rename("src", "newsrc") < 0 )
|
||||
|
@ -842,11 +838,9 @@ int main(void)
|
|||
else if ( conf.src )
|
||||
{
|
||||
preserve_src("src");
|
||||
install_manifest("src", "", ".");
|
||||
install_manifest("src", "", ".", (const char*[]){}, 0);
|
||||
}
|
||||
}
|
||||
if ( conf.ports )
|
||||
install_ports("", ".");
|
||||
if ( conf.system )
|
||||
upgrade_finalize(target_release, &new_release, "", ".");
|
||||
if ( conf.system )
|
||||
|
|
Loading…
Reference in a new issue