| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| zpaqfranzxp.exe | < 8 hours ago | 6.9 MB | |
| zpaqfranz-open.exe | < 8 hours ago | 5.8 MB | |
| zpaqfranzhw.exe | < 8 hours ago | 6.6 MB | |
| zpaqfranz-full.exe | < 8 hours ago | 12.4 MB | |
| zpaqfranz32.exe | < 8 hours ago | 7.1 MB | |
| zpaqfranz.exe | < 8 hours ago | 6.7 MB | |
| zpaqfranz.cpp | < 8 hours ago | 8.8 MB | |
| README.md | < 8 hours ago | 56.9 kB | |
| Windows 32_64 bit executables and source code and first WinPE source code.tar.gz | < 8 hours ago | 3.2 MB | |
| Windows 32_64 bit executables and source code and first WinPE source code.zip | < 8 hours ago | 3.2 MB | |
| Totals: 10 Items | 60.8 MB | 0 | |
zpaqfranz 65.9
DO NOT USE IN PRODUCTION: IT IS STILL TO BE STABILIZED!
This build (65.9z) is released on purpose as not for production. What
is new about the disk of Windows (ac, xc, their screens, the WinPE) is
young, and it is put out now to get feedback and to debug it along the way
with who uses it, instead of doing it alone. Try it on machines and disks
that can be lost, and tell what happens. Do not make it your only backup!
With this release, I think I've implemented everything I wanted to do; now it's time to focus on stabilization and bug fixes. Please open issues on GitHub so I can fix them and improve the program. The game-changer was finally setting up a cluster of VMs to automate testing, but they require a lot of RAM and disk space, so the more help I get, the better the program will evolve.
This is the release of the PC that comes back. One command saves the disk of Windows so that it can be put back and boots, one command puts it back, and there is a first WinPE to do it from:
ac archive: the disk of Windows, for a restore that boots.C:by its used clusters, everything else that is needed to start (the hidden partitions, the partition table, the space between the partitions) byte by byte, and a map of the disk with the hashes of every piece. MBR and GPT.-all: the data partitions too.xc archive: the restore. Alone it is the plan, nothing is written;-to 2: -forcewrites a physical disk,-to x.vmdkorx.vhdxa virtual one. Every piece is checked before and while it is written. A restored Windows 10 was started: it boots.- A WinPE to restore from (first version): an ISO with zpaqfranz in it, to start a PC that has no Windows any more.
-gui:acandxcon a screen. Which partitions, which disk, with the arrows and the space bar.- A command without its archive opens a chooser:
zpaqfranz tand ENTER. Pick the archive on a screen, then the switches, then it runs. -ntfs: the list of the files read from the$MFT, not folder by folder. The same list, 7 times faster on aC:, 12 times with a shadow copy. By itself when the sources are whole NTFS drives.- Images over 2 TB: they restore to a
.vhdx(new), to a.vmdkin more files as VMware makes it, with a GPT inside. d: what to look for (d u:\*.mp4), filters, a new output in groups, hard links told apart, xxh3 for real.- The shadow copy of an image: by itself for
C:only. And an image that asked for it and could not have it is an error, not a raw image. - Fixes that matter: an image on a disk that fills up said "all OK"; an
elevated run lost the
""and the paths with a space; the old-ntfslost hard links and folders.
This document has three parts:
- Part one: what is new, in short, for everybody
- Part two: the same things in more detail, for power users
- Part three: "lo spiegone", how it works inside, and why, for developers
Part one, what is new, for the user
ac: the disk of Windowsxc: the restore- A WinPE to restore from
-gui: choose on a screen- A command without its archive
-ntfs: the list from the <[inline_block>1](#6- Images over 2 TB
d: what to look for, and a new output- The shadow copy of an image
- Smaller things
1. ac: the disk of Windows
zpaqfranz ac z:\pc.zpaq (the disk of Windows)
zpaqfranz ac "" (dry run: what would be taken)
zpaqfranz ac z:\pc.zpaq -all (every partition of that disk)
zpaqfranz ac z:\pc.zpaq -all -not m: (every partition but M:)
zpaqfranz ac z:\pc.zpaq -gui (choose on a screen)
An image of C: does not boot: Windows starts from partitions that have
no letter, and from a partition table. ac takes the disk C: is on:
C:asa archive c: -imagedoes: the used clusters, with a shadow copy;- every partition without a letter (boot, EFI, recovery...) byte by byte;
- the start of the disk with its partition table, the space between the partitions, the end of the disk;
disk_C.txt, a text file: the map of the disk, how each piece was read, its size and its hashes.
All in one version of the archive, one for each run. Before anything is read a table says what will be taken, and what will not:
# from size read saved what
00 0.00 B 1.00 MB 1.00 MB raw (disk_C_00.raw) the start of the disk
01 1.00 MB 50.00 MB 50.00 MB raw (disk_C_01.raw) partition 1, BOOT, NTFS "Riservato per il sistema", active
02 51.00 MB 43.43 GB 11.08 GB used clusters (image_C.fhd) partition 2, WINDOWS, C:, NTFS
03 43.48 GB 1.30 MB 1.30 MB raw (disk_C_03.raw) between the partitions
04 43.48 GB 10.00 GB - NOT SAVED: data (mounted) partition 4, D:, NTFS "DATI"
05 53.48 GB 1.00 MB 1.00 MB raw (disk_C_05.raw) between the partitions
06 53.48 GB 5.00 GB - NOT SAVED: data (mounted) partition 5, E:, NTFS "ALTRI"
...
10 59.48 GB 534.00 MB 534.00 MB raw (disk_C_10.raw) partition 3, recovery, NTFS
11 60.00 GB 2.00 MB 2.00 MB raw (disk_C_11.raw) the end of the disk
- The partitions with a letter (
D:,E:...) are not taken: they are data, and the table says so. Their place in the partition table stays. -alltakes them too: NTFS, FAT and exFAT by their used clusters, anything else (not formatted, encrypted by something else) whole. Not the partition the archive is on.-not X:leaves one out.ac ""reads everything and writes nothing: how much it is, how long it takes.- An archive of
acis an image archive like the others:imagegivesC:back alone, as a.vhdor on a partition, as before. - A BitLocker volume that is unlocked is saved in clear (it is told);
-keyencrypts the archive. - Refused, for now: boot files on another disk than the one of
C:, dynamic disks.
On the test machine (Windows 10, a disk of 60 GB, 11 GB to read): 36 seconds, an archive of 5.3 GB; a second run right after adds 11 MB.
2. xc: the restore
zpaqfranz xc z:\pc.zpaq (the plan: nothing is written)
zpaqfranz xc z:\pc.zpaq -gui (choose on a screen what, and where)
zpaqfranz xc z:\pc.zpaq -to 2: -force (the physical disk 2: OVERWRITTEN)
zpaqfranz xc z:\pc.zpaq -to 2: -force -verify
zpaqfranz xc z:\pc.zpaq -to d:\pc.vmdk (a disk for a virtual machine)
zpaqfranz xc z:\pc.zpaq -to d:\pc.vhdx -until 3
zpaqfranz drives (the disks of this PC, with their numbers)
xc archive alone is the plan: which backup it is, its pieces, where each
one goes.
xc: the backup of DESKTOP-09SNTPI (Windows 10 Pro), taken 2026-10-06 13:07:42 UTC by zpaqfranz v65.9o: version 1 of 1
xc: disk of 60.00 GB (VMware Virtual SATA Hard Drive, SATA), MBR B28ABB93, sectors of 512 bytes, boot by BIOS
# from size to write restored from what
00 0.00 B 1.00 MB 1.00 MB raw (disk_C_00.raw) the start of the disk
01 1.00 MB 50.00 MB 50.00 MB the whole partition (disk_C_01.raw) partition 1, BOOT, NTFS "Riservato per il sistema"
02 51.00 MB 43.43 GB 11.08 GB its used clusters (image_C.fhd) partition 2, WINDOWS, C:, NTFS
...
- Every piece goes back where it was, with the partition table of the backup: the same disk signature (or the same GUIDs). Nothing to repair to make it boot, no external program.
- The pieces must be the ones the backup wrote: size, CRC-32 and hash are checked in the index of the archive before anything is written, and again on the data while it is written.
-to N: -forcewrites the physical disk N.-forceis the consent, then a captcha that names the disk (killdrive2for disk 2) must be typed. The disk must be at least as big as the one of the backup, with sectors of the same size. Never the disk of the running Windows, nor the one the archive is on.- On the disk of the backup itself what was not in the backup (the data partitions) is not touched. On another disk those partitions come back empty, to be formatted.
-to x.vmdk(VMware, VirtualBox, QEMU/Proxmox),-to x.vhdx(Windows mounts it, Hyper-V starts it),-to x.raw: the whole disk in a file.-verifyreads back what was written.- While it writes, a live map of what goes on the disk; at the end one line for each piece: "as the backup wrote".
A disk of Windows 10 (MBR, BIOS) restored this way on an empty disk was
then used to start the machine: up in 50 seconds, chkdsk C: clean, the
recovery environment in its place.
On the test machine a restore of 11.8 GB on a disk takes 33 seconds.
3. A WinPE to restore from
DO NOT USE IN PRODUCTION: IT IS STILL TO BE STABILIZED
A restore on the disk Windows runs from cannot be made from that Windows. So here is the first version of a WinPE with zpaqfranz inside: a small Windows that starts from a USB stick or a CD, on a PC with an empty disk, a broken Windows, or none.
https://sourceforge.net/projects/zpaqfranz/files/winpe/beta65_9z/
WinPE_zpaqfranz_65_9z.iso
- About 430 MB.
- What is in it:
zpaqfranz.exe, and a script that sets the English keyboard (if asked). Nothing else: all the rest is Microsoft's WinPE as it is. -
Start the PC (or the virtual machine) from it, and at its prompt:
zpaqfranz drives (which disk is which) zpaqfranz xc e:\pc.zpaq (the plain) zpaqfranz xc e:\pc.zpaq -gui (choose on a screen, then F8) zpaqfranz xc e:\pc.zpaq -to 0: -force (or straight on disk 0) zpaqfranz xc \thenas\theshare\thebackup.zpaq -to 0:
The archive must be somewhere WinPE can read: a USB disk, a second
disk. image is there too, for one partition alone, or on a supported network (DHCP)
It is a beginning, and it will grow with the rest. For now it is there: who tries it and tells how it went (which PC, BIOS or UEFI, which disk, what the screen said) is doing the debug that one person alone cannot do in a short time.
4. -gui: choose on a screen
zpaqfranz ac z:\pc.zpaq -gui
>[ ] ac: disk 0, 60.00 GB (VMware Virtual SATA Hard Drive, SATA), boot by BIOS
# size used type what
[+] 02 43.43 GB 22.91 GB NTFS WINDOWS (C:, NTFS)
[ ] 04 10.00 GB 97.76 MB NTFS DATA (D:, NTFS "DATI")
[ ] 06 5.00 GB 85.20 MB NTFS DATA (E:, NTFS "ALTRI")
TO READ 23.48 GB 9 pieces
NOT SAVED 15.00 GB not in the backup: D: E:
ESC/q=quit SPACE=select a=all v=system ?=help F8/g=START
[+] is what is always taken (Windows, and what it boots with), [X] and
[ ] every other partition: arrows and space. The first line is the whole
disk. The line in cyan is how much will be read. F8 (or g) starts,
ESC leaves and nothing was read.
zpaqfranz xc z:\pc.zpaq -gui
xc: the backup of DESKTOP-09SNTPI (Windows 10 Pro): 60.00 GB, MBR, BIOS
taken 2026-10-07 08:41:18 UTC by zpaqfranz v65.9v, version 1 of 1
# size to write type what
[+] 02 43.43 GB 19.84 GB NTFS WINDOWS (C:, NTFS)
>[X] 04 10.00 GB 100.00 MB NTFS DATA (D:, NTFS "DATI")
TO WRITE 20.51 GB 10 pieces
NOT WRITTEN 5.00 GB not restored: E:
######################################################################
######################################################################
[-] 00 SATA, Fixed VMware Virtual SATA Hard D 60.00 GB C:, D: "DATI", E: "A
[-] 01 SATA, Fixed VMware Virtual SATA Hard D 50.00 GB Z: "ARCHIVI"
[ ] 02 SATA, Fixed VMware Virtual SATA Hard D 70.00 GB BACKUP DISK: 5 parti
(As it is on a console of 80 columns: the long lines are cut.)
Above, the partitions of the backup; below, the disks of this PC: one
only. Green: a disk without partitions. Yellow: it has partitions, they
will be lost. Red, [-]: it cannot be (too small, the disk of Windows,
the one of the archive), and why. F8 asks to type the name of the disk
(killdrive2), then it writes. i asks for a file instead (.vhdx,
.vmdk, .raw, .img).
Only with -gui: ac and xc without it are what they were, for scripts
and scheduled tasks.
5. A command without its archive
zpaqfranz t
zpaqfranz x
zpaqfranz i
A command that wants an archive, typed without one on a console, used to print the help. Now it opens a chooser on the file system:
FILE 00000014|C:/zt/sc/
zpaqfranz t: ENTER on an archive (in cyan) takes it; SPACE marks more than one
>[ ] 00000001| 2026-10-07 12:37:12 <DIR> dati/
[ ] 00000006| 2026-10-07 12:37:12 6.499 bk_00000001.zpaq
[ ] 00000009| 2026-10-07 12:37:12 7.799 due.zpaq
[ ] 00000011| 2026-10-07 12:37:12 7.804 multi_0001.zpaq
ESC/q F1-F4=Sort all not inv search filter SPACE :line path ?help
The archives are in cyan, the folders in green. The keys are the ones of
the tui command. ENTER on an archive, and the switches of that command
come up:
zpaqfranz t <archive> -checksum
RUN -verify -ssd -paranoid -quick [-checksum] -collision -crc32
-all other: (none)
arrows=move SPACE/ENTER=on/off (on: reversed) ENTER on RUN=start ESC/q=back
ENTER on RUN, and the command runs: the line is shown as it would have been typed, to be copied in a script next time.
- What a command cannot do without is asked:
xasks where to extract (nothing: here). - A piece of a multipart archive is taken as the whole archive
(
multi_????.zpaq); a file of thebackupcommand too. - More archives marked with SPACE: one run each (
tandx), ast "name*"does. - The password, when there is one, is asked by zpaqfranz as always.
- For:
x xx e t l i v p pp w zip image crop trim password ls tui gui xc.
Not on a console (a script, a redirected output): the help, as before.
6. -ntfs: the list from the $MFT
zpaqfranz a z:\1.zpaq c:\ e:\ (by itself: whole NTFS drives, an administrator)
zpaqfranz a z:\1.zpaq c:\data -ntfs (asked for, on a folder)
zpaqfranz a z:\1.zpaq c:\ -nontfs (never: folder by folder, as always)
Before a backup, the list of the files. Windows gives it folder by
folder, and it costs for each folder. -ntfs reads the $MFT of the
volume, where NTFS keeps every name, in one go.
| the scan alone | folder by folder | -ntfs |
|---|---|---|
a C: (182,600 names, 34,300 folders) |
1.80 s | 0.25 s |
the same through a shadow copy (-vss) |
8.7 s | 0.69 s |
| 1,000,000 files in 101,000 folders, not in the cache of Windows | 26-30 s | 0.85 s |
| the same, already in the cache | 3.1 s | 0.85-1.15 s |
- The same list: the same names, the same sizes, dates and attributes (but for a file that is open and growing: see part two). That was the point, more than the speed.
- By itself when every source is a whole NTFS drive and an
administrator runs. A folder, a FAT drive, a share among the sources, no
administrator: the normal scan, and nothing is said.
-ntfsasks for it on a folder too;-nontfsnever. - The switch was already there, with other code: that one lost the hard links and the folders, and without an administrator it listed nothing. It is gone.
- Not for an image (
-imagedoes not list files).
7. Images over 2 TB
zpaqfranz image z:\big.zpaq g: -to d:\g.vhd (over 2040 GB: d:\g.vhdx by itself)
zpaqfranz image z:\c.zpaq c: -to d:\c.vhdx (a .vhdx, of any size)
zpaqfranz image z:\big.zpaq g: -to d:\g.vmdk (over 2032 GB: g.vmdk, g-s001.vmdk, g-s002.vmdk...)
The backup of a volume over 2 TB was already good. What was missing was
the way out: a .vhd ends at 2040 GB, one sparse .vmdk a little before.
.vhdx, new: of any size, mounted by Windows the same way as a.vhd. Over 2040 GB a.vhdbecomes a.vhdxby itself (withxtoo).- A sparse
.vmdkover 2032 GB is written in more files, as VMware Workstation makes it. - A partition over 2 TB does not fit in an MBR: the virtual disk gets a GPT.
- The free clusters as zeros in the image (the thin image of 65.8), and
f X: -test -force -zero -ntfs: over 2 TB too.
Tried on a volume of 4,000 GB with 2,300 GB used: the image in 1 hour and
34 minutes (417 MB/s), then -to x.vhd gave a .vhdx that Windows
mounted, chkdsk clean, the files the same.
8. d: what to look for, and a new output
zpaqfranz d u:\*.mp4 (only the .mp4, and below)
zpaqfranz d u:\ *.mp4 -minsize 100MB -zero (as find; the big ones; not the empty files)
zpaqfranz d c:\d0\ -verbose (the groups)
zpaqfranz d c:\d0\ -force (wet run: the duplicates are DELETED)
- What to look for:
d folder\*.mp4, ord folder\ *.mp4asfinddoes.-find,-minsize,-maxsize,-only,-not,-datefrom,-dateto.-norecursion: that folder only. -zeroleaves the empty files alone (by default they are all duplicates of each other).- The output: with
-verbosethe files in groups, the one that stays first (with its date), the ones that go marked====, then what the group gives back and its hash. The smallest saving first, the biggest last, where the eye is when it ends. The same total with and without-verbose. - Hard links (more names of the same file) are in yellow and are not counted: deleting one of them frees nothing.
- xxh3 is the default, as the help always said. It was SHA-1.
- Two files that could not be read, of the same size, were duplicates of each other. Not any more.
It is a dry run without -force.
9. The shadow copy of an image
zpaqfranz a z:\c.zpaq c: -image (C: a shadow copy, by itself)
zpaqfranz a z:\d.zpaq d: -image (D: the volume is locked)
zpaqfranz a z:\d.zpaq d: -image -vss (D: with a shadow copy)
- By itself for
C:only. Any other drive is locked while it is read; a shadow copy there is asked for, with-vss. The line of the image says which one it is: "with VSS", or "without VSS (by itself for C: only; -vss to have it)". - A drive that cannot be locked (something keeps a file open) is read as it is, with the warning of always and exit code 1.
-image -vsswhen the shadow copy cannot be made (no room for it on the volume, a USB disk) is an error now: nothing written, exit code 2. It became a raw image of the whole partition, without a shadow copy, with exit code 0, after a line that said "with VSS".
10. Smaller things
- An image on a destination that fills up (Windows): the whole source was read, and it ended with "(all OK)" and exit code 0, the archive cut short. Now it is an error, exit code 2, as it was for files.
- The elevated window lost its arguments. From a normal prompt
ac "", and any path with a space in it, got to the elevated run without the""and without the quotes:ac ""ended at once with "(all OK)". The command line now goes through as it was typed.gends with the exit code of its elevated run (it was always 0). writtenin the progress lines ofais what really went out: on a destination that is full it stops, and that is the truth. With""as the archive it shows what would have been written.i -verboseshows the backups ofacthat are in the archive: the last one with its table, with-allevery one. On every platform.tui: F1 to F4 sort by name, size, date and extension, the same key again reverses. (F4 to F6 were the reverse sorts.)- OpenBSD 7.9: the linker does not complain about
rand()any more. - It builds everywhere it did: Windows (64 and 32 bit), Linux,
FreeBSD, OpenBSD, macOS (Intel and Apple Silicon), Solaris, the ESXi
build (gcc 3.4.6), the eleven NAS binaries. Some
-Wallwarnings of the last releases are gone.
Part two, the details, for power users
ac: what is taken, and howxc: the rules- The WinPE, and what to tell
- The two screens, key by key
- The chooser: keys, and the table of the commands
-ntfs: when, and where it differs- Virtual disks: every combination, again
- What can change for a script
1. ac: what is taken, and how
The disk is not seen as "EFI, MSR, recovery": it is partitions, and everything that is outside them. The same rules for MBR and GPT.
| on the disk | ac |
ac -all |
|---|---|---|
the partition of Windows (C:) |
used clusters, shadow copy: image_C.fhd and its companions |
the same |
| the partition Windows boots from | raw, always | the same |
| a system type (EFI, MSR, recovery, OEM) | raw, always | the same |
| a partition with a letter, NTFS, FAT, exFAT | not taken: data | used clusters: image_X.fhd |
| a partition with a letter, anything else (not formatted, VeraCrypt, a locked BitLocker) | not taken | raw, the whole of it: image_X.raw |
| a partition without a letter, up to 1 GB | raw | raw |
| a partition without a letter, over 1 GB | not taken, and told | raw |
| the partition the archive is on | not taken | not taken |
| the start of the disk, its end | raw | raw |
| a gap between partitions, up to 16 MiB | raw | raw |
| a bigger gap (not allocated space) | its first and last MiB | the same |
- The raw pieces are
disk_C_00.raw,disk_C_01.raw...: the number is the one of the zone in the table, in the order of the disk. - With
-allevery image of a partition with a letter can be taken back alone:image z:\pc.zpaq d: -to x.vhd. (a c: -image -allis another thing, and it is still there: the whole disk, raw, in one file.) - The shadow copies:
C:always (-novss: none). With-allthe other partitions are locked, one after the other; one that cannot be locked is read as it is, with a warning and exit code 1.-vss: a shadow copy for every NTFS. Each one is consistent in itself, not with the others. - The partition Windows boots from is mounted, with files open in it: it is read as it is, and the map says so.
- BitLocker: its presence is written in the map. An unlocked volume
is read through Windows, so in clear; a locked one, with
-all, raw as it is on the disk. -not X:with-allleaves that partition out.-not c:is refused. (-notwith files, inside an image, still means what it meant.)- The last line of
acis the name, the size, the CRC-32 and the hash ofdisk_C.txt: the same thatl -checksumshows. It ends up in the report by e-mail by itself. x z:\pc.zpaq disk_C.txt -to z:\out\to read the map: it is a text.
Refused, with the reason: the boot files on another disk; the boot files on a data partition with a letter over 1 GB; a dynamic disk; a table of a logical drive (MBR) that would fall outside what is taken.
2. xc: the rules
-to |
what |
|---|---|
| nothing | the plan: nothing is written |
N: (a number) -force |
the physical disk N; then the captcha killdriveN |
x.vmdk |
one sparse file (-raw: a flat one) |
x.vhdx |
a dynamic .vhdx |
x.raw, any other extension |
the disk byte by byte, in a sparse file |
x.vhd |
refused (66132): x.vhdx |
a letter (G:) |
refused (66130): a whole disk comes back; one partition alone is for image |
Which backup. The last one. -until N for the one of version N; a
drive after the archive (xc arc c:) when the archive has the disks of
more machines.
The check before. Each piece named in the map must be in the index of
the archive, in that version, with that size, CRC-32, hash, and the same
list of fragments. An archive where an a c: -image was added after the
ac has an image_C.fhd that is not the one of the map: xc says which
pieces are not the ones, and which -until to use. The pieces and the
map of two different days are never mixed.
The check while. Size, CRC-32 and hash of every piece as it comes out of the archive. The small raw pieces (the partition table is always one of them) are kept in memory and written only when they are right. The serial number of the volume inside an image must be the one of the map.
The disk of the backup, or another one.
| the same disk | another disk | |
|---|---|---|
| it is, when | same signature (or GUID), every partition of the backup in its place | anything else |
| the partition table | never missing | wiped first, written last |
| a partition that was not in the backup | not touched, stays mounted | empty: its first MiB is zeros, to be formatted |
| a partition made after the backup | it will be gone, and it is told before the consent (66148) | - |
- A bigger disk is fine (on GPT Windows moves the second table to the end by itself). A smaller one is refused.
- Refused as a destination: the disk of the running Windows, the one it boots from, the one the archive is on, the one of zpaqfranz itself, a read-only disk.
- Inside an image, on a physical disk: what the file system counts as used but was not saved (pagefile, hiberfil, swapfile) is written as zeros. An old hibernation must not be found there.
- At the end, when another disk on line in this PC has the same signature (the original next to its copy), the restored disk is kept off line, and it is told (66194): put on line there it would get a new signature from Windows, and would not boot. Take one of the two away.
-verify: a physical disk and a raw file are read back, piece by piece.-nocaptchaskips the captcha, for scripts.-forceis also the consent to write over a destination file that is there.- An error while writing: 66180, then "RESTORE NOT DONE" (66182), exit code 2.
The live map shows what is written, in the order of the disk: green
when written, with -verify blue then green when read back. Redirected,
or with -nodashboard or -noeta: the lines of before.
3. The WinPE, and what to tell
- The ISO is a WinPE as Microsoft makes it: write it on a USB stick or attach it to a virtual machine, and boot from it.
- Inside WinPE there is no Windows running from the disk, so the disk the PC boots from can be the destination: that is what it is for.
- The numbers of the disks there may not be the ones Windows gave:
always
zpaqfranz drivesfirst. - The console of WinPE is 80 columns by 25 lines: the screens of
-guiwere made to fit 80 columns, and a list that is longer scrolls. - The keyboard is the one WinPE starts with (Italian, yep I am from Italy); the script in the ISO sets the English one, if asked.
What helps, when something goes wrong: the model of the PC, BIOS or UEFI,
MBR or GPT, the kind of disk (SATA, NVMe, a RAID controller that WinPE
does not see), the output of drives and of xc archive (the plan), and
the last lines on the screen.
I'll make a new github repository with the "source code" of the WinPE (in fact, a single .cmd) Please be patient, I have to ... work too 😄
4. The two screens, key by key
| key | ac -gui |
xc -gui |
|---|---|---|
| arrows, PgUp, PgDn, Home, End | move (the fixed lines are skipped) | the same |
SPACE, ENTER, +, x |
take / leave the partition; on the first line: all, or none | a partition: restore it or not; a disk: this one |
a |
every partition that can be taken | every partition of the backup |
v |
the system pieces too, with their true numbers (as -verbose) |
the same |
F8, g |
start | the captcha, typed on the screen; then start |
i |
- | to a file: name and path, .vhdx .vmdk .raw .img |
?, F1 |
help | help |
ESC, q |
leave: nothing was read, exit code 0 | leave: nothing was written, exit code 0 |
In the column type: the file system; RAW for a partition that can be
chosen but is read whole; --- for a system partition, always taken.
[-]inac: a partition that cannot be taken (the one the archive is on), with the reason.- A disk in
xc: green, no partitions; yellow, partitions that will be lost; magenta "BACKUP DISK", the disk of the backup itself (or a copy of it): its data partitions stay; red[-], it cannot be, and why. - From the screen no
-forceis needed: the consent is the name of the disk typed by hand. xc arc -to 2: -gui: disk 2 already chosen. A partition saved byac -alland left out here is as one that was not in the backup.i: a name without what comes before the extension is refused; a file that is there: "overwrite it? (y/n)"; a.vmdkis the sparse one.- Not a console: 66300, exit code 2.
5. The chooser: keys, and the table of the commands
| key | |
|---|---|
arrows (k j), PgUp PgDn (u d), Home End (h e) |
move |
| ENTER | a folder: in; an archive: take it (and the marked ones) |
| BACKSPACE | up; above the root, on Windows, the list of the drives |
| SPACE | mark; a all, n none, i the others |
| F1 F2 F3 F4 | by name, size, date, extension; the same key again: reversed |
s |
search; f filter (the folders stay); : to a line |
p |
type a path |
g, x |
go, with the marked ones only |
? |
help; ESC, q: back to the shell, exit code 0 |
Only .zpaq and .franzen (and the files that are pieces of them) can
be taken. A command for one archive refuses the marks.
| command | switches offered | asks |
|---|---|---|
x, xx |
-force -checksum -longpath -flat -utf -fix255 -zero -space -all |
-to (nothing: ./) |
e |
-force -longpath -utf |
|
t |
-verify -ssd -paranoid -quick -checksum -collision -crc32 -all |
|
l |
-checksum -summary -comment -utc -terse -attr -date -nodel -all |
|
i |
-verbose -stat -comment -all |
|
v |
-ssd -verbose -noeta |
|
p |
-verify -verbose -noeta |
|
pp |
-verify -paranoid -ssd -verbose -noeta |
|
w |
-ramdisk -frugal -ssd -test -verbose -checksum -verify -paranoid -longpath |
-to |
zip |
-all -deflate -force -space |
-to |
image |
-raw -image -force -space |
-to |
crop |
-kill -force |
-to |
trim |
-kill -verify |
-to |
xc |
-gui (on) -verify -verbose |
|
password, ls, tui, gui |
none: they start at once |
- A switch already on the command line (
zpaqfranz x -to z:\out -force) is on in the panel, and what it wants is not asked again. - The last item of the panel, "other", is a line typed by hand: its words before the first switch go right after the archive (the files to extract, the drive of an image), the rest at the end.
- The time on the last line of the run counts from after the choice.
- The chooser starts only when the command is one of the table, no archive follows it, and both the input and the output are a console.
6. -ntfs: when, and where it differs
| by itself | every source is a whole drive (c:\, c:\*), every one is a local NTFS, an administrator runs |
-ntfs |
on a folder too. No administrator: it says so (65900), the normal scan |
-nontfs |
never by itself |
-image |
no scan at all |
-ssd (the multithread scan) |
not through the $MFT |
with -vss |
the $MFT of the shadow copy |
When it started by itself and cannot go on (the volume cannot be opened),
the normal scan, told only with -verbose. One line says what was done:
how many names, in how many seconds, out of how many records.
Where it pays. The normal scan costs for each folder (30 to 60
microseconds when in the cache of Windows, about 280 when not), almost
nothing for each file. -ntfs costs the read of the $MFT (about half a
second for each million records, on that disk) and little more. So: a
whole volume, many folders, and above all a volume that is not in the
cache. A shadow copy is always a new volume for the cache: with -vss
the normal scan is always the slow one. One million files in 10,000
folders, already in the cache: even.
Where it differs, and it is not an error:
- A file that a program keeps open and makes grow is 0 bytes for the
normal scan until it is closed, and has its true size in the
$MFT. On a liveC:: some tens of names out of 180,000 (logs, traces). On a shadow copy: none. - What only Windows can tell is left to Windows: a folder that cannot be listed, paths of 240 characters and more, names that are not Win32, folders of a cloud (OneDrive, Dropbox). That subtree goes through the normal scan.
7. Virtual disks: every combination, again
| up to 2 TB | over | |
|---|---|---|
-to x.vhd |
a dynamic .vhd |
x.vhdx, by itself (over 2040 GB) |
-to x.vhdx |
a dynamic .vhdx |
the same |
-to x.vmdk |
one sparse file | x.vmdk (a text) and x-s001.vmdk, x-s002.vmdk... (over 2032 GB) |
-to x.vmdk -raw |
flat: x.vmdk and x-flat.vmdk |
the same |
-to folder, the x command |
image_X.vhd |
image_X.vhdx |
| the partition inside | our MBR, the partition at 1 MiB | a protective MBR and a GPT (a partition over 2 TB) |
| the free clusters in the image | zeros | zeros |
- A
.vhdx, as a.vhd, keeps whole blocks of 2 MB: on a volume with its free space in small pieces it is almost as big as the disk (8,086 MB for 3,056 MB used on the tortured test volume). A sparse.vmdk(grains of 64 KB) stays near the used data. - Sectors of 512 bytes only.
xcwrites the same kinds of virtual disks, of a whole disk:.vmdksparse or flat,.vhdx, raw.
The volume of 4,000 GB with 2,300 GB used, on the test machine:
| time | size | |
|---|---|---|
a img.zpaq G: -image -novss |
1 h 34 min (417 MB/s), 800 MB of RAM | 662 MB (the used space was one reserved file: zeros) |
image -to out.vhd => out.vhdx |
3 min | 923 MB |
image -to out.vmdk => 2 files |
2 min 30 s | 879 + 456 MB |
t |
13 min 30 s |
Mounted by Windows (GPT, NTFS of 4,000 GB) and by OSFMount, chkdsk
clean, the list of the files the same as on the volume.
8. What can change for a script
| before | now |
|---|---|
a x.zpaq d: -image: a shadow copy, by itself, on any NTFS |
by itself for C: only; elsewhere the volume is locked (-vss to have it) |
a ... -image -vss, no shadow copy possible: a raw image, exit code 0 |
an error (78178), exit code 2, no archive |
a ... -image on a destination that fills up (Windows): "all OK", 0 |
an error, 2 |
a x.zpaq c:\ e:\ as an administrator: folder by folder |
from the $MFT (-nontfs: as before) |
-ntfs without an administrator: nothing listed |
a warning, the normal scan |
-ntfs: hard links and folders not in the list |
the list of the normal scan |
| a file open and growing: 0 bytes in the list | its size, with -ntfs |
d folder: SHA-1 |
xxh3 |
d: the output |
groups, the total at the end; hard links not counted |
d: two unreadable files of the same size are duplicates |
they are not |
image -to x.vhd over 2040 GB |
x.vhdx |
image -to x.vmdk over 2 TB: refused |
more files |
f X: -test -force -zero -ntfs over 2 TB: refused |
done |
zpaqfranz t (no archive) on a console: the help |
the chooser. Not on a console: the help |
tui: F4, F5, F6 = the reverse sorts |
F4 = by extension; the same key reverses; F5 and F6 nothing |
an elevated run: "" and the quotes lost |
the command line as typed |
g: exit code 0 always |
the exit code of the elevated run |
written in the progress of a: counted before the write |
what really went out |
i -verbose |
the same, and the backups of ac at the end |
Part three, lo spiegone, for developers
- The disk as zones, and <[inline_block>0](#1
- More images in one run
xc: one pass of the engine for each piecefranzscelta, and the map of <[inline_block>1](#4franzscegli: one hook before the parserfranzntfs2: the$MFTin memoryfranzvhdx, the.vmdkin pieces, the GPT- Asking for the administrator, again
- How it was tested
- Fixes
- Tried and thrown away
- Known limits and open points
1. The disk as zones, and disk_C.txt
Windows gives the layout of a disk with one call, the same for MBR and
GPT, logical drives included (IOCTL_DISK_GET_DRIVE_LAYOUT_EX). ac
makes of it a list of zones that covers the disk from the first byte
to the last: a partition, or what is outside. Each zone gets a decision
(ac_decidi): an image, raw, not taken and why.
The partition Windows boots from is read in the registry
(HKLM\SYSTEM\Setup\SystemPartition), not guessed from its type: on MBR
it looks like a data partition. BitLocker is seen in the first sector as
the disk has it (-FVE-FS-); anything else under a volume, when the
first sector of the disk is not the one of the volume. No external
program is run (no bcdedit, no reagentc): what is not needed is not
asked.
Why it boots without repairing anything: on MBR the boot configuration finds its partition by the signature of the disk (4 bytes of sector 0) and an offset. The same sector 0, the same offsets: it starts. On GPT, the same GUIDs.
In the archive C: is exactly what a c: -image writes (image_C.fhd
and its companions), so every command that knows an image knows this one.
What is new has another prefix, disk_C_, because the names that begin
with image_ have a meaning by their length and by one letter.
The map is a text, franzdisk 1 on its first line:
franzdisk 1
# every piece named here is in the SAME version of the archive, with the size and the hashes written here.
# A key not known is to be ignored. Numbers are bytes, from the start of the disk.
[run]
program=zpaqfranz v65.9o (2026-10-04)
command=C:/zt/zf9o.exe ac Z:/tutto.zpaq -all -noeta
computer=DESKTOP-09SNTPI
windows=Windows 10 Pro
firmware=BIOS
started_utc=2026-10-06 13:07:42
seconds=30.3
archive_version=1
hash=XXHASH64B
...
[disk]
number=0
model=VMware Virtual SATA Hard Drive
bytes=64424509440
sector=512
style=MBR
signature=B28ABB93
zones=12
...
[zone 01]
kind=partition
start=1048576
bytes=52428800
mbr_type=07
active=yes
filesystem=NTFS
label=Riservato per il sistema
volume_serial=D446C64B
boot=yes
encrypted=no
saved=raw
file=disk_C_01.raw
layer=disk
note=its volume was mounted, not locked, while it was read
read_errors=0
...
then [files] (name, size, CRC-32, hash with its kind, the hash of the
list of the fragments) and [archive before] (the versions that were
there). It describes facts, it does not give orders: what to do is
decided by the restore, looking at the destination. As VFILE-info: keys
are only added.
It is written last in its version, as a file in memory, so it holds
hashes that zpaqfranz computed while reading: nothing is read twice. The
hasher of a file is closed when the index is written; ac closes it when
the file ends and keeps the result, and the index gets the same one.
The pieces of ac do not get the compression method of the companions of
an image (the one for the small metadata files): on 590 MB it took 41
seconds and 1.3 GB of RAM instead of 18 seconds and 27 MB.
2. More images in one run
add() was made for one image in a run: one reader, one drive letter,
one set of names, one live map. ac -all wants C:, D:, E: by their
used clusters in the same version.
The engine was not rewritten. The files of every image are queued in
order (an image, its companions, the next image..., the raw pieces, the
map last), and when the file in turn is the image of another partition
ac_cambia() puts a new reader in the place of the old one (the
destructor, then a placement new: the shadow copy of before is released),
sets the letter, the kind (used clusters or raw), the names, and the
bases the progress is counted from. The live map starts again from zero
for each image.
When the used clusters of a partition cannot be read after all, its
companions are skipped and image_X.raw takes the place of
image_X.fhd, with a warning.
Only with ac: the path of one image, the one of a c: -image, does not
go through any of this.
3. xc: one pass of the engine for each piece
xc does not have an engine of its own. The extraction to stdout
(extractstdout) is called once for each piece, in the order the
destination wants: a virtual disk is written from its start to its end
(its writer wants whole blocks of 2 MiB, in order: an assembler makes
them); a physical disk and a raw file get the piece with the partition
table last. The fixed cost is about 0.35 seconds a piece.
franzxc is the piece going through: it counts, computes CRC-32 and
hash, and sends the bytes to franzxcuscita (a physical disk, a sparse
raw file, a virtual disk through franzvdisk).
An image of the used clusters is a list of records (512 bytes and a block
of 2 MB), and a map says which block of the partition each record is. It
goes straight to its place on the disk: no .vhd in between.
On a physical disk:
- a volume is locked and dismounted before it is written; with a file kept open by another program the lock fails. That is why, on a disk that is not the one of the backup, the partition table is wiped first: the volumes are gone, in use or not;
- a disk off line can be written raw;
- after the last piece Windows is told to read the disk again; on a GPT disk bigger than the original it moves the second table to the end and writes the header again by itself.
In WinPE (the MiniNT key in the registry) the partition Windows boots
from is not looked for: there is no such thing there.
4. franzscelta, and the map of xc
franzscelta is a list with check boxes on the console API of Windows:
lines of text, boxes, fixed ones ([+]), forbidden ones ([-]),
one-of-many. It knows nothing of disks. ac_scegli and
xc_scegli fill it and read it back; a question (a file name, the
captcha) is asked on its last lines.
The numbers shown are the true numbers of the zones (02, 04, 06), never 1, 2, 3: they are the ones of the table, of the map, of the names in the archive.
After F8 nothing special happens: ac decides each partition again
with the same function as without the screen, with "chosen" or "not
chosen" in the place of -all and -not. The map says optional=yes
for a partition that was a choice, so that xc can offer it.
franzxcdash is the live map of xc: cells of what is written, in
the order of the disk, each with its own counters. Not the whole disk: a
disk of 2 TB with 20 GB to write would be a screen of nothing. The lines
of the pieces are kept and printed after the map.
5. franzscegli: one hook before the parser
A table, one line a command:
{"x", "-force -checksum -longpath -flat -utf -fix255 -zero -space -all", "-to", "Where to extract", "./", true},
{"t", "-verify -ssd -paranoid -quick -checksum -collision -crc32 -all", "", "", "", true},
{"xc", "-gui* -verify -verbose", "", "", "", false},
the command, the switches offered (*: on at the start), the switch to
ask a value for, the question, the value of an empty answer, and whether
more archives can be marked. A new command is a new line.
And one hook, in main, before the command line is read: when the
first word is in the table, nothing that is an archive follows, and input
and output are a console, the chooser builds a command line as it would
have been typed, shows it, and puts it in the place of argc and argv.
From there on it is the parser of always. The switches are not "set":
they are read, as if typed. Nothing else in the program knows that a
chooser exists.
More archives: their list is given to multisomething(), the loop that
already runs t "name*" one archive at a time, with its reset of the
state between one and the next.
The chooser is on the primitives of the tui command (ANSI, get_key),
so it is on every platform that has tui; franzscelta is Windows
only, as ac and xc are. get_key got the left and right arrows, and
F1 to F4 as ESC O P...S (xterm, the terminal of Windows): they were
read as an ESC.
6. franzntfs2: the $MFT in memory
One hook in scandir(). The folder gives its volume (its handle, the
final path, the device: through the link of -vss it is the shadow
copy); the pieces of the $MFT are asked to Windows
(FSCTL_GET_RETRIEVAL_POINTERS) and read raw, 4 MB at a time; the
records become a table of names with their parents. The volume is
flushed first (0.03 to 0.13 seconds on a C:).
"The same list" is a strong word, and it is kept by construction:
where only Windows knows what it would answer, Windows is asked. A
folder that cannot be listed (one probe for each different security id,
not for each folder), long paths, odd names, cloud folders: that subtree
goes back to the normal scandir(). A file with a visible reparse point
is asked one by one, with the same calls.
The attributes are the ones Windows gives, not the ones on the disk: some bits are only on the disk and are taken away; compressed, sparse and encrypted come from the header of the data, not from the standard information; the files compressed by Windows itself (WOF) are sparse and reparse on the disk and plain for a program.
Two things found on the way:
- The kind of a name (Win32, DOS, POSIX) cannot be a signal of "odd name": a Windows laid down from an image has the POSIX kind on every folder.
- A shadow copy has the same serial number as the live volume: a cache of the volumes goes by device, not by serial.
The names come out already in the order of the table of the files and are inserted with a hint: 0.9 microseconds a name instead of 1.95.
7. franzvhdx, the .vmdk in pieces, the GPT
franzvdisk is the base: something that takes blocks of a disk in order.
franzvmdk (one sparse file, pieces of 4,261,412,864 sectors as
Workstation makes them, flat) and franzvhdx (dynamic, blocks of 2 MB,
CRC-32C on the headers and on the region table, a block table of 64 bit)
are on it.
The image of the used clusters in the archive is not a .vhd: it is
records, a map, and a header and a footer kept to make the .vhd again.
So the backup of a volume over 2 TB was already right, and only the 32
bit block table of that header was in the way: over 2040 GB the .vhd is
not put together, the .vhdx is written instead.
The GPT is made when the virtual disk is written, not kept in the archive: a protective MBR, the GPT, its copy at the end, a disk 1 MiB longer.
8. Asking for the administrator, again
The command line for the elevated run was rebuilt from argv, the
arguments joined by spaces. An empty argument is nothing between two
spaces, and a path with a space becomes two arguments. ac "" reached
the elevated window as ac -pause -elevated: no archive, so no command,
so exit code 0. "(all OK)", in one second.
Now the line is the one Windows has (GetCommandLineW), with the name of
the program taken away: what was typed, quotes and all.
9. How it was tested
Two virtual machines for the disks: a Windows 10 on MBR and BIOS (a
reserved partition, C:, an extended one with two logical drives, the
recovery; a second disk as the destination), a Windows 11 on GPT and
UEFI. Windows code runs there over ssh, as an administrator.
ac: every raw piece taken out of the archive and compared with the disk, byte by byte; CRC-32 and hash computed again against the map; a second version; ana -imagemixed in;-sha256; BitLocker on a volume, unlocked and locked; FAT32 and exFAT; a partition without a file system; a drive kept open, with and without-vss; each image of-alltaken back alone, mounted, its files compared by hash. A real archive of the Windows 11 disk (GPT, 64 GB): 82 seconds, 32 GB.xc: a disk made to look like a disk of Windows, MBR and GPT, saved and restored to a raw file, a.vhdx, a flat.vmdkand a physical disk, each compared with the original zone by zone and as a whole; the same disk with a file kept open on a data partition; dirty destination; mixed versions;-key; every refusal. 69 checks. Then the real disk of Windows 10: restored on an empty disk, the machine started from it.- The screens: driven in a real console with scripted keys, the stream kept and drawn again to look at it; 80 columns.
-ntfs: 40 forms of the argument; a liveC:and its shadow copy; records of 4 KB, clusters of 64 KB and 2 MB, a compressed volume; an$MFTin 4,652 pieces; a standard user; a real backup both ways, the same 1,828 files.- Over 2 TB: a volume of 4,000 GB on a thin virtual disk, with files below and above the 2 TB.
- The battery of the images (138 checks) and
autotest -allafter each step. - Every platform, at the end: Windows (ucrt64; 32 bit, the open
build,
-DANCIENT,-DNOEMAIL,-DSFTP), Fedora 44 (gcc 16,-O1and-O3), FreeBSD 14.2 (clang 18), OpenBSD 7.9 (clang 19), macOS 12.7 (x86_64, the arm64 half, with macFUSE and FUSE-T), Solaris 11.4 (gcc 7.3), the ESXi build (gcc 3.4.6, static), the eleven NAS binaries (musl). No errors;autotestwhere the binary runs.
10. Fixes
- An image on a full destination said "all OK" (Windows): the check of what was written against what was expected was skipped for images.
-image -vsswithout a shadow copy fell back twice, to a raw image and then to a direct read, and ended well.- The elevated run without its quotes (see above).
galways ended with 0.- The old
-ntfs: no hard links, no folders, nothing at all without an administrator. d: the hash was SHA-1 whatever the help said (the default was set after the hasher had been chosen); two files not read had the same empty hash, so they were duplicates.writtenwas counted where the data was handed over, not where it was written; with""it read a counter nobody touched.- A message with six digits, an error printed as a normal line.
- An extraction to stdout asked Windows for the kind of the disk (a slow
question) every time: with
-verboseonly. acwrites "Windows 11" in its map (the registry still says 10).- OpenBSD:
arc4random_bufin the place of the lastrand()the linker saw. - Functions used on one platform only were compiled, and warned about, on the others.
11. Tried and thrown away
-ntfs2, a second switch next to the old -ntfs, with -ntfs2test
that ran both scans and compared them. It was the way to trust the new
code; once trusted, it took the name and the old code went.
A cache of the index of the volume, kept up with the USN journal: one
more thing that can be wrong on the day of the backup. The $MFT is read
every time: it is a second.
A threshold of time to pass from the normal scan to the $MFT on a
folder: by itself only on whole drives, where it always pays.
A shadow copy for the partition Windows boots from: on an EFI partition (FAT32) it cannot be made anyway. Read as it is, and written in the map.
Leaving out the space Windows reserves for the shadow copies (20 GB on a volume of 4 TB, in the image as used clusters): the old shadow copies would be left out with it.
A .vhd from xc: there is the .vhdx.
The whole disk in the map of xc: see above.
12. Known limits and open points
- The whole of
ac,xcand the WinPE is new: see the first lines of this document. - Started for real: a restored Windows 10 on MBR and BIOS, in a
virtual machine. VM 11: a restored GPT and UEFI disk (it was
compared with the original byte by byte, not booted); a restore on
different hardware; a virtual machine started from a
.vmdkor a.vhdxwritten byxc. - Not tested: disks with sectors of 4,096 bytes; a disk of Windows
over 2 TB; BitLocker on
C:; a Windows that is not onC:; a read error inside a raw piece; Ctrl+C in the middle of a restore; the elevated window with a real UAC question (I do not have UACsa at all); the three refusals ofac(no disk made that way at hand). - A restore on a smaller disk is refused. Linux:
acandxcare Windows only. xc, maybe, will become a Linux thing too (for restoring from USB stick. For now WinPE seems good enough) acwith-notof big folders inside the image costs a lot (ten minutes and 12 GB of RAM for six folders with 768,000 files), asa c: -image -notdoes.- With
-alleach partition is consistent in itself, not with the others. [archive before]in the map has the list of the versions, not the hashes of the pieces of the run before: they are not known without reading the index again.cloud ... -imagewith the local archive on a disk that fills up still says OK, and uploads an archive cut short.cloudwith files ends with an error, after a banner that says OK.- On a full destination the whole source is read anyway.
-ntfsdoes nothing on the multithread scan (-ssd); the$MFTstays in memory until the last source is done. Not tried: cluster of 512 bytes, more than 1.1 million files, real slow disks.d: a symbolic link and its target have the same content; when the link is the one that stays, it is left pointing to nothing.- The chooser: not tried on FreeBSD and macOS; the function keys as PuTTY
sends them; folders with thousands of files. Folders can be marked, and
nothing uses them yet: that is for
a. - The limits of 65.8 that are still there: sectors of 4,096 bytes in the virtual disks, the image of a *nix device to a raw file only.