412 lines
8.7 KiB
C
412 lines
8.7 KiB
C
/*******************************************************************************
|
|
|
|
Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
|
|
|
|
This program is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by the Free
|
|
Software Foundation, either version 3 of the License, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
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 <= (errno = 0, line_length = getline(&line, &line_size, fpin)) )
|
|
{
|
|
if ( line_length && 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 ( errno )
|
|
{
|
|
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 < (errno = 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 ( errno != 0 )
|
|
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 < (errno = 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 ( errno )
|
|
{
|
|
warn("%s", cmd);
|
|
pclose(fp);
|
|
_exit(2);
|
|
}
|
|
pclose(fp);
|
|
free(cmd);
|
|
free(inst_in_path);
|
|
free(inst_out_path);
|
|
}
|