Forest OS

A free and open source operating system, written from scratch. A Bluethefox project, hosted by Enclica.

← Back to Wiki

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, partial MAJOR / MAJOR.MINOR forms accepted.
  • Missing file: step_detect_install() defaults to 0.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
DirectiveMeaningStored as
version: X.Y.ZTarget 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 PATHDelete this path from the installed system.action = 1
keep PATHExplicit 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) counts add / remove / keep for 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):

PathConstantRole
/media/forest-update/manifest.txtUPDATE_SOURCE_DIR "/manifest.txt"Update USB / mounted media. Payload files live alongside it, mirroring destination paths.
/tmp/forupdate-manifest.txtMANIFEST_FILEPre-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 /etc files are best-effort (missing source is ignored).
  • The directory is created with mkdir -p semantics (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:

ActionWhat happensOutput
addmkdir 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)
removeremove(path)[REMOVED], or [NOT FOUND] if already absent (neutral)
keepNo-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-version keeps 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, then step_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 missing forest-kernel in the backup as fatal; a missing forest-initrd is 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's add/remove lines to reconcile by hand.

8. Error codes

Return codes (update.h:54-64):

CodeNameWhenWhat to do
0UPDATE_OKSuccess (or reboot declined after success).Reboot to load the new kernel.
1UPDATE_ERR_NOT_ROOTgetuid() != 0.Re-run as sudo forupdate.
2UPDATE_ERR_NO_INSTALLNo /boot install markers.Install first with forinstall.
3UPDATE_ERR_UP_TO_DATECurrent == manifest target. Exits before confirm/backup.Nothing to do.
4UPDATE_ERR_MANIFESTNo source, or manifest_load() failed.Mount media at /media/forest-update, or stage /tmp/forupdate-manifest.txt.
5UPDATE_ERR_DOWNLOADDefined, never returned (no network).N/A — missing payloads surface as [SKIP].
6UPDATE_ERR_INTEGRITYDefined, never returned (checksums parsed, not enforced).N/A.
7UPDATE_ERR_BACKUPbackup_create() failed.Check /boot space/permissions, re-run; no files modified.
8UPDATE_ERR_INSTALL≥1 [FAIL] during apply. Version file not rewritten.Fix each [FAIL] cause, re-run.
9UPDATE_ERR_CANCELLEDDeclined at the confirm prompt.No changes made; re-run when ready.

Quick triage:

MessageCauseFix
must be run as rootuid ≠ 0sudo forupdate
No Installation Foundno /boot install markersrun forinstall first
No update source foundneither manifest path existsmount media at /media/forest-update or stage /tmp/forupdate-manifest.txt
Failed to load update manifestcorrupt manifestre-copy media; check version: line
Already Up to Dateversions 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

  • Installerforinstall + forupdate internals, offline guarantees
  • System Toolsforupdate flow reference
  • Post-Install — users, networking, updates setup
  • Backup — what /boot/backup does not cover

← Back to Wiki