jamesstringer90 / jamesstringer90/appsandbox

A second snapshot captures the base disk instead of the VM's current state, and moves the VM off its branch

Open
#152 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C
Stars
704
Forks
75
Avg merge
9m
Merged PRs (30d)
6

Description

Your issue

Version: 0.1.8, the official AppSandbox-0.1.8-win-x64.zip. The same code is in 0.1.4. Windows 11 host.

What happens. The first snapshot of a VM works as expected. Every snapshot after it saves the
disk as it was at the first snapshot, not the VM's current state, and then switches the VM onto
a new branch of that old state. The work done in between is not deleted. It stays on the previous
branch, and selecting that branch brings it back. But nothing says so, and the VM now boots without
it, so it looks as if the work is gone.

Steps (observed on a freshly installed Windows 11 VM, no GPU):

  1. Shut the VM down. Take snapshot A. The VM moves to A's "Default Branch", which is correct.
  2. Start the VM, create after-A.txt on the desktop, shut it down.
  3. Take snapshot B. Start the VM.
  • Expected: after-A.txt is there. B was taken after it was created.
  • Actual: after-A.txt is gone. The VM boots B's new branch, and B holds the disk as it was at A.

The differencing disks afterwards, with the parent read from each file's parent locator:

file size parent
snapshot_c3883d92-….vhdx (A) 4 MB disk.vhdx
branch_55e7a34a-….vhdx (A's Default Branch) 1.6 GB A — holds after-A.txt
snapshot_0c308386-….vhdx (B) 4 MB disk.vhdx
branch_ca059406-….vhdx (B's Default Branch) 0.9 GB B — the VM's disk now

Cause. snapshot_take in src/backend_win/snapshot.c always parents the new snapshot on the
base disk:

/* line 258 */
hr = vhdx_create_differencing(vhdx_path, tree->base_vhdx);

tree->base_vhdx is set once, on the first snapshot (lines 250–251), and nothing ever updates it to
the disk the VM is running on. The only other writes load it from tree.dat (line 177) and clear it
when the tree is empty (line 77). asb_snap_take in asb_core.c calls snapshot_take directly,
with no merge first. So from the second snapshot on, the parent is always the original base.

Is the flat layout intended? The code disagrees with itself here, which is why this is an issue
and not only a PR:

  • For: get_branch_list hands out base_vhdx as the parent (line 37), the header describes each
    snapshot as a "frozen fork off base", and the New Snapshot dialog says "Create a new snapshot of
    the base disk" (web/app.js, line 1560).
  • Against: the header documents snapshot_take as "freeze current state as a named fork of the
    base". Both halves can only be true for the first snapshot, which is the one that works.

If the flat layout is intended, the dialog and API should say plainly that a snapshot does not
contain the current state, and that taking one moves the VM off the branch it was on.

Why the obvious fix is not enough. Parenting B on the VM's current disk (instance->vhdx_path)
captures the right content, but it makes A's branch an ancestor of B. That branch must then never
be written again, yet it is still listed as a selectable branch of A, and booting it would corrupt B
and everything built on it.

Suggested fix: PR #"linked below". On a working branch, snapshot_take renames that branch into the new
snapshot, removes it from its old branch list, and continues on a fresh branch of it: the Hyper-V
checkpoint model. That is O(1), with no copying. Snapshots then form a real tree, so:

  • the parent is recorded in tree.dat as an optional Parent= line, and flat trees are read and
    written exactly as before;
  • deleting a snapshot that another was taken on is refused, with a message;
  • the snapshot window nests each snapshot under its parent, and the API reports parent.

Testing: built on 0.1.8 (only AppSandbox.exe, appsandbox_core.dll and web/app.js
replaced; the released drivers are unchanged) and run against the same VM after the stock build.
It loaded the stock tree unchanged. A snapshot taken from A's branch held after-A.txt, and so did
the VM afterwards. A second level (D on C) held both files. Deleting A was refused while C existed.
The tree survived a restart. A new branch from C had C's content and nothing later.

Why it matters to us. We use snapshots as rollback points for an automated build VM: take a
baseline after a warm build, and branch from it when a run goes wrong. Until this is resolved we can
only rely on the first snapshot.

🤖 Generated with Claude Code

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with snapshot_take in src/backend_win/snapshot.c and asb_snap_take in asb_core.c, then inspect tree.dat handling and the snapshot UI in web/app.js. Reproduce the A/B sequence and verify that snapshots preserve the current disk, maintain parent-child branches, refuse unsafe deletion, survive restart, and expose the tree correctly through the API.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, javascript
Domain
backend, operating-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.