10/4
This commit is contained in:
1 parent
c432274936
commit
ee4bcc3b77
27 files changed
+2719
-589
No files matched your search
+89
-98
@@ -16,17 +16,28 @@
|
||||
#include "log.h"
|
||||
#include "transaction.h"
|
||||
|
||||
#define GL_STAGE_BASE "/glacier/usr/stage"
|
||||
#define GL_LOCK_BASE "/glacier/usr/lock"
|
||||
#define GL_OLD_BASE "/glacier/usr/old"
|
||||
/* The one directory a scope owns; everything below derives from it.
|
||||
* See the layout comment in common.h. */
|
||||
static void
|
||||
scope_base(gl_ctx_scope_t scope, uid_t uid, char *out)
|
||||
{
|
||||
switch (scope) {
|
||||
case GL_SCOPE_USR:
|
||||
gl_usr_base(uid, out, PATH_MAX);
|
||||
break;
|
||||
case GL_SCOPE_SYS:
|
||||
snprintf(out, PATH_MAX, "%s", GL_SYS_ROOT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define GL_USR_INDEX "/glacier/usr/index"
|
||||
#define GL_USR_STORE "/glacier/usr/store"
|
||||
#define GL_USR_LINKS "/glacier/usr/links"
|
||||
|
||||
#define GL_SYS_INDEX "/glacier/sys/index"
|
||||
#define GL_SYS_STORE "/glacier/sys/store"
|
||||
#define GL_SYS_LINKS "/glacier/sys/links"
|
||||
static void
|
||||
scope_subdir(gl_ctx_scope_t scope, uid_t uid, const char *name, char *out)
|
||||
{
|
||||
char base[PATH_MAX];
|
||||
scope_base(scope, uid, base);
|
||||
snprintf(out, PATH_MAX, "%s/%s", base, name);
|
||||
}
|
||||
|
||||
static int
|
||||
fsync_dir(const char *path)
|
||||
@@ -59,25 +70,16 @@ static void
|
||||
derive_base_paths(gl_ctx_scope_t scope, uid_t uid, char *index_out,
|
||||
char *store_out, char *links_out)
|
||||
{
|
||||
switch (scope) {
|
||||
case GL_SCOPE_USR:
|
||||
snprintf(index_out, PATH_MAX, "/glacier/usr/index/%u", uid);
|
||||
snprintf(store_out, PATH_MAX, "/glacier/usr/store/%u", uid);
|
||||
snprintf(links_out, PATH_MAX, "/glacier/usr/links/%u", uid);
|
||||
break;
|
||||
case GL_SCOPE_SYS:
|
||||
strncpy(index_out, "/glacier/sys/index", PATH_MAX - 1);
|
||||
strncpy(store_out, "/glacier/sys/store", PATH_MAX - 1);
|
||||
/* This is the DEFAULT system-scope links location — for
|
||||
* packages the operator explicitly installs under system
|
||||
* scope that aren't part of the base system. Base-repo
|
||||
* packages get routed to /usr instead, but that's a
|
||||
* per-package decision made in gl_link_pkg/gl_relink_store
|
||||
* (which know a package's repo), not something the context
|
||||
* can decide up front. See GL_BASE_REPO in istoreutils.c. */
|
||||
strncpy(links_out, "/glacier/sys/links", PATH_MAX - 1);
|
||||
break;
|
||||
}
|
||||
scope_subdir(scope, uid, "index", index_out);
|
||||
scope_subdir(scope, uid, "store", store_out);
|
||||
/* For system scope this is the DEFAULT links location — for
|
||||
* packages the operator explicitly installs under system scope that
|
||||
* aren't part of the base system. Base-repo packages get routed to
|
||||
* /usr instead, but that's a per-package decision made in
|
||||
* gl_link_pkg/gl_relink_store (which know a package's repo), not
|
||||
* something the context can decide up front. See GL_BASE_REPO in
|
||||
* istoreutils.c. */
|
||||
scope_subdir(scope, uid, "links", links_out);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -100,7 +102,13 @@ hardlink_tree(const char *src, const char *dest)
|
||||
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(dir))) {
|
||||
if (ent->d_name[0] == '.') { continue; }
|
||||
/* Only . and .. — dotfiles are real content here: a package's
|
||||
* own dotfiles, and the store's .displaced originals (see
|
||||
* gl_link_pkg), both of which would otherwise be dropped from
|
||||
* the stage and lost on commit. */
|
||||
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char s[PATH_MAX], d[PATH_MAX];
|
||||
snprintf(s, sizeof(s), "%s/%s", src, ent->d_name);
|
||||
@@ -149,6 +157,7 @@ gl_init_live_context(gl_context_t *ctx, gl_ctx_scope_t scope, uid_t uid)
|
||||
ctx->mode = GL_CTX_LIVE;
|
||||
ctx->scope = scope;
|
||||
ctx->lock_fd = -1;
|
||||
ctx->overwrite = 0;
|
||||
|
||||
strncpy(ctx->root_path, "/glacier", PATH_MAX - 1);
|
||||
derive_base_paths(scope, uid, ctx->index_path, ctx->store_path,
|
||||
@@ -168,40 +177,55 @@ gl_init_stage_context(gl_context_t *ctx, gl_ctx_scope_t scope, uid_t uid)
|
||||
ctx->mode = GL_CTX_STAGE;
|
||||
ctx->scope = scope;
|
||||
ctx->lock_fd = -1;
|
||||
ctx->overwrite = 0;
|
||||
|
||||
strncpy(ctx->root_path, "/glacier", PATH_MAX - 1);
|
||||
|
||||
char stage_base[PATH_MAX - 7];
|
||||
|
||||
switch (scope) {
|
||||
case GL_SCOPE_USR:
|
||||
snprintf(stage_base, sizeof(stage_base), "/glacier/usr/stage/%u", uid);
|
||||
break;
|
||||
case GL_SCOPE_SYS:
|
||||
strncpy(stage_base, "/glacier/sys/stage", sizeof(stage_base));
|
||||
break;
|
||||
if (scope == GL_SCOPE_USR) {
|
||||
switch (gl_usr_base_check(uid)) {
|
||||
case GL_USR_BASE_OK:
|
||||
break;
|
||||
case GL_USR_BASE_MISSING:
|
||||
lg_printf(2, "No Glacier directory for UID %u. Run doas gstore -n first.", uid);
|
||||
return GL_TXN_ERR_PERM;
|
||||
case GL_USR_BASE_UNSAFE_ROOT:
|
||||
lg_printf(2, "Refusing to run: %s is not root-owned, or is group/world-writable.",
|
||||
GL_USR_ROOT);
|
||||
return GL_TXN_ERR_PERM;
|
||||
case GL_USR_BASE_UNSAFE:
|
||||
lg_printf(2, "Refusing to run: %s/%u is not a directory owned by UID %u with mode 0700.",
|
||||
GL_USR_ROOT, uid, uid);
|
||||
return GL_TXN_ERR_PERM;
|
||||
}
|
||||
}
|
||||
|
||||
char stage_base[PATH_MAX - 7];
|
||||
scope_subdir(scope, uid, "stage", stage_base);
|
||||
|
||||
snprintf(ctx->index_path, PATH_MAX, "%s/index", stage_base);
|
||||
snprintf(ctx->store_path, PATH_MAX, "%s/store", stage_base);
|
||||
snprintf(ctx->links_path, PATH_MAX, "%s/links", stage_base);
|
||||
|
||||
struct stat st;
|
||||
if (stat(stage_base, &st) == 0) {
|
||||
if (gl_rmdir_recursive(stage_base) != 0) {
|
||||
return GL_TXN_ERR_MKDIR;
|
||||
}
|
||||
}
|
||||
|
||||
if (gl_mkdirp(GL_LOCK_BASE, 0700) != 0 && errno != EEXIST) {
|
||||
return GL_TXN_ERR_MKDIR;
|
||||
}
|
||||
|
||||
/* Lock BEFORE clearing a leftover stage: the other order let a
|
||||
* second invocation wipe a running transaction's stage and only
|
||||
* then discover it couldn't get the lock. */
|
||||
gl_txn_status_t lock_status = gl_begin_transaction(ctx);
|
||||
if (lock_status != GL_TXN_OK) {
|
||||
return lock_status;
|
||||
}
|
||||
|
||||
/* EBUSY: stage_base is itself a mount point. Its contents are gone
|
||||
* and the directory stays, which is all a fresh stage needs (the
|
||||
* mkdirps below tolerate it existing). Commit then reports
|
||||
* GL_TXN_ERR_XDEV, as it should for a stage on another filesystem. */
|
||||
struct stat st;
|
||||
if (stat(stage_base, &st) == 0) {
|
||||
if (gl_rmdir_recursive(stage_base) != 0 && errno != EBUSY) {
|
||||
gl_abort_transaction(ctx);
|
||||
return GL_TXN_ERR_MKDIR;
|
||||
}
|
||||
}
|
||||
|
||||
if (gl_mkdirp(ctx->index_path, 0700) != 0) {
|
||||
gl_abort_transaction(ctx);
|
||||
return GL_TXN_ERR_MKDIR;
|
||||
@@ -239,8 +263,13 @@ gl_init_stage_context(gl_context_t *ctx, gl_ctx_scope_t scope, uid_t uid)
|
||||
* area are on different filesystems. */
|
||||
if (hardlink_tree(live_index, ctx->index_path) != 0 ||
|
||||
hardlink_tree(live_store, ctx->store_path) != 0) {
|
||||
/* link() can't cross filesystems: a stage on a different
|
||||
* fs than live is caught here, before anything is staged,
|
||||
* rather than at commit's rename. Same condition, same
|
||||
* status as commit's check. */
|
||||
int seed_errno = errno;
|
||||
gl_abort_transaction(ctx);
|
||||
return GL_TXN_ERR_MKDIR;
|
||||
return seed_errno == EXDEV ? GL_TXN_ERR_XDEV : GL_TXN_ERR_MKDIR;
|
||||
}
|
||||
|
||||
return GL_TXN_OK;
|
||||
@@ -254,15 +283,7 @@ gl_begin_transaction(gl_context_t *ctx)
|
||||
}
|
||||
|
||||
char lock_path[PATH_MAX];
|
||||
switch (ctx->scope) {
|
||||
case GL_SCOPE_USR:
|
||||
snprintf(lock_path, PATH_MAX, "%s/%u.lock", GL_LOCK_BASE,
|
||||
ctx->uid);
|
||||
break;
|
||||
case GL_SCOPE_SYS:
|
||||
snprintf(lock_path, PATH_MAX, "%s/sys.lock", GL_LOCK_BASE);
|
||||
break;
|
||||
}
|
||||
scope_subdir(ctx->scope, ctx->uid, "lock", lock_path);
|
||||
|
||||
int fd = open(lock_path, O_WRONLY | O_CREAT, 0600);
|
||||
if (fd < 0) {
|
||||
@@ -289,15 +310,7 @@ gl_commit_transaction(gl_context_t *ctx)
|
||||
}
|
||||
|
||||
char stage_base[PATH_MAX];
|
||||
switch (ctx->scope) {
|
||||
case GL_SCOPE_USR:
|
||||
snprintf(stage_base, PATH_MAX, "/glacier/usr/stage/%u",
|
||||
ctx->uid);
|
||||
break;
|
||||
case GL_SCOPE_SYS:
|
||||
strncpy(stage_base, "/glacier/sys/stage", PATH_MAX - 1);
|
||||
break;
|
||||
}
|
||||
scope_subdir(ctx->scope, ctx->uid, "stage", stage_base);
|
||||
|
||||
int same = check_same_filesystem(ctx->root_path, stage_base);
|
||||
if (same < 0) {
|
||||
@@ -314,14 +327,7 @@ gl_commit_transaction(gl_context_t *ctx)
|
||||
|
||||
/* Derive old/backup paths */
|
||||
char old_base[PATH_MAX];
|
||||
switch (ctx->scope) {
|
||||
case GL_SCOPE_USR:
|
||||
snprintf(old_base, PATH_MAX, "/glacier/usr/old/%u", ctx->uid);
|
||||
break;
|
||||
case GL_SCOPE_SYS:
|
||||
strncpy(old_base, "/glacier/sys/old", PATH_MAX - 1);
|
||||
break;
|
||||
}
|
||||
scope_subdir(ctx->scope, ctx->uid, "old", old_base);
|
||||
|
||||
char old_index[PATH_MAX], old_store[PATH_MAX], old_links[PATH_MAX];
|
||||
snprintf(old_index, PATH_MAX, "%s/index", old_base);
|
||||
@@ -355,19 +361,12 @@ gl_commit_transaction(gl_context_t *ctx)
|
||||
return GL_TXN_ERR_RENAME;
|
||||
}
|
||||
|
||||
/* fsync the parent directories, not the uid subdirs */
|
||||
switch (ctx->scope) {
|
||||
case GL_SCOPE_USR:
|
||||
fsync_dir("/glacier/usr/index");
|
||||
fsync_dir("/glacier/usr/store");
|
||||
fsync_dir("/glacier/usr/links");
|
||||
break;
|
||||
case GL_SCOPE_SYS:
|
||||
fsync_dir("/glacier/sys/index");
|
||||
fsync_dir("/glacier/sys/store");
|
||||
fsync_dir("/glacier/sys/links");
|
||||
/* fsync the directory the renames happened in */
|
||||
char base[PATH_MAX];
|
||||
scope_base(ctx->scope, ctx->uid, base);
|
||||
fsync_dir(base);
|
||||
if (ctx->scope == GL_SCOPE_SYS) {
|
||||
fsync_dir("/usr"); /* base-repo packages land here instead */
|
||||
break;
|
||||
}
|
||||
|
||||
gl_context_t live_ctx;
|
||||
@@ -397,15 +396,7 @@ gl_abort_transaction(gl_context_t *ctx)
|
||||
|
||||
/* Remove stage area — live is untouched */
|
||||
char stage_uid[PATH_MAX];
|
||||
switch (ctx->scope) {
|
||||
case GL_SCOPE_USR:
|
||||
snprintf(stage_uid, PATH_MAX, "/glacier/usr/stage/%u",
|
||||
ctx->uid);
|
||||
break;
|
||||
case GL_SCOPE_SYS:
|
||||
strncpy(stage_uid, "/glacier/sys/stage", PATH_MAX - 1);
|
||||
break;
|
||||
}
|
||||
scope_subdir(ctx->scope, ctx->uid, "stage", stage_uid);
|
||||
|
||||
gl_rmdir_recursive(stage_uid); /* best-effort; ignore errors */
|
||||
|
||||
|
||||
Reference in new issue
Block a user