mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
408 lines
8.6 KiB
C
408 lines
8.6 KiB
C
/*
|
|
* 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
|
|
* 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.
|
|
*
|
|
* manifest.c
|
|
* Manifest handling functions.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <ioleast.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "execute.h"
|
|
#include "fileops.h"
|
|
#include "manifest.h"
|
|
|
|
bool has_manifest(const char* manifest)
|
|
{
|
|
char* path;
|
|
if ( asprintf(&path, "/tix/manifest/%s", manifest) < 0 )
|
|
{
|
|
warn("asprintf");
|
|
_exit(2);
|
|
}
|
|
bool result = access(path, F_OK) == 0;
|
|
free(path);
|
|
return result;
|
|
}
|
|
|
|
struct hardlink
|
|
{
|
|
dev_t dev;
|
|
ino_t ino;
|
|
char* path;
|
|
};
|
|
|
|
void install_manifest(const char* manifest,
|
|
const char* from_prefix,
|
|
const char* to_prefix)
|
|
{
|
|
printf(" - Installing %s...\n", manifest);
|
|
struct hardlink* hardlinks = NULL;
|
|
size_t hardlinks_used = 0;
|
|
size_t hardlinks_length = 0;
|
|
size_t buffer_size = 1 << 16;
|
|
char* buffer = malloc(buffer_size);
|
|
if ( !buffer )
|
|
{
|
|
warn("malloc");
|
|
_exit(2);
|
|
}
|
|
mode_t old_umask = umask(0000);
|
|
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 )
|
|
{
|
|
warn("asprintf");
|
|
_exit(2);
|
|
}
|
|
FILE* fpin = fopen(inmanifest, "r");
|
|
if ( !fpin )
|
|
{
|
|
warn("%s", inmanifest);
|
|
_exit(2);
|
|
}
|
|
FILE* fpout = fopen(outmanifest, "w");
|
|
if ( !fpout )
|
|
{
|
|
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)) )
|
|
{
|
|
if ( line[line_length-1] == '\n' )
|
|
line[--line_length] = '\0';
|
|
if ( fprintf(fpout, "%s\n", line) < 0 )
|
|
{
|
|
warn("write: %s", outmanifest);
|
|
_exit(2);
|
|
}
|
|
if ( line[0] != '/' )
|
|
continue;
|
|
char* in_path;
|
|
if ( asprintf(&in_path, "%s%s", from_prefix, line) < 0 )
|
|
{
|
|
warn("asprintf");
|
|
_exit(2);
|
|
}
|
|
char* out_path = line;
|
|
if ( asprintf(&out_path, "%s%s", to_prefix, line) < 0 )
|
|
{
|
|
warn("asprintf");
|
|
_exit(2);
|
|
}
|
|
struct stat inst;
|
|
if ( lstat(in_path, &inst) < 0 )
|
|
{
|
|
warn("%s", in_path);
|
|
_exit(2);
|
|
}
|
|
struct hardlink* hardlink = NULL;
|
|
if ( S_ISREG(inst.st_mode) && 2 <= inst.st_nlink )
|
|
{
|
|
for ( size_t i = 0; i < hardlinks_used; i++ )
|
|
{
|
|
if ( hardlinks[i].dev != inst.st_dev ||
|
|
hardlinks[i].ino != inst.st_ino )
|
|
continue;
|
|
hardlink = &hardlinks[i];
|
|
break;
|
|
}
|
|
}
|
|
if ( hardlink )
|
|
{
|
|
unlink(out_path);
|
|
if ( link(hardlink->path, out_path) < 0 )
|
|
{
|
|
warn("link: %s -> %s", hardlink->path, out_path);
|
|
_exit(2);
|
|
}
|
|
}
|
|
else if ( S_ISDIR(inst.st_mode) )
|
|
{
|
|
if ( mkdir(out_path, inst.st_mode & 07777) < 0 && errno != EEXIST )
|
|
{
|
|
warn("mkdir: %s", out_path);
|
|
_exit(2);
|
|
}
|
|
}
|
|
else if ( S_ISREG(inst.st_mode) )
|
|
{
|
|
|
|
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,
|
|
inst.st_mode & 07777);
|
|
if ( out_fd < 0 )
|
|
{
|
|
warn("%s", out_path);
|
|
_exit(2);
|
|
}
|
|
while ( true )
|
|
{
|
|
ssize_t amount = read(in_fd, buffer, buffer_size);
|
|
if ( amount < 0 )
|
|
{
|
|
warn("read: %s", in_path);
|
|
_exit(2);
|
|
}
|
|
if ( amount == 0 )
|
|
break;
|
|
if ( writeall(out_fd, buffer, (size_t) amount) < (size_t) amount )
|
|
{
|
|
warn("write: %s", out_path);
|
|
_exit(2);
|
|
}
|
|
}
|
|
close(out_fd);
|
|
close(in_fd);
|
|
if ( 2 <= inst.st_nlink )
|
|
{
|
|
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));
|
|
if ( !new_hardlinks )
|
|
{
|
|
warn("malloc");
|
|
_exit(2);
|
|
}
|
|
hardlinks = new_hardlinks;
|
|
hardlinks_length = new_length;
|
|
}
|
|
hardlinks[hardlinks_used].ino = inst.st_ino;
|
|
hardlinks[hardlinks_used].dev = inst.st_dev;
|
|
if ( !(hardlinks[hardlinks_used].path = strdup(out_path)) )
|
|
{
|
|
warn("strdup");
|
|
_exit(2);
|
|
}
|
|
hardlinks_used++;
|
|
}
|
|
}
|
|
else if ( S_ISLNK(inst.st_mode) )
|
|
{
|
|
ssize_t amount = readlink(in_path, buffer, buffer_size - 1);
|
|
if ( amount < 0 )
|
|
{
|
|
warn("readlink: %s", in_path);
|
|
_exit(2);
|
|
}
|
|
buffer[amount] = '\0';
|
|
unlink(out_path);
|
|
if ( symlink(buffer, out_path) < 0 && errno != EEXIST )
|
|
{
|
|
warn("symlink: %s", out_path);
|
|
_exit(2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
warnx("%s: Don't know how to copy this object", in_path);
|
|
_exit(2);
|
|
}
|
|
free(in_path);
|
|
free(out_path);
|
|
}
|
|
free(line);
|
|
if ( ferror(fpin) )
|
|
{
|
|
warn("%s", inmanifest);
|
|
_exit(2);
|
|
}
|
|
fclose(fpin);
|
|
if ( fclose(fpout) == EOF )
|
|
{
|
|
warn("close: %s", outmanifest);
|
|
_exit(2);
|
|
}
|
|
free(inmanifest);
|
|
free(outmanifest);
|
|
umask(old_umask);
|
|
free(buffer);
|
|
for ( size_t i = 0; i < hardlinks_used; i++ )
|
|
free(hardlinks[i].path);
|
|
free(hardlinks);
|
|
}
|
|
|
|
bool check_installed(const char* path, const char* package)
|
|
{
|
|
FILE* fp = fopen(path, "r");
|
|
if ( !fp )
|
|
{
|
|
if ( errno != ENOENT )
|
|
warn("%s", path);
|
|
return false;
|
|
}
|
|
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 ( !strcmp(line, package) )
|
|
{
|
|
free(line);
|
|
fclose(fp);
|
|
return true;
|
|
}
|
|
}
|
|
if ( ferror(fp) )
|
|
warn("%s", path);
|
|
free(line);
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
|
|
static char* shell_single_quote(const char* string)
|
|
{
|
|
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++ )
|
|
{
|
|
if ( string[i] == '\'' )
|
|
fputs("\'\\\'\'", fp);
|
|
else
|
|
fputc((unsigned char) string[i], fp);
|
|
}
|
|
fputc('\'', fp);
|
|
fflush(fp);
|
|
int waserr = ferror(fp);
|
|
fclose(fp);
|
|
if (waserr) {
|
|
free(result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static char* sort_file_cmd(const char* file)
|
|
{
|
|
char* file_esc = shell_single_quote(file);
|
|
if ( !file_esc )
|
|
return NULL;
|
|
char* cmd;
|
|
if ( asprintf(&cmd, "sort -- %s", file_esc) < 0 )
|
|
{
|
|
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");
|
|
_exit(2);
|
|
}
|
|
if ( access_or_die(inst_in_path, F_OK) < 0 )
|
|
{
|
|
free(inst_in_path);
|
|
free(inst_out_path);
|
|
return;
|
|
}
|
|
char* cmd = sort_file_cmd(inst_in_path);
|
|
if ( !cmd )
|
|
{
|
|
warn("sort_file_cmd");
|
|
_exit(2);
|
|
}
|
|
FILE* fp = popen(cmd, "r");
|
|
if ( !fp )
|
|
{
|
|
warn("%s", cmd);
|
|
_exit(2);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|