7/19
This commit is contained in:
1 parent
0d8859b063
commit
7b22e3746a
29 files changed
+3949
-193
No files matched your search
+401
-178
@@ -21,6 +21,7 @@
|
||||
#include "istoreutils.h"
|
||||
#include "log.h"
|
||||
#include "pkg.h"
|
||||
#include "transaction.h"
|
||||
|
||||
static const dir_t usr_index = { "/glacier/usr/index", 0700 };
|
||||
static const dir_t sys_index = { "/glacier/sys/index", 0700 };
|
||||
@@ -29,6 +30,17 @@ 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 };
|
||||
static const dir_t sys_links = { "/glacier/sys/links", 0700 };
|
||||
|
||||
/* Base-system exception: packages in the "base" repo under system
|
||||
* scope get their symlinks placed directly in /usr rather than the
|
||||
* default isolated /glacier/sys/links, so boot/init/scripts that
|
||||
* hardcode /usr/bin, /usr/lib, etc. work without glacier-aware PATH
|
||||
* setup. Everything else installed under system scope (an operator
|
||||
* running `gpkg -s` on something that isn't part of the base system)
|
||||
* stays under /glacier/sys/links. See gl_link_pkg / gl_relink_store. */
|
||||
#define GL_BASE_REPO "base"
|
||||
#define GL_BASE_LINKS_DEST "/usr"
|
||||
|
||||
bool
|
||||
gl_usr_istore_exists(index_store_t index_or_store, int uid)
|
||||
@@ -115,6 +127,36 @@ create_index(const char *ind_path, uid_t uid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_dir(const char *d)
|
||||
{
|
||||
struct stat st;
|
||||
return stat(d, &st) == 0 && S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* is_glacier_symlink
|
||||
* Returns true only if `path` is a symlink whose target lives under
|
||||
* `store_prefix` — i.e. a symlink glacier itself created, as opposed
|
||||
* to some unrelated file, directory, or foreign symlink that happens
|
||||
* to occupy that path. This distinction only matters once a scope's
|
||||
* links_path can alias a directory glacier doesn't exclusively own
|
||||
* (e.g. /usr for system-scope packages) rather than always being an
|
||||
* isolated glacier-only tree. Everywhere this is used, the rule is the
|
||||
* same: if this returns false, never touch the path — not overwrite
|
||||
* it, not prune it, nothing.
|
||||
*/
|
||||
static bool
|
||||
is_glacier_symlink(const char *path, const char *store_prefix)
|
||||
{
|
||||
char target[PATH_MAX];
|
||||
ssize_t n = readlink(path, target, sizeof(target) - 1);
|
||||
if (n < 0) { return false; } /* not a symlink, or doesn't exist */
|
||||
target[n] = '\0';
|
||||
return strncmp(target, store_prefix, strlen(store_prefix)) == 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
gl_init_user(uid_t uid, bool isVerbose)
|
||||
{
|
||||
@@ -253,6 +295,46 @@ gl_init_user(uid_t uid, bool isVerbose)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gl_init_sys(bool isVerbose)
|
||||
{
|
||||
const char *paths[] = {
|
||||
sys_index.path,
|
||||
sys_store.path,
|
||||
sys_links.path,
|
||||
};
|
||||
mode_t modes[] = {
|
||||
sys_index.mode,
|
||||
sys_store.mode,
|
||||
sys_links.mode,
|
||||
};
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (isVerbose) { lg_printf(0, "Creating %s", paths[i]); }
|
||||
|
||||
if (mkdir(paths[i], modes[i]) == -1) {
|
||||
switch (errno) {
|
||||
case EEXIST:
|
||||
break;
|
||||
case EACCES:
|
||||
lg_printf(2, "Cannot create %s: permission denied", paths[i]);
|
||||
return 1;
|
||||
case ENOSPC:
|
||||
lg_printf(2, "Cannot create %s: no space left on device", paths[i]);
|
||||
return 1;
|
||||
case EROFS:
|
||||
lg_printf(2, "Cannot create %s: read-only filesystem", paths[i]);
|
||||
return 1;
|
||||
default:
|
||||
lg_printf(2, "Cannot create %s: mkdir failed", paths[i]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
trim(char *s)
|
||||
{
|
||||
@@ -313,17 +395,34 @@ gl_parse_index(struct gindex *idx, FILE *f)
|
||||
|
||||
char *first = strstr(line, "::");
|
||||
if (!first) { continue; }
|
||||
|
||||
*first = 0;
|
||||
|
||||
char *repo = line;
|
||||
char *pkg = first + 2;
|
||||
char *rest = first + 2;
|
||||
|
||||
char *second = strstr(rest, "::");
|
||||
char *pkg;
|
||||
char *ver;
|
||||
if (second) {
|
||||
*second = 0;
|
||||
pkg = rest;
|
||||
ver = second + 2;
|
||||
} else {
|
||||
/* Older index files may only have repo::pkg with no
|
||||
* version field — tolerate that rather than reject it. */
|
||||
pkg = rest;
|
||||
ver = "unknown";
|
||||
}
|
||||
|
||||
trim(repo);
|
||||
trim(pkg);
|
||||
trim(ver);
|
||||
|
||||
struct gpkg_entry ent;
|
||||
ent.repo = dupstr(repo);
|
||||
ent.pkg = dupstr(pkg);
|
||||
if (!ent.repo || !ent.pkg) { return -1; }
|
||||
ent.ver = dupstr(ver);
|
||||
if (!ent.repo || !ent.pkg || !ent.ver) { return -1; }
|
||||
|
||||
struct gpkg_entry *tmp =
|
||||
realloc(idx->entries, sizeof(*idx->entries) * (idx->count + 1));
|
||||
@@ -341,80 +440,19 @@ 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[i].ver);
|
||||
}
|
||||
|
||||
free(idx->entries);
|
||||
idx->entries = NULL;
|
||||
idx->count = 0;
|
||||
}
|
||||
/*
|
||||
int
|
||||
gl_rebuild_index(uid_t uid, const char *out_dir)
|
||||
gl_rebuild_index(gl_context_t *ctx)
|
||||
{
|
||||
char store_path[PATH_MAX];
|
||||
snprintf(store_path, sizeof(store_path), "%s/%d", usr_store.path, uid);
|
||||
if (!ctx) { return -1; }
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
DIR *store_dir = opendir(ctx->store_path);
|
||||
if (!store_dir) return -1;
|
||||
|
||||
/* Write to a new timestamped index file */
|
||||
@@ -422,7 +460,8 @@ gl_rebuild_index(uid_t uid, const char *out_dir)
|
||||
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);
|
||||
snprintf(new_index_path, sizeof(new_index_path), "%s/%s",
|
||||
ctx->index_path, fname);
|
||||
|
||||
FILE *out = fopen(new_index_path, "w");
|
||||
if (!out) {
|
||||
@@ -438,7 +477,7 @@ gl_rebuild_index(uid_t uid, const char *out_dir)
|
||||
|
||||
char repo_path[PATH_MAX];
|
||||
snprintf(repo_path, sizeof(repo_path), "%s/%s",
|
||||
store_path, repo_ent->d_name);
|
||||
ctx->store_path, repo_ent->d_name);
|
||||
|
||||
struct stat rst;
|
||||
if (stat(repo_path, &rst) != 0 || !S_ISDIR(rst.st_mode)) {
|
||||
@@ -461,9 +500,33 @@ gl_rebuild_index(uid_t uid, const char *out_dir)
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(out, "%s::%s\n",
|
||||
/* One version dir per package name, named
|
||||
* "<pkg_name>-<version>" (see gl_cat_dir_and_ver
|
||||
* in pkg.c, which is what created it). Strip the
|
||||
* known "<pkg_name>-" prefix to recover just the
|
||||
* version part — safe even if pkg_name itself
|
||||
* contains dashes, since we already know its
|
||||
* exact length from pkg_ent->d_name. */
|
||||
char pkg_ver[64] = "unknown";
|
||||
DIR *ver_dir = opendir(pkg_path);
|
||||
if (ver_dir) {
|
||||
struct dirent *ver_ent;
|
||||
while ((ver_ent = readdir(ver_dir))) {
|
||||
if (ver_ent->d_name[0] == '.') { continue; }
|
||||
size_t prefix_len = strlen(pkg_ent->d_name) + 1;
|
||||
if (strlen(ver_ent->d_name) > prefix_len) {
|
||||
snprintf(pkg_ver, sizeof(pkg_ver), "%s",
|
||||
ver_ent->d_name + prefix_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
closedir(ver_dir);
|
||||
}
|
||||
|
||||
fprintf(out, "%s::%s::%s\n",
|
||||
repo_ent->d_name,
|
||||
pkg_ent->d_name);
|
||||
pkg_ent->d_name,
|
||||
pkg_ver);
|
||||
listed++;
|
||||
}
|
||||
closedir(repo_dir);
|
||||
@@ -476,14 +539,15 @@ gl_rebuild_index(uid_t uid, const char *out_dir)
|
||||
if (!tmp_in) return -1;
|
||||
|
||||
char tmp_path[PATH_MAX];
|
||||
snprintf(tmp_path, sizeof(tmp_path), "%s/.index_tmp_XXXXXX", out_dir);
|
||||
snprintf(tmp_path, sizeof(tmp_path), "%s/.index_tmp_XXXXXX",
|
||||
ctx->index_path);
|
||||
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, "uid = %d\n", ctx->uid);
|
||||
fprintf(tmp_out, "listed = %zu\n\n", listed);
|
||||
|
||||
char line[512];
|
||||
@@ -494,15 +558,15 @@ gl_rebuild_index(uid_t uid, const char *out_dir)
|
||||
fclose(tmp_out);
|
||||
|
||||
rename(tmp_path, new_index_path);
|
||||
chown(new_index_path, uid, (gid_t)-1);
|
||||
chown(new_index_path, ctx->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);
|
||||
snprintf(link_path, sizeof(link_path), "%s/current", ctx->index_path);
|
||||
unlink(link_path);
|
||||
|
||||
if (symlink(fname, link_path) != 0) { return -1; }
|
||||
lchown(link_path, uid, (gid_t)-1);
|
||||
lchown(link_path, ctx->uid, (gid_t)-1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -672,62 +736,14 @@ gl_backup_istore(const char *tar_path,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int
|
||||
gl_rmdir_recursive(const char *path)
|
||||
{
|
||||
DIR *dir = opendir(path);
|
||||
if (!dir) { return -1; }
|
||||
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(dir))) {
|
||||
if (ent->d_name[0] == '.') { continue; }
|
||||
|
||||
char full[PATH_MAX];
|
||||
snprintf(full, sizeof(full), "%s/%s", path, ent->d_name);
|
||||
|
||||
struct stat st;
|
||||
if (lstat(full, &st) != 0) { continue; }
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
gl_rmdir_recursive(full);
|
||||
}
|
||||
else {
|
||||
unlink(full);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return rmdir(path);
|
||||
}
|
||||
|
||||
static int
|
||||
gl_unlink_pkg(const char *pkg_store_final, uid_t uid)
|
||||
gl_unlink_pkg(gl_context_t *ctx, const char *pkg_store_final, const char *pkg_repo)
|
||||
{
|
||||
char links_base[PATH_MAX];
|
||||
snprintf(links_base, sizeof(links_base), "%s/%d", usr_links.path, uid);
|
||||
if (ctx->scope == GL_SCOPE_SYS && strcmp(pkg_repo, GL_BASE_REPO) == 0) {
|
||||
snprintf(links_base, sizeof(links_base), "%s", GL_BASE_LINKS_DEST);
|
||||
} else {
|
||||
snprintf(links_base, sizeof(links_base), "%s", ctx->links_path);
|
||||
}
|
||||
|
||||
size_t strip_len = strlen(pkg_store_final);
|
||||
|
||||
@@ -781,12 +797,14 @@ gl_unlink_pkg(const char *pkg_store_final, uid_t uid)
|
||||
}
|
||||
|
||||
gl_remove_status_t
|
||||
gl_remove_pkg(const char *pkg_name, const char *pkg_repo, uid_t uid)
|
||||
gl_remove_pkg(gl_context_t *ctx, const char *pkg_name, const char *pkg_repo)
|
||||
{
|
||||
if (!ctx) { return GL_REMOVE_ERR_NOT_FOUND; }
|
||||
|
||||
/* find the package base directory in the store */
|
||||
char pkg_store_base[PATH_MAX];
|
||||
snprintf(pkg_store_base, sizeof(pkg_store_base),
|
||||
"%s/%d/%s/%s", usr_store.path, uid, pkg_repo, pkg_name);
|
||||
"%s/%s/%s", ctx->store_path, pkg_repo, pkg_name);
|
||||
|
||||
struct stat st;
|
||||
if (stat(pkg_store_base, &st) != 0 || !S_ISDIR(st.st_mode)) {
|
||||
@@ -811,9 +829,14 @@ gl_remove_pkg(const char *pkg_name, const char *pkg_repo, uid_t uid)
|
||||
return GL_REMOVE_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* remove symlinks from links tree */
|
||||
if (gl_unlink_pkg(pkg_store_final, uid) != 0) {
|
||||
return GL_REMOVE_ERR_UNLINK;
|
||||
/* remove symlinks from links tree. GL_CTX_STAGE: skip — the
|
||||
* stage links tree isn't seeded (links are fully derived from
|
||||
* the store post-commit by relink_store), so there's nothing
|
||||
* there to unlink. */
|
||||
if (ctx->mode == GL_CTX_LIVE) {
|
||||
if (gl_unlink_pkg(ctx, pkg_store_final, pkg_repo) != 0) {
|
||||
return GL_REMOVE_ERR_UNLINK;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove package from store */
|
||||
@@ -822,10 +845,7 @@ gl_remove_pkg(const char *pkg_name, const char *pkg_repo, uid_t uid)
|
||||
}
|
||||
|
||||
/* rebuild index */
|
||||
char index_dir[PATH_MAX];
|
||||
snprintf(index_dir, sizeof(index_dir), "%s/%d", usr_index.path, uid);
|
||||
|
||||
if (gl_rebuild_index(uid, index_dir) != 0) {
|
||||
if (gl_rebuild_index(ctx) != 0) {
|
||||
return GL_REMOVE_ERR_INDEX;
|
||||
}
|
||||
|
||||
@@ -833,10 +853,14 @@ gl_remove_pkg(const char *pkg_name, const char *pkg_repo, uid_t uid)
|
||||
}
|
||||
|
||||
static int
|
||||
gl_link_pkg(const char *pkg_store_final, uid_t uid)
|
||||
gl_link_pkg(gl_context_t *ctx, const char *pkg_store_final, const char *pkg_repo)
|
||||
{
|
||||
char links_base[PATH_MAX];
|
||||
snprintf(links_base, sizeof(links_base), "%s/%d", usr_links.path, uid);
|
||||
if (ctx->scope == GL_SCOPE_SYS && strcmp(pkg_repo, GL_BASE_REPO) == 0) {
|
||||
snprintf(links_base, sizeof(links_base), "%s", GL_BASE_LINKS_DEST);
|
||||
} else {
|
||||
snprintf(links_base, sizeof(links_base), "%s", ctx->links_path);
|
||||
}
|
||||
|
||||
size_t strip_len = strlen(pkg_store_final);
|
||||
|
||||
@@ -895,18 +919,29 @@ gl_link_pkg(const char *pkg_store_final, uid_t uid)
|
||||
gl_mkdirp(parent, 0700);
|
||||
}
|
||||
|
||||
/* remove existing symlink if present */
|
||||
unlink(link_path);
|
||||
/* Only ever replace an existing entry if it's a
|
||||
* symlink we created ourselves. See
|
||||
* is_glacier_symlink's comment for why this
|
||||
* check exists at all. */
|
||||
struct stat existing_st;
|
||||
if (lstat(link_path, &existing_st) == 0 &&
|
||||
!is_glacier_symlink(link_path, ctx->store_path)) {
|
||||
lg_printf(1,
|
||||
"Refusing to overwrite non-glacier path: %s",
|
||||
link_path);
|
||||
} else {
|
||||
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;
|
||||
/* 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, ctx->uid, (gid_t)-1);
|
||||
}
|
||||
|
||||
lchown(link_path, uid, (gid_t)-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -949,6 +984,78 @@ gl_delete_user(uid_t uid, bool isVerbose)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char
|
||||
*gl_find_pkg_repo(gl_context_t *ctx, const char *pkg_name)
|
||||
{
|
||||
char store_path[PATH_MAX];
|
||||
snprintf(store_path, sizeof(store_path), "%s", ctx->store_path);
|
||||
|
||||
DIR *uid_dir = opendir(store_path);
|
||||
if (!uid_dir) { return NULL; }
|
||||
|
||||
struct dirent *repo_entry;
|
||||
while ((repo_entry = readdir(uid_dir)) != NULL) {
|
||||
if (repo_entry->d_name[0] == '.') { continue; }
|
||||
|
||||
char pkg_path[PATH_MAX];
|
||||
snprintf(pkg_path, sizeof(pkg_path), "%s/%s/%s", store_path, repo_entry->d_name, pkg_name);
|
||||
|
||||
struct stat st;
|
||||
if (stat(pkg_path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
char *repo = strdup(repo_entry->d_name);
|
||||
closedir(uid_dir);
|
||||
return repo;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(uid_dir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gl_repo_list_t
|
||||
gl_find_pkg_repos(gl_context_t *ctx, const char *pkg_name)
|
||||
{
|
||||
gl_repo_list_t result = { NULL, 0 };
|
||||
|
||||
char store_path[PATH_MAX];
|
||||
snprintf(store_path, sizeof(store_path), "%s", ctx->store_path);
|
||||
|
||||
DIR *uid_dir = opendir(store_path);
|
||||
if (!uid_dir) { return result; }
|
||||
|
||||
struct dirent *repo_entry;
|
||||
while ((repo_entry = readdir(uid_dir)) != NULL) {
|
||||
if (repo_entry->d_name[0] == '.') { continue; }
|
||||
|
||||
char pkg_path[PATH_MAX];
|
||||
snprintf(pkg_path, sizeof(pkg_path),
|
||||
"%s/%s/%s", store_path, repo_entry->d_name, pkg_name);
|
||||
|
||||
struct stat st;
|
||||
if (stat(pkg_path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
char **tmp = realloc(result.repos,
|
||||
(result.count + 1) * sizeof(char *));
|
||||
if (!tmp) { break; }
|
||||
result.repos = tmp;
|
||||
result.repos[result.count] = strdup(repo_entry->d_name);
|
||||
result.count++;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(uid_dir);
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
gl_free_repo_list(gl_repo_list_t *list)
|
||||
{
|
||||
if (!list) { return; }
|
||||
for (size_t i = 0; i < list->count; i++) { free(list->repos[i]); }
|
||||
free(list->repos);
|
||||
list->repos = NULL;
|
||||
list->count = 0;
|
||||
}
|
||||
|
||||
gl_restore_status_t
|
||||
gl_restore_istore(const char *tar_path, const char *glacier_root, const char *uid)
|
||||
{
|
||||
@@ -1011,8 +1118,9 @@ gl_restore_istore(const char *tar_path, const char *glacier_root, const char *ui
|
||||
}
|
||||
|
||||
gl_install_status_t
|
||||
gl_install_pkg(const char *gpkg_path, uid_t uid)
|
||||
gl_install_pkg(gl_context_t *ctx, const char *gpkg_path)
|
||||
{
|
||||
if (!ctx) { return GL_INSTALL_ERR_OPEN; }
|
||||
char original_cwd[PATH_MAX];
|
||||
if (getcwd(original_cwd, sizeof(original_cwd)) == NULL) {
|
||||
return GL_INSTALL_ERR_OPEN;
|
||||
@@ -1086,7 +1194,7 @@ gl_install_pkg(const char *gpkg_path, uid_t uid)
|
||||
|
||||
/* 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);
|
||||
"%s/%s/%s", ctx->store_path, 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,
|
||||
@@ -1186,15 +1294,13 @@ gl_install_pkg(const char *gpkg_path, uid_t uid)
|
||||
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 */
|
||||
/* Resolve "current" symlink to confirm an index exists to rebuild
|
||||
* against. The actual write happens in gl_rebuild_index below —
|
||||
* anything written here would just be overwritten by that call,
|
||||
* since it creates a fresh timestamped index file and repoints
|
||||
* "current" at it rather than editing this one in place. */
|
||||
char current_link[PATH_MAX];
|
||||
snprintf(current_link, sizeof(current_link), "%s/current", index_dir);
|
||||
snprintf(current_link, sizeof(current_link), "%s/current", ctx->index_path);
|
||||
|
||||
char current_target[PATH_MAX];
|
||||
ssize_t len = readlink(current_link, current_target, sizeof(current_target) - 1);
|
||||
@@ -1204,31 +1310,148 @@ gl_install_pkg(const char *gpkg_path, uid_t uid)
|
||||
}
|
||||
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) {
|
||||
if (gl_rebuild_index(ctx) != 0) {
|
||||
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;
|
||||
if (ctx->mode == GL_CTX_LIVE) {
|
||||
if (gl_link_pkg(ctx, pkg_store_final, pkg.pkg_repo) != 0) {
|
||||
gl_free_pkg(&pkg);
|
||||
return GL_INSTALL_ERR_EXTRACT;
|
||||
}
|
||||
}
|
||||
/* GL_CTX_STAGE: skip. A symlink written here would target
|
||||
* ctx->store_path, which is the stage path — it goes stale the
|
||||
* moment gl_commit_transaction renames the store into its live
|
||||
* location. The links tree is fully derived from the store, so
|
||||
* it's regenerated wholesale post-commit instead of tracked
|
||||
* incrementally here (see relink_store in transaction.c). */
|
||||
|
||||
gl_free_pkg(&pkg);
|
||||
chdir(original_cwd);
|
||||
return GL_INSTALL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* prune_stale_links
|
||||
* Recursively removes only the symlinks glacier itself created (per
|
||||
* is_glacier_symlink) whose store target no longer exists — i.e. links
|
||||
* left behind by a package that's since been removed. Directories,
|
||||
* regular files, and symlinks glacier didn't create are never touched,
|
||||
* recursed into for directories aside. This must stay this
|
||||
* conservative because links_path is not always glacier-exclusive
|
||||
* territory: for system-scope packages it can be /usr itself, which
|
||||
* holds plenty of content glacier has no business deleting.
|
||||
*/
|
||||
static void
|
||||
prune_stale_links(const char *dir_path, const char *store_prefix)
|
||||
{
|
||||
DIR *dir = opendir(dir_path);
|
||||
if (!dir) { return; }
|
||||
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(dir))) {
|
||||
if (ent->d_name[0] == '.') { continue; }
|
||||
|
||||
char full[PATH_MAX];
|
||||
snprintf(full, sizeof(full), "%s/%s", dir_path, ent->d_name);
|
||||
|
||||
struct stat lst;
|
||||
if (lstat(full, &lst) != 0) { continue; }
|
||||
|
||||
if (S_ISLNK(lst.st_mode)) {
|
||||
if (!is_glacier_symlink(full, store_prefix)) {
|
||||
continue; /* not ours - never touch */
|
||||
}
|
||||
struct stat target_st;
|
||||
if (stat(full, &target_st) != 0 && errno == ENOENT) {
|
||||
/* our symlink, but its target is gone */
|
||||
unlink(full);
|
||||
}
|
||||
} else if (S_ISDIR(lst.st_mode)) {
|
||||
prune_stale_links(full, store_prefix);
|
||||
}
|
||||
/* regular files: never ours, never touched */
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
int
|
||||
gl_relink_store(gl_context_t *ctx)
|
||||
{
|
||||
/* This function's contract is "derive the links tree entirely
|
||||
* from the store" (same as gl_rebuild_index does for the index) -
|
||||
* but "derive fully" now means prune-then-refresh, not
|
||||
* wipe-then-rebuild. links_path may be a directory glacier
|
||||
* doesn't exclusively own (e.g. /usr for system scope), so a
|
||||
* recursive delete of the whole tree is off the table; only
|
||||
* glacier's own stale symlinks get removed. gl_mkdirp is
|
||||
* non-destructive (mkdir tolerates EEXIST) so this is still safe
|
||||
* to call even if links_path already exists. */
|
||||
if (gl_mkdirp(ctx->links_path, 0700) != 0) {
|
||||
return -1;
|
||||
}
|
||||
prune_stale_links(ctx->links_path, ctx->store_path);
|
||||
|
||||
/* Base-repo packages under system scope route to /usr instead of
|
||||
* ctx->links_path (see GL_BASE_REPO) — prune there too, since a
|
||||
* package that WAS in the base repo and got removed/moved leaves
|
||||
* its stale symlink in /usr, not in ctx->links_path. Only relevant
|
||||
* for system scope; /usr is never a valid destination for
|
||||
* per-uid packages. */
|
||||
if (ctx->scope == GL_SCOPE_SYS) {
|
||||
prune_stale_links(GL_BASE_LINKS_DEST, ctx->store_path);
|
||||
}
|
||||
|
||||
DIR *repo_dir = opendir(ctx->store_path);
|
||||
if (!repo_dir) return (errno == ENOENT) ? 0 : -1;
|
||||
|
||||
struct dirent *repo_ent;
|
||||
while ((repo_ent = readdir(repo_dir))) {
|
||||
if (repo_ent->d_name[0] == '.') continue;
|
||||
|
||||
char repo_path[PATH_MAX];
|
||||
snprintf(repo_path, sizeof(repo_path), "%s/%s",
|
||||
ctx->store_path, repo_ent->d_name);
|
||||
if (!is_dir(repo_path)) continue;
|
||||
|
||||
DIR *name_dir = opendir(repo_path);
|
||||
if (!name_dir) continue;
|
||||
|
||||
struct dirent *name_ent;
|
||||
while ((name_ent = readdir(name_dir))) {
|
||||
if (name_ent->d_name[0] == '.') continue;
|
||||
|
||||
char name_path[PATH_MAX];
|
||||
snprintf(name_path, sizeof(name_path), "%s/%s",
|
||||
repo_path, name_ent->d_name);
|
||||
if (!is_dir(name_path)) continue;
|
||||
|
||||
/* one version dir per package name today */
|
||||
DIR *ver_dir = opendir(name_path);
|
||||
if (!ver_dir) continue;
|
||||
|
||||
struct dirent *ver_ent;
|
||||
while ((ver_ent = readdir(ver_dir))) {
|
||||
if (ver_ent->d_name[0] == '.') continue;
|
||||
|
||||
char ver_path[PATH_MAX];
|
||||
snprintf(ver_path, sizeof(ver_path), "%s/%s",
|
||||
name_path, ver_ent->d_name);
|
||||
if (!is_dir(ver_path)) continue;
|
||||
|
||||
if (gl_link_pkg(ctx, ver_path, repo_ent->d_name) != 0) {
|
||||
closedir(ver_dir); closedir(name_dir);
|
||||
closedir(repo_dir);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
closedir(ver_dir);
|
||||
}
|
||||
closedir(name_dir);
|
||||
}
|
||||
closedir(repo_dir);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user