Updates — Updating with forupdate (Offline-Capable)
forupdate (userspace/forupdate/, v0.1.0) upgrades an installed Forest OS system to a new version. It is interactive, runs on the installed system (not the live CD), and is fully offline: it reads a manifest plus file payloads from local media. No network is used at any point.
sudo forupdate # interactive wizard, no arguments
10-step flow (main.c:429-490):
banner → root check → detect install → load manifest → show diff
→ up-to-date check → confirm → backup → apply → finalize → reboot prompt
Cancelling at the confirm step makes no changes. Failing before the finalize step leaves /etc/forest-version untouched, so the update is retryable.
1. Prerequisites
1.1 Must run as root
forupdate aborts unless getuid() == 0 (step_check_root, main.c:71-84):
Error: forupdate must be run as root.
Please run: sudo forupdate
Exit code 1 (UPDATE_ERR_NOT_ROOT). Re-run with sudo.
1.2 An installed system must exist
Detection (detect_forest_install, main.c:13-24) stat()s for any of:
/boot/forest-kernel/boot/forest-initrd/boot/foreb-stage1
If none exists, the wizard prints No Installation Found and tells you to run forinstall first. Exit code 2 (UPDATE_ERR_NO_INSTALL).
2. Version check: /etc/forest-version vs manifest
Current version
Read from FOREST_VERSION_FILE = /etc/forest-version (version.c:117-119). The file holds a plain string:
1.2.0
- Parsed by
version_parse()(version.c:8-43):MAJOR.MINOR.PATCH, leading whitespace tolerated, partialMAJOR/MAJOR.MINORforms accepted. - Missing file:
step_detect_install()defaults to0.0.1(main.c:101-106). A corrupt/unparseable file behaves the same way on the next comparison. - Compared with
version_compare(): first differing field of major → minor → patch decides (<0 / 0 / >0,version.c:93-98).
Target version
Comes from the manifest's version: line. The wizard shows (step_show_comparison, main.c:157-187):
Current version : v1.1.0
Available : v1.2.0
Files to update: N files
Files to remove: M files
Files unchanged: K files
Changelog:
...
Up-to-date short-circuit
If version_compare(current, target) == 0 the wizard prints Already Up to Date and exits before confirmation and backup (main.c:190-206). Exit code 3 (UPDATE_ERR_UP_TO_DATE). Nothing is backed up, copied, or rewritten.
The version file is only rewritten on success, in the finalize step (step_update_version, main.c:377-398). If apply reports any error, finalize never runs and /etc/forest-version still holds the old version.
3. Manifest format
Manifest parser: manifest.c, limits in update.h:19-43:
- Max 256 entries (
entries[256]). - Max 4096-byte changelog buffer.
- One entry per line.
#= comment, blank lines skipped.
# Forest OS update manifest
version: 1.2.0
changelog: Fix framebuffer flicker on VBE 0x118.
changelog: Remove legacy old-tool, keep hostname config.
add /boot/forest-kernel sha256:abc123 1048576
add /usr/bin/forest-shell sha256:def456 524288
remove /usr/bin/old-tool
keep /etc/forest-hostname
| Directive | Meaning | Stored as |
|---|---|---|
version: X.Y.Z | Target version for this update. Parsed by version_parse(). | Manifest.target_version |
changelog: <text> | Starts/continues the changelog block. Every line after the first changelog: line is appended verbatim until a version: line resets it. Printed verbatim pre-confirm. | Manifest.changelog[4096] |
add PATH [CHECKSUM] [SIZE] | Copy/update this path from update media. | action = 0 |
remove PATH | Delete this path from the installed system. | action = 1 |
keep PATH | Explicit no-op, counted as unchanged. | action = 2 |
ACTION PATH [CHECKSUM] [SIZE]— checksum and size are parsed and stored (manifest.c:133-146) but not cryptographically verified on apply. Verification today = per-file copy success.- Unknown action words default to
add(parse_action,manifest.c:44-50). manifest_compare()(manifest.c:159-214) countsadd / remove / keepfor the pre-confirm summary. Return value =adds + removes.
4. Offline path: no network, files from USB /media
forupdate never opens a socket. "Download" is just "read from local media". The two accepted sources (update.h:51-52, main.c:27-47, main.c:133-139):
| Path | Constant | Role |
|---|---|---|
/media/forest-update/manifest.txt | UPDATE_SOURCE_DIR "/manifest.txt" | Update USB / mounted media. Payload files live alongside it, mirroring destination paths. |
/tmp/forupdate-manifest.txt | MANIFEST_FILE | Pre-staged manifest (e.g. copied by hand). Checked first; used if present. |
check_update_source() requires at least one to exist. If neither exists the wizard shows No update source found and exits with code 4 (UPDATE_ERR_MANIFEST). A present-but-unparseable manifest shows Failed to load update manifest / media may be corrupted, also code 4.
Payload layout on the media
For each add /dest/path, the source is:
UPDATE_SOURCE_DIR + /dest/path-minus-leading-slash
Example: manifest line add /boot/forest-kernel reads from:
/media/forest-update/boot/forest-kernel
Only add entries read payloads; remove/keep need no source file.
Typical USB update
# 1. Insert the update USB and mount it where forupdate expects it
mkdir -p /media/forest-update
mount -t vfat /dev/sdb1 /media/forest-update
# 2. Verify the source tree
ls /media/forest-update/manifest.txt
cat /media/forest-update/manifest.txt
# 3. Run the updater (as root)
sudo forupdate
# 4. Afterwards
umount /media/forest-update
cp /media/forest-update/manifest.txt /tmp/forupdate-manifest.txt
sudo forupdate
/tmp is preferred when both exist, so an explicit staged copy wins over the USB copy. Missing per-file payloads on the media are not fatal: that entry prints [SKIP: not on local media] (yellow) and is a network-update placeholder, not counted as an error. Unwritable destinations are fatal for that run.
Related codes UPDATE_ERR_DOWNLOAD (5) and UPDATE_ERR_INTEGRITY (6) are defined in update.h but never returned by this offline build — there is nothing to download and no hash enforcement.
5. Backup: /boot/backup/vX.Y.Z, keep 3
Before any file is touched, step_backup() calls backup_create(current) (backup.c:59-122). The backup captures the pre-update version.
Layout
/boot/backup/v1.1.0/
forest-kernel # copy of /boot/forest-kernel (required)
forest-initrd # copy of /boot/forest-initrd (best-effort)
forest-version # copy of /etc/forest-version (best-effort)
forest-hostname # copy of /etc/forest-hostname (best-effort)
forest-network # copy of /etc/forest-network (best-effort)
- Directory
BOOT_BACKUP_DIR "/v" + version(e.g./boot/backup/v1.1.0/). - Kernel copy failure aborts the update with
UPDATE_ERR_BACKUP (7). Initrd and the three/etcfiles are best-effort (missing source is ignored). - The directory is created with
mkdir -psemantics (mkdirs).
Retention: keep 3
Finalize calls backup_cleanup_old(3) (backup.c:178-231): list /boot/backup/, sort names alphabetically, delete the oldest beyond the newest 3. Failed updates that abort before finalize do not prune, so a failed target never evicts your last good backups.
6. Apply + verify
step_apply_update() (main.c:263-374) walks every manifest entry with a progress bar and per-file status:
| Action | What happens | Output |
|---|---|---|
add | mkdir destination dir (single level, 0755), copy 4 KiB-chunked from media tree with O_WRONLY|O_CREAT|O_TRUNC, 0644 | [OK] on success, [FAIL: cannot write] if dest unwritable, [FAIL: source missing] if open fails, [SKIP: not on local media] if payload absent (not an error) |
remove | remove(path) | [REMOVED], or [NOT FOUND] if already absent (neutral) |
keep | No-op | (no line beyond Processing:) |
Applied: N files
Errors: M files # only if M > 0
- Any counted error returns
UPDATE_ERR_INSTALL (8)and skips finalize:/etc/forest-versionkeeps the old version, so re-running after fixing the[FAIL]cause retries cleanly. - On zero errors,
step_update_version()writes the new version, then prunes old backups, thenstep_success()prompts for reboot (sync()+reboot()). Reboot to load a replaced kernel.
Verify after update
cat /etc/forest-version
ls /boot/backup/ # new v<old> entry present, max 3 kept
ls -l /boot/forest-kernel /boot/forest-initrd
# re-run shows up-to-date:
sudo forupdate # → Already Up to Date, exit 3
If the run printed Errors: N files, fix each [FAIL] line (usually permissions or a full filesystem), confirm /etc/forest-version is still the old version, and re-run. [SKIP] lines need no fix unless you expected that payload on the media.
7. Rollback: copy back + sync
Rollback is manual copy-back from the pre-update backup (backup_restore(), backup.c:144-176, copies kernel then initrd). Restore the matching forest-version by hand so the next forupdate compares against the rolled-back version.
# 1. Pick the backup (pre-update version)
ls /boot/backup/
# 2. Copy the kernel back (required) + initrd (if the backup has one)
cp /boot/backup/v1.1.0/forest-kernel /boot/forest-kernel
cp /boot/backup/v1.1.0/forest-initrd /boot/forest-initrd
# 3. Roll the version file back too (otherwise forupdate thinks
# you are still on the failed target)
cp /boot/backup/v1.1.0/forest-version /etc/forest-version
# 4. Flush and reboot into the restored system
sync
reboot
backup_restore()treats a missingforest-kernelin the backup as fatal; a missingforest-initrdis ignored.- Optional configs (
forest-hostname,forest-network) restore the same way:cp /boot/backup/v1.1.0/forest-hostname /etc/forest-hostname cp /boot/backup/v1.1.0/forest-network /etc/forest-network sync removed files are not resurrected by rollback — only kernel/initrd (+ version/configs you copy by hand) revert. Files added by the failed update stay until you delete them; check the manifest'sadd/removelines to reconcile by hand.
8. Error codes
Return codes (update.h:54-64):
| Code | Name | When | What to do |
|---|---|---|---|
0 | UPDATE_OK | Success (or reboot declined after success). | Reboot to load the new kernel. |
1 | UPDATE_ERR_NOT_ROOT | getuid() != 0. | Re-run as sudo forupdate. |
2 | UPDATE_ERR_NO_INSTALL | No /boot install markers. | Install first with forinstall. |
3 | UPDATE_ERR_UP_TO_DATE | Current == manifest target. Exits before confirm/backup. | Nothing to do. |
4 | UPDATE_ERR_MANIFEST | No source, or manifest_load() failed. | Mount media at /media/forest-update, or stage /tmp/forupdate-manifest.txt. |
5 | UPDATE_ERR_DOWNLOAD | Defined, never returned (no network). | N/A — missing payloads surface as [SKIP]. |
6 | UPDATE_ERR_INTEGRITY | Defined, never returned (checksums parsed, not enforced). | N/A. |
7 | UPDATE_ERR_BACKUP | backup_create() failed. | Check /boot space/permissions, re-run; no files modified. |
8 | UPDATE_ERR_INSTALL | ≥1 [FAIL] during apply. Version file not rewritten. | Fix each [FAIL] cause, re-run. |
9 | UPDATE_ERR_CANCELLED | Declined at the confirm prompt. | No changes made; re-run when ready. |
Quick triage:
| Message | Cause | Fix |
|---|---|---|
must be run as root | uid ≠ 0 | sudo forupdate |
No Installation Found | no /boot install markers | run forinstall first |
No update source found | neither manifest path exists | mount media at /media/forest-update or stage /tmp/forupdate-manifest.txt |
Failed to load update manifest | corrupt manifest | re-copy media; check version: line |
Already Up to Date | versions equal (exit 3) | no action |
Backup FAILED! | kernel copy failed (exit 7) | free space / fix /boot perms |
Errors: N files | [FAIL] lines (exit 8) | fix per-file cause, re-run |
Further reading
- Installer —
forinstall+forupdateinternals, offline guarantees - System Tools —
forupdateflow reference - Post-Install — users, networking, updates setup
- Backup — what
/boot/backupdoes not cover