3/18: Functional package installation backend

This commit is contained in:
lw-everestlinux committed 2026-03-18 09:57:09 -04:00
1 parent e79c96a987
commit 6819fe7c69
24 files changed
+1011 -145

No files matched your search

+829 -14
View File
@@ -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;
}