Refuse base-repo installs under GL_SCOPE_USR at the library level
gl_install_pkg now returns GL_INSTALL_ERR_SCOPE for scope == GL_SCOPE_USR && repo == "base", checked immediately after manifest parse and before any store path construction or filesystem mutation. Per SECURITY_DESIGN.txt section 8: enforced in the library rather than a CLI, since the split-.so architecture lets third-party tools call gl_install_pkg directly. Also drops src/istoreutils.c.rej, .c.back, .h.back — stale leftovers from the uid -> gl_context_t migration patch; that migration is fully present in the current tree, so these were dead weight. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7b22e3746a
commit
c432274936
5 files changed
+22
-1768
No files matched your search
@@ -1188,6 +1188,27 @@ gl_install_pkg(gl_context_t *ctx, const char *gpkg_path)
|
|||||||
if (!manifest_found) {
|
if (!manifest_found) {
|
||||||
return GL_INSTALL_ERR_MANIFEST;
|
return GL_INSTALL_ERR_MANIFEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Base-repo packages can NEVER be installed under per-user scope.
|
||||||
|
* Enforced here, at the library level, rather than in any CLI: the
|
||||||
|
* split-.so architecture means a third-party tool can call
|
||||||
|
* gl_install_pkg directly without going through gpkg/syspkg, and a
|
||||||
|
* rule only one CLI respects is not a real guarantee. See
|
||||||
|
* SECURITY_DESIGN.txt section 8.
|
||||||
|
*
|
||||||
|
* This is the earliest possible point — pkg.pkg_repo only exists
|
||||||
|
* once the manifest is parsed — and deliberately sits before any
|
||||||
|
* store path construction or filesystem mutation below.
|
||||||
|
*/
|
||||||
|
if (ctx->scope == GL_SCOPE_USR &&
|
||||||
|
strcmp(pkg.pkg_repo, GL_BASE_REPO) == 0) {
|
||||||
|
lg_printf(2,
|
||||||
|
"Refusing to install base-repo package '%s' under user scope.",
|
||||||
|
pkg.pkg_name);
|
||||||
|
gl_free_pkg(&pkg);
|
||||||
|
return GL_INSTALL_ERR_SCOPE;
|
||||||
|
}
|
||||||
|
|
||||||
char pkg_store_base[PATH_MAX];
|
char pkg_store_base[PATH_MAX];
|
||||||
char pkg_store_final[PATH_MAX];
|
char pkg_store_final[PATH_MAX];
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -1,302 +0,0 @@
|
|||||||
--- istoreutils.c.orig 2026-06-01 19:43:00.493773073 +0000
|
|
||||||
+++ istoreutils.c.new 2026-06-01 19:43:00.573469330 +0000
|
|
||||||
@@ -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,7 @@
|
|
||||||
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 };
|
|
||||||
|
|
||||||
bool
|
|
||||||
gl_usr_istore_exists(index_store_t index_or_store, int uid)
|
|
||||||
@@ -253,6 +255,46 @@
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
@@ -409,12 +451,11 @@
|
|
||||||
*/
|
|
||||||
|
|
||||||
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);
|
|
||||||
+ DIR *store_dir = opendir(ctx->store_path);
|
|
||||||
if (!store_dir) return -1;
|
|
||||||
|
|
||||||
/* Write to a new timestamped index file */
|
|
||||||
@@ -422,7 +463,8 @@
|
|
||||||
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 +480,7 @@
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
@@ -476,14 +518,15 @@
|
|
||||||
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 +537,15 @@
|
|
||||||
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,10 +715,10 @@
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
char links_base[PATH_MAX];
|
|
||||||
- snprintf(links_base, sizeof(links_base), "%s/%d", usr_links.path, uid);
|
|
||||||
+ strncpy(links_base, ctx->links_path, PATH_MAX - 1);
|
|
||||||
|
|
||||||
size_t strip_len = strlen(pkg_store_final);
|
|
||||||
|
|
||||||
@@ -729,12 +772,14 @@
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
@@ -760,7 +805,7 @@
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remove symlinks from links tree */
|
|
||||||
- if (gl_unlink_pkg(pkg_store_final, uid) != 0) {
|
|
||||||
+ if (gl_unlink_pkg(ctx, pkg_store_final) != 0) {
|
|
||||||
return GL_REMOVE_ERR_UNLINK;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -770,10 +815,7 @@
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -781,10 +823,10 @@
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
char links_base[PATH_MAX];
|
|
||||||
- snprintf(links_base, sizeof(links_base), "%s/%d", usr_links.path, uid);
|
|
||||||
+ strncpy(links_base, ctx->links_path, PATH_MAX - 1);
|
|
||||||
|
|
||||||
size_t strip_len = strlen(pkg_store_final);
|
|
||||||
|
|
||||||
@@ -854,7 +896,7 @@
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- lchown(link_path, uid, (gid_t)-1);
|
|
||||||
+ lchown(link_path, ctx->uid, (gid_t)-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -898,11 +940,10 @@
|
|
||||||
}
|
|
||||||
|
|
||||||
char
|
|
||||||
-*gl_find_pkg_repo(const char *pkg_name, uid_t uid)
|
|
||||||
+*gl_find_pkg_repo(gl_context_t *ctx, const char *pkg_name)
|
|
||||||
{
|
|
||||||
char store_path[PATH_MAX];
|
|
||||||
- snprintf(store_path, sizeof(store_path),
|
|
||||||
-"/glacier/usr/store/%u", uid);
|
|
||||||
+ strncpy(store_path, ctx->store_path, PATH_MAX - 1);
|
|
||||||
|
|
||||||
DIR *uid_dir = opendir(store_path);
|
|
||||||
if (!uid_dir) { return NULL; }
|
|
||||||
@@ -927,13 +968,12 @@
|
|
||||||
}
|
|
||||||
|
|
||||||
gl_repo_list_t
|
|
||||||
-gl_find_pkg_repos(const char *pkg_name, uid_t uid)
|
|
||||||
+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),
|
|
||||||
- "/glacier/usr/store/%u", uid);
|
|
||||||
+ strncpy(store_path, ctx->store_path, PATH_MAX - 1);
|
|
||||||
|
|
||||||
DIR *uid_dir = opendir(store_path);
|
|
||||||
if (!uid_dir) { return result; }
|
|
||||||
@@ -1033,8 +1073,9 @@
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
@@ -1108,7 +1149,7 @@
|
|
||||||
|
|
||||||
/* 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,
|
|
||||||
@@ -1208,15 +1249,9 @@
|
|
||||||
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);
|
|
||||||
+ 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);
|
|
||||||
@@ -1227,7 +1262,8 @@
|
|
||||||
current_target[len] = '\0';
|
|
||||||
|
|
||||||
char index_path[PATH_MAX];
|
|
||||||
- snprintf(index_path, sizeof(index_path), "%s/%s", index_dir, current_target);
|
|
||||||
+ snprintf(index_path, sizeof(index_path), "%s/%s",
|
|
||||||
+ ctx->index_path, current_target);
|
|
||||||
|
|
||||||
FILE *idx = fopen(index_path, "a");
|
|
||||||
if (!idx) {
|
|
||||||
@@ -1238,12 +1274,12 @@
|
|
||||||
fprintf(idx, "%s::%s\n", pkg.pkg_repo, pkg.pkg_name);
|
|
||||||
fclose(idx);
|
|
||||||
|
|
||||||
- if (gl_rebuild_index(uid, index_dir) != 0) {
|
|
||||||
+ if (gl_rebuild_index(ctx) != 0) {
|
|
||||||
gl_free_pkg(&pkg);
|
|
||||||
return GL_INSTALL_ERR_INDEX;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (gl_link_pkg(pkg_store_final, uid) != 0) {
|
|
||||||
+ if (gl_link_pkg(ctx, pkg_store_final) != 0) {
|
|
||||||
gl_free_pkg(&pkg);
|
|
||||||
return GL_INSTALL_ERR_EXTRACT;
|
|
||||||
}
|
|
||||||
@@ -47,6 +47,7 @@ typedef enum {
|
|||||||
GL_INSTALL_ERR_INDEX = 4,
|
GL_INSTALL_ERR_INDEX = 4,
|
||||||
GL_INSTALL_ERR_ALREADY_INSTALLED = 5,
|
GL_INSTALL_ERR_ALREADY_INSTALLED = 5,
|
||||||
GL_INSTALL_SKIPPED = 6,
|
GL_INSTALL_SKIPPED = 6,
|
||||||
|
GL_INSTALL_ERR_SCOPE = 7,
|
||||||
}
|
}
|
||||||
gl_install_status_t;
|
gl_install_status_t;
|
||||||
|
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
#ifndef ISTOREUTILS_H_
|
|
||||||
#define ISTOREUTILS_H_
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
INDEX,
|
|
||||||
STORE
|
|
||||||
}
|
|
||||||
index_store_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
GL_BACKUP_OK = 0,
|
|
||||||
GL_BACKUP_ERR_ALLOC = 1,
|
|
||||||
GL_BACKUP_ERR_OPEN = 2,
|
|
||||||
GL_BACKUP_ERR_ADD_INDEX = 3,
|
|
||||||
GL_BACKUP_ERR_ADD_STORE = 4,
|
|
||||||
GL_BACKUP_ERR_ADD_LINKS = 5,
|
|
||||||
GL_BACKUP_ERR_CLOSE = 6
|
|
||||||
}
|
|
||||||
gl_backup_status_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
GL_RESTORE_OK = 0,
|
|
||||||
GL_RESTORE_ERR_OPEN = 1,
|
|
||||||
GL_RESTORE_ERR_EXTRACT = 2
|
|
||||||
} gl_restore_status_t;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GL_INSTALL_OK: installation successful
|
|
||||||
* GL_INSTALL_ERR_OPEN: unable to open .gpkg file
|
|
||||||
* GL_INSTALL_ERR_MANIFEST: manifest is missing or unparseable
|
|
||||||
* GL_INSTALL_ERR_EXTRACT: unable to extract files
|
|
||||||
* GL_INSTALL_ERR_INDEX: unable to update index
|
|
||||||
* GL_INSTALL_ERR_ALREADY_INSTALLED: package already installed
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
GL_INSTALL_OK = 0,
|
|
||||||
GL_INSTALL_ERR_OPEN = 1,
|
|
||||||
GL_INSTALL_ERR_MANIFEST = 2,
|
|
||||||
GL_INSTALL_ERR_EXTRACT = 3,
|
|
||||||
GL_INSTALL_ERR_INDEX = 4,
|
|
||||||
GL_INSTALL_ERR_ALREADY_INSTALLED = 5,
|
|
||||||
GL_INSTALL_SKIPPED = 6,
|
|
||||||
}
|
|
||||||
gl_install_status_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
GL_REMOVE_OK = 0,
|
|
||||||
GL_REMOVE_ERR_NOT_FOUND = 1,
|
|
||||||
GL_REMOVE_ERR_UNLINK = 2,
|
|
||||||
GL_REMOVE_ERR_INDEX = 3,
|
|
||||||
} gl_remove_status_t;
|
|
||||||
|
|
||||||
struct gpkg_entry {
|
|
||||||
char *repo;
|
|
||||||
char *pkg;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct gindex {
|
|
||||||
int uid;
|
|
||||||
int listed;
|
|
||||||
struct gpkg_entry *entries;
|
|
||||||
size_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool gl_usr_istore_exists(index_store_t index_or_store, int uid);
|
|
||||||
bool gl_sys_istore_exists(index_store_t index_or_store);
|
|
||||||
|
|
||||||
int gl_init_user(uid_t uid, bool isVerbose);
|
|
||||||
int gl_delete_user(uid_t uid, bool isVerbose);
|
|
||||||
|
|
||||||
int gl_parse_index(struct gindex *idx, FILE *f);
|
|
||||||
void gl_free_index(struct gindex *idx);
|
|
||||||
|
|
||||||
int gl_rebuild_index(uid_t uid, const char *out_dir);
|
|
||||||
|
|
||||||
char *gl_find_pkg_repo(const char *pkg_name, uid_t uid);
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char **repos;
|
|
||||||
size_t count;
|
|
||||||
}
|
|
||||||
gl_repo_list_t;
|
|
||||||
|
|
||||||
gl_repo_list_t gl_find_pkg_repos(const char *pkg_name, uid_t uid);
|
|
||||||
void gl_free_repo_list(gl_repo_list_t *list);
|
|
||||||
|
|
||||||
gl_backup_status_t gl_backup_istore(const char *tar_path, const char *glacier_root,
|
|
||||||
const char *uid);
|
|
||||||
gl_restore_status_t gl_restore_istore(const char *tar_path, const char *glacier_root, const char *uid);
|
|
||||||
|
|
||||||
gl_install_status_t gl_install_pkg(const char *gpkg_path, uid_t uid);
|
|
||||||
gl_remove_status_t gl_remove_pkg(const char *pkg_name, const char *pkg_repo, uid_t uid);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Reference in new issue
Block a user