jamesstringer90 / jamesstringer90/appsandbox
A second snapshot captures the base disk instead of the VM's current state, and moves the VM off its branch
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):
- Shut the VM down. Take snapshot A. The VM moves to A's "Default Branch", which is correct.
- Start the VM, create
after-A.txton the desktop, shut it down. - Take snapshot B. Start the VM.
- Expected:
after-A.txtis there. B was taken after it was created. - Actual:
after-A.txtis 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_listhands outbase_vhdxas 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_takeas "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.datas an optionalParent=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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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