178 lines
7.7 KiB
Plaintext
178 lines
7.7 KiB
Plaintext
+------------------------------------+
|
|
| 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.
|