3/18: Functional package installation backend
This commit is contained in:
1 parent
e79c96a987
commit
6819fe7c69
24 files changed
+1011
-145
No files matched your search
+829
-14
@@ -1,27 +1,35 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/limits.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "istoreutils.h"
|
||||
#include "log.h"
|
||||
#include "pkg.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
INDEX,
|
||||
STORE
|
||||
}
|
||||
index_store_t;
|
||||
|
||||
static const dir_t usr_index = { "/glacier/usr/index", 0700 };
|
||||
static const dir_t sys_index = { "/glacier/sys/index", 0700 };
|
||||
|
||||
static const dir_t usr_store = { "/glacier/usr/store", 0700 };
|
||||
static const dir_t sys_store = { "/glacier/sys/store", 0700 };
|
||||
|
||||
static const dir_t usr_links = { "/glacier/usr/links", 0700 };
|
||||
|
||||
bool
|
||||
gl_usr_istore_exists(index_store_t index_or_store, int uid)
|
||||
{
|
||||
@@ -37,7 +45,7 @@ gl_usr_istore_exists(index_store_t index_or_store, int uid)
|
||||
break;
|
||||
default:
|
||||
printf("libglacier: argument index_or_store of function gl_usr_istore_exists must be INDEX or STORE\n");
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stat(path, &st) != 0) {
|
||||
@@ -62,7 +70,7 @@ gl_sys_istore_exists(index_store_t index_or_store)
|
||||
break;
|
||||
default:
|
||||
printf("libglacier: argument index_or_store of function gl_sys_istore_exists must be INDEX or STORE\n");
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stat(path, &st) != 0) {
|
||||
@@ -72,19 +80,65 @@ gl_sys_istore_exists(index_store_t index_or_store)
|
||||
return S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
int
|
||||
gl_init_user(int uid)
|
||||
static void
|
||||
make_index_name(char *buffer, size_t buffersz)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
struct tm *tm = localtime(&now);
|
||||
strftime(buffer, buffersz, "index-%m_%d_%Y@%H:%M", tm);
|
||||
}
|
||||
|
||||
static int
|
||||
create_index(const char *ind_path, uid_t uid)
|
||||
{
|
||||
char fname[64];
|
||||
make_index_name(fname, sizeof(fname));
|
||||
|
||||
char full_path[PATH_MAX];
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", ind_path, fname);
|
||||
|
||||
FILE *f = fopen(full_path, "w");
|
||||
if (!f) { return -1; }
|
||||
fprintf(f, "uid = %d\n", uid);
|
||||
fprintf(f, "listed = 0\n\n");
|
||||
fclose(f);
|
||||
|
||||
if (chown(full_path, uid, (gid_t)-1) != 0) { return -1; }
|
||||
|
||||
char link_path[PATH_MAX];
|
||||
snprintf(link_path, sizeof(link_path), "%s/current", ind_path);
|
||||
unlink(link_path);
|
||||
|
||||
if (symlink(fname, link_path) != 0) { return -1; }
|
||||
if (lchown(link_path, uid, (gid_t)-1) != 0) { return -1; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gl_init_user(uid_t uid, bool isVerbose)
|
||||
{
|
||||
if (isVerbose) { lg_printf(0, "Checking if UID is within acceptable bounds..."); }
|
||||
if (uid < 1000) {
|
||||
if (isVerbose) {
|
||||
lg_printf(2, "Attempted to create index and store for UID < 1000. Quitting...");
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (isVerbose) { lg_printf(0, "Constructing paths for index and store..."); }
|
||||
|
||||
char ind_path[PATH_MAX];
|
||||
char sto_path[PATH_MAX];
|
||||
char lnk_path[PATH_MAX];
|
||||
|
||||
snprintf(ind_path, sizeof(ind_path), "%s/%d", usr_index.path, uid);
|
||||
snprintf(sto_path, sizeof(sto_path), "%s/%d", usr_store.path, uid);
|
||||
|
||||
snprintf(lnk_path, sizeof(lnk_path), "%s/%d", usr_links.path, uid);
|
||||
|
||||
if (isVerbose) { lg_printf(0, "Paths constructed:\n%s\n%s", ind_path, sto_path); }
|
||||
|
||||
if (isVerbose) { lg_printf(0, "Attempting to create index directory at %s", ind_path); }
|
||||
if (mkdir(ind_path, 0700) == -1) {
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
@@ -115,6 +169,7 @@ gl_init_user(int uid)
|
||||
}
|
||||
}
|
||||
|
||||
if (isVerbose) { lg_printf(0, "Attempting to create store directory at %s", sto_path); }
|
||||
if (mkdir(sto_path, 0700) == -1) {
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
@@ -145,6 +200,56 @@ gl_init_user(int uid)
|
||||
}
|
||||
}
|
||||
|
||||
if (isVerbose) { lg_printf(0, "Attempting to create links directory at %s", lnk_path); }
|
||||
if (mkdir(lnk_path, 0700) == -1) {
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
lg_printf(2, "Cannot create store: permission denied");
|
||||
return 1;
|
||||
break;
|
||||
case EEXIST:
|
||||
lg_printf(2, "Cannot create store: directory already exists");
|
||||
return 1;
|
||||
break;
|
||||
case ENOENT:
|
||||
/* should never happen, but here for completeness */
|
||||
lg_printf(2, "Cannot create store: parent directory does not exist");
|
||||
return 1;
|
||||
break;
|
||||
case ENOSPC:
|
||||
lg_printf(2, "Cannot create store: no space left on device");
|
||||
return 1;
|
||||
break;
|
||||
case EROFS:
|
||||
lg_printf(2, "Cannot create store: read only filesystem");
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
printf("libglacier: mkdir error in function gl_init_user\n");
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isVerbose) { lg_printf(0, "Changing ownership of index and store to UID %i", uid); }
|
||||
|
||||
if (chown(sto_path, uid, (gid_t)-1) != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (chown(ind_path, uid, (gid_t)-1) != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (chown(lnk_path, uid, (gid_t)-1) != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (create_index(ind_path, uid) != 0) {
|
||||
/* No switch for errno. chown() shouldn't fail if mkdir() didn't */
|
||||
lg_printf(2, "Cannot create initial index file");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -162,7 +267,7 @@ trim(char *s)
|
||||
|
||||
size_t len = strlen(s);
|
||||
while (len && isspace((unsigned char)s[len - 1])) {
|
||||
s[len--] = 0;
|
||||
s[--len] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,4 +286,714 @@ static char
|
||||
|
||||
int
|
||||
gl_parse_index(struct gindex *idx, FILE *f)
|
||||
{}
|
||||
{
|
||||
idx->uid = -1;
|
||||
idx->listed = -1;
|
||||
idx->entries = NULL;
|
||||
idx->count = 0;
|
||||
|
||||
char line[512];
|
||||
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
trim(line);
|
||||
if (!line[0] || line[0] == '#') { continue; }
|
||||
|
||||
char *eq = strchr(line, '=');
|
||||
if (eq) {
|
||||
*eq = 0;
|
||||
char *key = line;
|
||||
char *val = eq + 1;
|
||||
trim(key);
|
||||
trim(val);
|
||||
|
||||
if (strcmp(key, "uid") == 0) { idx->uid = atoi(val); }
|
||||
else if (strcmp(key, "listed") == 0) { idx->listed=atoi(val); }
|
||||
continue;
|
||||
}
|
||||
|
||||
char *first = strstr(line, "::");
|
||||
if (!first) { continue; }
|
||||
|
||||
*first = 0;
|
||||
char *repo = line;
|
||||
char *pkg = first + 2;
|
||||
trim(repo);
|
||||
trim(pkg);
|
||||
|
||||
struct gpkg_entry ent;
|
||||
ent.repo = dupstr(repo);
|
||||
ent.pkg = dupstr(pkg);
|
||||
if (!ent.repo || !ent.pkg) { return -1; }
|
||||
|
||||
struct gpkg_entry *tmp =
|
||||
realloc(idx->entries, sizeof(*idx->entries) * (idx->count + 1));
|
||||
if (!tmp) { return -1; }
|
||||
idx->entries = tmp;
|
||||
idx->entries[idx->count++] = ent;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
gl_free_index(struct gindex *idx)
|
||||
{
|
||||
for (size_t i = 0; i < idx->count; i++) {
|
||||
free(idx->entries[i].repo);
|
||||
free(idx->entries[i].pkg);
|
||||
}
|
||||
|
||||
free(idx->entries);
|
||||
idx->entries = NULL;
|
||||
idx->count = 0;
|
||||
}
|
||||
/*
|
||||
int
|
||||
gl_rebuild_index(uid_t uid, const char *out_dir)
|
||||
{
|
||||
char store_path[PATH_MAX];
|
||||
snprintf(store_path, sizeof(store_path), "%s/%d", usr_store.path, uid);
|
||||
|
||||
DIR *store_dir = opendir(store_path);
|
||||
if (!store_dir) return -1;
|
||||
|
||||
FILE *out = fopen(out_dir, "w");
|
||||
if (!out) return -1;
|
||||
|
||||
size_t listed = 0;
|
||||
|
||||
struct dirent *repo_ent;
|
||||
while ((repo_ent = readdir(store_dir))) {
|
||||
if (repo_ent->d_name[0] == '.') { continue; }
|
||||
|
||||
char repo_path[PATH_MAX];
|
||||
snprintf(repo_path, sizeof(repo_path), "%s/%s",
|
||||
store_path, repo_ent->d_name);
|
||||
|
||||
struct stat rst;
|
||||
if (stat(repo_path, &rst) != 0 || !S_ISDIR(rst.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DIR *repo_dir = opendir(repo_path);
|
||||
if (!repo_dir) continue;
|
||||
|
||||
struct dirent *pkg_ent;
|
||||
while ((pkg_ent = readdir(repo_dir))) {
|
||||
if (pkg_ent->d_name[0] == '.') { continue; }
|
||||
|
||||
char pkg_path[PATH_MAX];
|
||||
snprintf(pkg_path, sizeof(pkg_path), "%s/%s",
|
||||
repo_path, pkg_ent->d_name);
|
||||
|
||||
struct stat pst;
|
||||
if (stat(pkg_path, &pst) != 0 || !S_ISDIR(pst.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(out, "%s::%s\n",
|
||||
repo_ent->d_name,
|
||||
pkg_ent->d_name);
|
||||
listed++;
|
||||
}
|
||||
closedir(repo_dir);
|
||||
}
|
||||
|
||||
rewind(out);
|
||||
fprintf(out, "uid = %d\n", uid);
|
||||
fprintf(out, "listed = %zu\n\n", listed);
|
||||
|
||||
closedir(store_dir);
|
||||
fclose(out);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
int
|
||||
gl_rebuild_index(uid_t uid, const char *out_dir)
|
||||
{
|
||||
char store_path[PATH_MAX];
|
||||
snprintf(store_path, sizeof(store_path), "%s/%d", usr_store.path, uid);
|
||||
|
||||
DIR *store_dir = opendir(store_path);
|
||||
if (!store_dir) return -1;
|
||||
|
||||
/* Write to a new timestamped index file */
|
||||
char fname[64];
|
||||
make_index_name(fname, sizeof(fname));
|
||||
|
||||
char new_index_path[PATH_MAX];
|
||||
snprintf(new_index_path, sizeof(new_index_path), "%s/%s", out_dir, fname);
|
||||
|
||||
FILE *out = fopen(new_index_path, "w");
|
||||
if (!out) {
|
||||
closedir(store_dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t listed = 0;
|
||||
|
||||
struct dirent *repo_ent;
|
||||
while ((repo_ent = readdir(store_dir))) {
|
||||
if (repo_ent->d_name[0] == '.') { continue; }
|
||||
|
||||
char repo_path[PATH_MAX];
|
||||
snprintf(repo_path, sizeof(repo_path), "%s/%s",
|
||||
store_path, repo_ent->d_name);
|
||||
|
||||
struct stat rst;
|
||||
if (stat(repo_path, &rst) != 0 || !S_ISDIR(rst.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DIR *repo_dir = opendir(repo_path);
|
||||
if (!repo_dir) continue;
|
||||
|
||||
struct dirent *pkg_ent;
|
||||
while ((pkg_ent = readdir(repo_dir))) {
|
||||
if (pkg_ent->d_name[0] == '.') { continue; }
|
||||
|
||||
char pkg_path[PATH_MAX];
|
||||
snprintf(pkg_path, sizeof(pkg_path), "%s/%s",
|
||||
repo_path, pkg_ent->d_name);
|
||||
|
||||
struct stat pst;
|
||||
if (stat(pkg_path, &pst) != 0 || !S_ISDIR(pst.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(out, "%s::%s\n",
|
||||
repo_ent->d_name,
|
||||
pkg_ent->d_name);
|
||||
listed++;
|
||||
}
|
||||
closedir(repo_dir);
|
||||
}
|
||||
closedir(store_dir);
|
||||
fclose(out);
|
||||
|
||||
/* Prepend header by copying through a temp file then renaming over */
|
||||
FILE *tmp_in = fopen(new_index_path, "r");
|
||||
if (!tmp_in) return -1;
|
||||
|
||||
char tmp_path[PATH_MAX];
|
||||
snprintf(tmp_path, sizeof(tmp_path), "%s/.index_tmp_XXXXXX", out_dir);
|
||||
int tmp_fd = mkstemp(tmp_path);
|
||||
if (tmp_fd < 0) { fclose(tmp_in); return -1; }
|
||||
|
||||
FILE *tmp_out = fdopen(tmp_fd, "w");
|
||||
if (!tmp_out) { close(tmp_fd); fclose(tmp_in); return -1; }
|
||||
|
||||
fprintf(tmp_out, "uid = %d\n", uid);
|
||||
fprintf(tmp_out, "listed = %zu\n\n", listed);
|
||||
|
||||
char line[512];
|
||||
while (fgets(line, sizeof(line), tmp_in)) {
|
||||
fputs(line, tmp_out);
|
||||
}
|
||||
fclose(tmp_in);
|
||||
fclose(tmp_out);
|
||||
|
||||
rename(tmp_path, new_index_path);
|
||||
chown(new_index_path, uid, (gid_t)-1);
|
||||
|
||||
/* Update the "current" symlink to point at the new index */
|
||||
char link_path[PATH_MAX];
|
||||
snprintf(link_path, sizeof(link_path), "%s/current", out_dir);
|
||||
unlink(link_path);
|
||||
|
||||
if (symlink(fname, link_path) != 0) { return -1; }
|
||||
lchown(link_path, uid, (gid_t)-1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
add_to_archive(struct archive *a, const char *real_path, const char *archive_path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (lstat(real_path, &st) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
struct archive_entry *entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, archive_path);
|
||||
archive_entry_set_filetype(entry, AE_IFDIR);
|
||||
archive_entry_set_perm(entry, st.st_mode & 0777);
|
||||
|
||||
if (archive_write_header(a, entry) != ARCHIVE_OK) {
|
||||
archive_entry_free(entry);
|
||||
return -1;
|
||||
}
|
||||
|
||||
archive_entry_free(entry);
|
||||
|
||||
DIR *dir = opendir(real_path); /* NEW */
|
||||
if (!dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(dir))) { /* NEW */
|
||||
if (strcmp(ent->d_name, ".") == 0 ||
|
||||
strcmp(ent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char child_real[PATH_MAX]; /* NEW */
|
||||
char child_arch[PATH_MAX]; /* NEW */
|
||||
|
||||
snprintf(child_real, sizeof(child_real),
|
||||
"%s/%s", real_path, ent->d_name);
|
||||
|
||||
snprintf(child_arch, sizeof(child_arch),
|
||||
"%s/%s", archive_path, ent->d_name);
|
||||
|
||||
if (add_to_archive(a, child_real, child_arch) != 0) {
|
||||
closedir(dir);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* existing file handling logic continues below */
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
|
||||
int fd = open(real_path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct archive_entry *entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, archive_path);
|
||||
archive_entry_set_size(entry, st.st_size);
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
archive_entry_set_perm(entry, st.st_mode & 0777);
|
||||
|
||||
if (archive_write_header(a, entry) != ARCHIVE_OK) {
|
||||
archive_entry_free(entry);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buf[8192];
|
||||
ssize_t n;
|
||||
|
||||
while ((n = read(fd, buf, sizeof(buf))) > 0) {
|
||||
if (archive_write_data(a, buf, (size_t)n) < 0) {
|
||||
archive_entry_free(entry);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
archive_entry_free(entry);
|
||||
close(fd);
|
||||
|
||||
if (n < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0; /* NEW: ignore other file types */
|
||||
}
|
||||
|
||||
gl_backup_status_t
|
||||
gl_backup_istore(const char *tar_path,
|
||||
const char *glacier_root,
|
||||
const char *uid)
|
||||
{
|
||||
struct archive *a = archive_write_new();
|
||||
if (!a) { return GL_BACKUP_ERR_ALLOC; }
|
||||
|
||||
if (archive_write_add_filter_gzip(a) != ARCHIVE_OK) {
|
||||
archive_write_free(a);
|
||||
return GL_BACKUP_ERR_OPEN;
|
||||
}
|
||||
|
||||
if (archive_write_set_format_pax_restricted(a) != ARCHIVE_OK) {
|
||||
archive_write_free(a);
|
||||
return GL_BACKUP_ERR_OPEN;
|
||||
}
|
||||
|
||||
if (archive_write_open_filename(a, tar_path) != ARCHIVE_OK) {
|
||||
archive_write_free(a);
|
||||
return GL_BACKUP_ERR_OPEN;
|
||||
}
|
||||
|
||||
char index_path[1024];
|
||||
char store_path[1024];
|
||||
char links_path[1024];
|
||||
|
||||
char arch_index[512];
|
||||
char arch_store[512];
|
||||
char arch_links[512];
|
||||
|
||||
snprintf(index_path, sizeof(index_path), "%s/index/%s", glacier_root, uid);
|
||||
snprintf(store_path, sizeof(store_path), "%s/store/%s", glacier_root, uid);
|
||||
snprintf(links_path, sizeof(links_path), "%s/links/%s", glacier_root, uid);
|
||||
|
||||
snprintf(arch_index, sizeof(arch_index), "%s/index", uid);
|
||||
snprintf(arch_store, sizeof(arch_store), "%s/store", uid);
|
||||
snprintf(arch_links, sizeof(arch_links), "%s/links", uid);
|
||||
|
||||
if (add_to_archive(a, index_path, arch_index) != 0) {
|
||||
archive_write_close(a);
|
||||
archive_write_free(a);
|
||||
return GL_BACKUP_ERR_ADD_INDEX;
|
||||
}
|
||||
|
||||
if (add_to_archive(a, store_path, arch_store) != 0) {
|
||||
archive_write_close(a);
|
||||
archive_write_free(a);
|
||||
return GL_BACKUP_ERR_ADD_STORE;
|
||||
}
|
||||
|
||||
if (add_to_archive(a, links_path, arch_links) != 0) {
|
||||
archive_write_close(a);
|
||||
archive_write_free(a);
|
||||
return GL_BACKUP_ERR_ADD_LINKS;
|
||||
}
|
||||
|
||||
if (archive_write_close(a) != ARCHIVE_OK) {
|
||||
archive_write_free(a);
|
||||
return GL_BACKUP_ERR_CLOSE;
|
||||
}
|
||||
|
||||
archive_write_free(a);
|
||||
return GL_BACKUP_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
gl_mkdirp(const char *path, mode_t mode)
|
||||
{
|
||||
char tmp[PATH_MAX];
|
||||
strncpy(tmp, path, sizeof(tmp));
|
||||
tmp[sizeof(tmp) - 1] = '\0';
|
||||
|
||||
for (char *p = tmp + 1; *p; p++) {
|
||||
if (*p == '/') {
|
||||
*p = '\0';
|
||||
if (mkdir(tmp, mode) == -1 && errno != EEXIST) {
|
||||
return -1;
|
||||
}
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
|
||||
if (mkdir(tmp, mode) == -1 && errno != EEXIST) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gl_link_pkg(const char *pkg_store_final, uid_t uid)
|
||||
{
|
||||
char links_base[PATH_MAX];
|
||||
snprintf(links_base, sizeof(links_base), "%s/%d", usr_links.path, uid);
|
||||
|
||||
size_t strip_len = strlen(pkg_store_final);
|
||||
|
||||
/* use a stack-based queue to walk the tree without recursion */
|
||||
char *stack[4096];
|
||||
int top = 0;
|
||||
|
||||
stack[top++] = strdup(pkg_store_final);
|
||||
|
||||
while (top > 0) {
|
||||
char *current = stack[--top];
|
||||
|
||||
DIR *dir = opendir(current);
|
||||
if (!dir) {
|
||||
free(current);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(dir))) {
|
||||
if (ent->d_name[0] == '.') { continue; }
|
||||
|
||||
char full_path[PATH_MAX];
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s",
|
||||
current, ent->d_name);
|
||||
|
||||
struct stat st;
|
||||
if (lstat(full_path, &st) != 0) { continue; }
|
||||
|
||||
/* relative path inside the package, e.g. usr/bin/kilo */
|
||||
const char *rel = full_path + strip_len;
|
||||
if (rel[0] == '/') { rel++; }
|
||||
|
||||
/* strip leading usr/ so bin/kilo lands at links/1000/bin/kilo */
|
||||
if (strncmp(rel, "usr/", 4) == 0) { rel += 4; }
|
||||
|
||||
char link_path[PATH_MAX];
|
||||
snprintf(link_path, sizeof(link_path), "%s/%s",
|
||||
links_base, rel);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
/* create the directory in the links tree */
|
||||
gl_mkdirp(link_path, 0700);
|
||||
|
||||
/* push onto stack to recurse into */
|
||||
if (top < 4096) {
|
||||
stack[top++] = strdup(full_path);
|
||||
}
|
||||
} else if (S_ISREG(st.st_mode)) {
|
||||
/* create parent dirs in links tree */
|
||||
char parent[PATH_MAX];
|
||||
strncpy(parent, link_path, sizeof(parent));
|
||||
char *slash = strrchr(parent, '/');
|
||||
if (slash) {
|
||||
*slash = '\0';
|
||||
gl_mkdirp(parent, 0700);
|
||||
}
|
||||
|
||||
/* remove existing symlink if present */
|
||||
unlink(link_path);
|
||||
|
||||
/* symlink store path -> links tree */
|
||||
if (symlink(full_path, link_path) != 0) {
|
||||
closedir(dir);
|
||||
free(current);
|
||||
while (top > 0) { free(stack[--top]); }
|
||||
return -1;
|
||||
}
|
||||
|
||||
lchown(link_path, uid, (gid_t)-1);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
free(current);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gl_install_status_t
|
||||
gl_install_pkg(const char *gpkg_path, uid_t uid)
|
||||
{
|
||||
/* The archive is opened and the manifest is parsed here */
|
||||
struct archive *a = archive_read_new();
|
||||
archive_read_support_filter_gzip(a);
|
||||
archive_read_support_format_tar(a);
|
||||
|
||||
if (archive_read_open_filename(a, gpkg_path, 10240) != ARCHIVE_OK) {
|
||||
archive_read_free(a);
|
||||
return GL_INSTALL_ERR_OPEN;
|
||||
}
|
||||
|
||||
struct archive_entry *entry;
|
||||
gl_pkg_t pkg = {0};
|
||||
bool manifest_found = false;
|
||||
|
||||
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
|
||||
const char *path = archive_entry_pathname(entry);
|
||||
|
||||
if (strstr(path, "manifest.gpm.cfg")) {
|
||||
char tmp_manifest[PATH_MAX];
|
||||
strncpy(tmp_manifest, "/tmp/gpkg_manifest_XXXXXX",
|
||||
sizeof(tmp_manifest));
|
||||
|
||||
int fd = mkstemp(tmp_manifest);
|
||||
if (fd < 0) {
|
||||
archive_read_free(a);
|
||||
return GL_INSTALL_ERR_MANIFEST;
|
||||
}
|
||||
|
||||
char buffer[8192];
|
||||
ssize_t n;
|
||||
|
||||
while ((n = archive_read_data(a, buffer, sizeof(buffer))) > 0) {
|
||||
ssize_t written = write(fd, buffer, (size_t)n);
|
||||
if (written < 0 || written != n) {
|
||||
close(fd);
|
||||
unlink(tmp_manifest);
|
||||
archive_read_free(a);
|
||||
return GL_INSTALL_ERR_MANIFEST;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
if (gl_gpm2gpkg(tmp_manifest, &pkg) != 0) {
|
||||
unlink(tmp_manifest);
|
||||
archive_read_free(a);
|
||||
return GL_INSTALL_ERR_MANIFEST;
|
||||
}
|
||||
|
||||
unlink(tmp_manifest);
|
||||
manifest_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
archive_read_data_skip(a);
|
||||
}
|
||||
|
||||
archive_read_free(a);
|
||||
|
||||
if (!manifest_found) {
|
||||
return GL_INSTALL_ERR_MANIFEST;
|
||||
}
|
||||
|
||||
char pkg_store_base[PATH_MAX];
|
||||
char pkg_store_final[PATH_MAX];
|
||||
|
||||
/* construct the directory for the package in the store */
|
||||
snprintf(pkg_store_base, sizeof(pkg_store_base),
|
||||
"%s/%d/%s/%s", usr_store.path, uid, pkg.pkg_repo, pkg.pkg_name);
|
||||
|
||||
snprintf(pkg_store_final, sizeof(pkg_store_final),
|
||||
"%s/%s-%d.%d.%d", pkg_store_base, pkg.pkg_name,
|
||||
pkg.pkg_ver.maj, pkg.pkg_ver.min, pkg.pkg_ver.pat);
|
||||
|
||||
if (gl_mkdirp(pkg_store_base, 0700) == -1) {
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_EXTRACT;
|
||||
}
|
||||
|
||||
if (mkdir(pkg_store_final, 0700) == -1) {
|
||||
if (errno == EEXIST) {
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_ALREADY_INSTALLED;
|
||||
}
|
||||
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_EXTRACT;
|
||||
}
|
||||
|
||||
/* now the archive must be reopened in order to extract package files */
|
||||
struct archive *a2 = archive_read_new();
|
||||
archive_read_support_filter_gzip(a2);
|
||||
archive_read_support_format_tar(a2);
|
||||
|
||||
if (archive_read_open_filename(a2, gpkg_path, 10240) != ARCHIVE_OK) {
|
||||
archive_read_free(a2);
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_EXTRACT;
|
||||
}
|
||||
|
||||
char strip_prefix[PATH_MAX];
|
||||
snprintf(strip_prefix, sizeof(strip_prefix),
|
||||
"%s-%d.%d.%d/files/", pkg.pkg_name,
|
||||
pkg.pkg_ver.maj, pkg.pkg_ver.min, pkg.pkg_ver.pat);
|
||||
|
||||
while (archive_read_next_header(a2, &entry) == ARCHIVE_OK) {
|
||||
const char *entry_path = archive_entry_pathname(entry);
|
||||
|
||||
if (strncmp(entry_path, strip_prefix, strlen(strip_prefix)) != 0) {
|
||||
archive_read_data_skip(a2);
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *rel_path = entry_path + strlen(strip_prefix);
|
||||
if (rel_path[0] == '\0') {
|
||||
archive_read_data_skip(a2);
|
||||
continue;
|
||||
}
|
||||
|
||||
char dest_path[PATH_MAX];
|
||||
snprintf(dest_path, sizeof(dest_path), "%s/%s",
|
||||
pkg_store_final, rel_path);
|
||||
|
||||
if (archive_entry_filetype(entry) == AE_IFDIR) {
|
||||
if (mkdir(dest_path, archive_entry_perm(entry)) == -1
|
||||
&& errno != EEXIST) {
|
||||
archive_read_free(a2);
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_EXTRACT;
|
||||
}
|
||||
}
|
||||
else if (archive_entry_filetype(entry) == AE_IFREG) {
|
||||
char parent[PATH_MAX];
|
||||
strncpy(parent, dest_path, sizeof(parent));
|
||||
char *slash = strrchr(parent, '/');
|
||||
if (slash) {
|
||||
*slash = '\0';
|
||||
gl_mkdirp(parent, 0700);
|
||||
}
|
||||
|
||||
int fd = open(dest_path, O_WRONLY | O_CREAT | O_TRUNC,
|
||||
archive_entry_perm(entry));
|
||||
|
||||
if (fd < 0) {
|
||||
archive_read_free(a2);
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_EXTRACT;
|
||||
}
|
||||
|
||||
char buffer[8192];
|
||||
ssize_t n;
|
||||
while ((n = archive_read_data(a2, buffer, sizeof(buffer))) > 0) {
|
||||
ssize_t written = write(fd, buffer, (size_t)n);
|
||||
if (written < 0 || written != n) {
|
||||
close(fd);
|
||||
archive_read_free(a2);
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_EXTRACT;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
archive_read_free(a2);
|
||||
|
||||
/* now the index will be updated */
|
||||
|
||||
/* now the index will be updated */
|
||||
char index_dir[PATH_MAX];
|
||||
snprintf(index_dir, sizeof(index_dir),
|
||||
"%s/%d", usr_index.path, uid);
|
||||
|
||||
/* Resolve "current" symlink to find the actual index file to append to */
|
||||
char current_link[PATH_MAX];
|
||||
snprintf(current_link, sizeof(current_link), "%s/current", index_dir);
|
||||
|
||||
char current_target[PATH_MAX];
|
||||
ssize_t len = readlink(current_link, current_target, sizeof(current_target) - 1);
|
||||
if (len < 0) {
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_INDEX;
|
||||
}
|
||||
current_target[len] = '\0';
|
||||
|
||||
char index_path[PATH_MAX];
|
||||
snprintf(index_path, sizeof(index_path), "%s/%s", index_dir, current_target);
|
||||
|
||||
FILE *idx = fopen(index_path, "a");
|
||||
if (!idx) {
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_INDEX;
|
||||
}
|
||||
|
||||
fprintf(idx, "%s::%s\n", pkg.pkg_repo, pkg.pkg_name);
|
||||
fclose(idx);
|
||||
|
||||
if (gl_rebuild_index(uid, index_dir) != 0) {
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_INDEX;
|
||||
}
|
||||
|
||||
if (gl_link_pkg(pkg_store_final, uid) != 0) {
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_EXTRACT;
|
||||
}
|
||||
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_OK;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user