7/19
This commit is contained in:
177
PACKAGE_SCOPES.txt
Normal file
177
PACKAGE_SCOPES.txt
Normal file
@@ -0,0 +1,177 @@
|
||||
+------------------------------------+
|
||||
| Package scopes and symlink routing |
|
||||
+------------------------------------+
|
||||
|
||||
This document records the design decisions behind how glacier packages
|
||||
are scoped, how their repo determines where their symlinks land, and
|
||||
what "boot-critical" means in this system. It exists because these
|
||||
rules aren't obvious from reading the code alone, and getting them
|
||||
wrong (particularly the symlink-placement rule) has real consequences
|
||||
up to and including destroying a live /usr.
|
||||
|
||||
|
||||
+------------------------------------+
|
||||
| 1. The two scopes |
|
||||
+------------------------------------+
|
||||
|
||||
USR (GL_SCOPE_USR)
|
||||
Per-uid packages. Index/store/links all live under a specific
|
||||
uid's tree:
|
||||
/glacier/usr/index/<uid>
|
||||
/glacier/usr/store/<uid>
|
||||
/glacier/usr/links/<uid>
|
||||
Symlinks always land under /glacier/usr/links/<uid>/{bin,...}.
|
||||
There is no base-repo exception for USR scope. Nothing installed
|
||||
under a per-user scope is ever considered part of the base system,
|
||||
so nothing here ever touches /usr.
|
||||
|
||||
SYS (GL_SCOPE_SYS)
|
||||
System-wide packages, not owned by any particular uid:
|
||||
/glacier/sys/index
|
||||
/glacier/sys/store
|
||||
Symlink destination depends on the package's repo (see section 3).
|
||||
Requires root. gpkg enforces this via geteuid() before it will
|
||||
even attempt a system-scope operation.
|
||||
|
||||
|
||||
+------------------------------------+
|
||||
| 2. Repo semantics |
|
||||
+------------------------------------+
|
||||
|
||||
A package's repo (PKG_REPO in its manifest) is not just a label for
|
||||
organizing the store — under system scope, it is the one thing that
|
||||
decides where that package's activation symlinks physically land.
|
||||
|
||||
base
|
||||
Packages that make up a minimal working system. ALL packages
|
||||
in this repo are considered boot-critical BY DEFINITION — this
|
||||
is a repo-level classification, not a per-package flag. A
|
||||
package doesn't have to be individually essential for booting
|
||||
to count; if it's filed under base, it's treated as part of
|
||||
the base system, full stop.
|
||||
|
||||
extra
|
||||
Software that may be important but is not necessary to run
|
||||
Everest, nor to install it.
|
||||
|
||||
community
|
||||
Everything else.
|
||||
|
||||
NOTE: extra and community are currently NOT distinguished by the
|
||||
library itself — both simply mean "not base," and both route to
|
||||
the same default system-scope links location. If they need to
|
||||
diverge further later (different confirmation prompts, different
|
||||
trust handling, whatever), that is a deliberate future change, not
|
||||
something already implemented.
|
||||
|
||||
|
||||
+------------------------------------+
|
||||
| 3. Symlink placement rule |
|
||||
+------------------------------------+
|
||||
|
||||
scope == USR -> /glacier/usr/links/<uid>
|
||||
scope == SYS && repo == "base" -> /usr (*)
|
||||
scope == SYS && repo != "base" -> /glacier/sys/links
|
||||
|
||||
(*) This is the base-system exception. Packages that are part of the
|
||||
minimal working system get their symlinks placed directly into
|
||||
/usr/bin, /usr/lib, etc. — real FHS locations — so that anything
|
||||
with a hardcoded path (init scripts, systemd units, shebang lines)
|
||||
works without needing glacier-aware PATH setup.
|
||||
|
||||
Everything an operator installs under system scope that ISN'T
|
||||
part of the base system stays isolated under /glacier/sys/links,
|
||||
same as it always has. The base system is the exception to the
|
||||
norm, not the other way around.
|
||||
|
||||
This decision is made per-package, at the point where a package's repo
|
||||
is actually known (inside gl_link_pkg / gl_unlink_pkg / the walk loop
|
||||
in gl_relink_store) — NOT baked into the context at creation time.
|
||||
gl_context_t's links_path field always holds the DEFAULT destination
|
||||
for that scope; the /usr override is computed separately per package.
|
||||
|
||||
|
||||
+------------------------------------+
|
||||
| 4. Why /usr as a target is safe |
|
||||
+------------------------------------+
|
||||
|
||||
/usr is not glacier-exclusive territory the way /glacier/sys/links is.
|
||||
It holds plenty of content glacier has no business touching. Every
|
||||
place that reads or writes into a links destination therefore treats
|
||||
that destination as "possibly shared, not owned":
|
||||
|
||||
- is_glacier_symlink(path, store_prefix) is the single source of
|
||||
truth for "did glacier itself create this." It reports true only
|
||||
if `path` is a symlink whose target lives under the relevant
|
||||
store path. Anything else — a real file, a directory, a foreign
|
||||
symlink pointing somewhere else entirely — is never touched.
|
||||
|
||||
- gl_link_pkg refuses to overwrite an existing path unless
|
||||
is_glacier_symlink says it's safe to replace. It will not clobber
|
||||
a foreign file that happens to occupy the same path.
|
||||
|
||||
- gl_relink_store no longer wipes and rebuilds its links directory
|
||||
wholesale (that was safe only when the target was glacier-only
|
||||
territory). It prunes ONLY stale symlinks glacier itself created
|
||||
(identified via is_glacier_symlink, removed only if their store
|
||||
target no longer exists) and leaves everything else alone.
|
||||
|
||||
- A single system-scope relink pass prunes BOTH possible
|
||||
destinations (/glacier/sys/links and /usr), since it can't know in
|
||||
advance whether any base-repo packages are involved without
|
||||
checking both.
|
||||
|
||||
- The stage tree's links directory is no longer hardlink-seeded at
|
||||
all. It was always unused (gl_link_pkg only ever runs against a
|
||||
LIVE context; gl_relink_store always builds its own fresh live
|
||||
context rather than touching a staged one) — and now that live
|
||||
links_path can be /usr, seeding it would mean hardlinking the
|
||||
entire /usr tree on every staged system transaction, and risking
|
||||
EXDEV outright if /usr and the stage area are on different
|
||||
filesystems.
|
||||
|
||||
|
||||
+------------------------------------+
|
||||
| 5. Updating base-system packages |
|
||||
+------------------------------------+
|
||||
|
||||
Base-repo packages are tracked in the system index like anything else,
|
||||
and updating them works exactly the same way updating any other
|
||||
package does — same staged transaction, same commit/rollback. No
|
||||
separate mechanism was built for this, because none was needed:
|
||||
|
||||
gpkg -s newmusl.gpkg
|
||||
|
||||
...updates musl in place under system scope, symlinks and all,
|
||||
whether it was originally installed by a bootstrap tool or by a normal
|
||||
`gpkg -s` call later. There is no meaningful distinction between
|
||||
"how it originally got there" and "how you update it."
|
||||
|
||||
Replacing something as fundamental as libc on a LIVE running system is
|
||||
safe under this model specifically because of POSIX unlink semantics:
|
||||
a process that already has a shared object open keeps working off the
|
||||
old inode even after the symlink swap happens. Only newly-spawned
|
||||
processes after the swap see the new version. This falls directly out
|
||||
of the atomic-rename commit design already in place — nothing extra
|
||||
was added to make it work.
|
||||
|
||||
|
||||
+------------------------------------+
|
||||
| 6. Open items |
|
||||
+------------------------------------+
|
||||
|
||||
- grootstrap has not been rebuilt against this design yet. The
|
||||
simplification this design enables: once gpkg/gstore themselves
|
||||
exist on the target (still a bootstrapping problem that needs
|
||||
solving separately), EVERY package — base included — can go
|
||||
through the same `gpkg -s -l` path. There is no longer a need for
|
||||
a separate raw-extraction special-case for base packages
|
||||
specifically; the repo alone routes them to /usr correctly.
|
||||
|
||||
- extra vs community currently behave identically (see section 2).
|
||||
Whether they should diverge, and how, is undecided.
|
||||
|
||||
- The bootstrapping-a-bootstrapper problem (getting gpkg/gstore onto
|
||||
a target filesystem before there's a working package manager to
|
||||
install them with) is still unsolved and orthogonal to everything
|
||||
in this document.
|
||||
Reference in New Issue
Block a user