You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit cb3fbab
Browse filesBrowse the repository at this point in the historyBrowse files
Harden the NTFS file table scanner, verify files before deleting, fix Recycle Bin and UI bugs
Scanner (Services/MftScanner.cs):
- Follow fragmented $MFT tables through the $ATTRIBUTE_LIST; verify the
pieces belong to $MFT and cover the valid length gap-free
- Read the table as one logical stream: exact reads, records that straddle
a read or an extent are carried over, a short table fails instead of
finishing
- Check each record's own number against its position; stop the scan on a
mismatch. Validate the update sequence array fully before writing it
back; count torn records instead of half-reading them
- Defer extension records until the table is read; merge only when the
base record's sequence number matches and its attribute list names them
- Bound every attribute, resident value, run list and reference; checked
arithmetic; fix the sign extension of 8-byte run offsets; validate the
volume geometry and the FSCTL reply; clamp out-of-range FILETIMEs
- Use the compressed size for sparse streams regardless of compression unit
- Give a hard-linked file's size to the first name that makes it into the
tree; apply exclusions to every name; dedupe names across namespaces
- Iterative tree build and sums (no depth cap), cancellation in every stage
- Real Win32 errors when the volume cannot be opened; HRESULT handling
- Test seam (geometry + reader delegate); MftImage fixture building NTFS
images in memory; 23 scanner tests
Deleting:
- Compare each file's size and write time with the scan before sending it
to the Recycle Bin; changed files are left alone and listed
- FOF_NORECURSEREPARSE: junctions and symlinks are removed as links
- Recycle Bin 0x7C: drop vanished paths, short-path over-long ones, retry
per item; all Trash messages localized (18 languages)
- Browser caches cleanup row states what it touches and asks to close the
browser first
UI:
- Dialog redesign: icon, message, details card; neutral Close for messages
- Start screen shows at launch when nothing is scanned, whichever view was
last used
- Hover card flicker on the map (#8): own layer, measured at its own width
- Palette follows the theme: dark and light each keep their own pick
- Window icon from the .ico (sharp taskbar icon at 125%/150%)
- Largest items legend trims instead of overflowing; sample folder names
translated
Docs: DEVELOPING.md scanning and deleting notes; CHANGELOG 2.4.2 (unreleased)
Copy file name to clipboardExpand all lines: CHANGELOG.md
+27Lines changed: 27 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,7 +10,34 @@ What changed in each SpaceSharp release from 2.0 on, newest first; the 1.x relea
10
10
### Shortcuts
11
11
- The Shortcuts window is wider, the keyboard scales to fill it, and the list under it gives the two longer groups more room, so every shortcut reads on one line; a line that still does not fit wraps instead of being cut off. Three of the lines are shorter ("Move the selection to the Recycle Bin", "Switch between the map and Insights", "Drives and largest items panel"), and the Close button is centered in the footer.
12
12
13
+
### The file table scanner
14
+
The NTFS scanner (`Services/MftScanner.cs`, the fast path for whole drives) was audited against the on-disk format and reworked. The public shape and the fallback to the folder walk are unchanged; what changed is what it accepts and what it refuses.
15
+
16
+
-**Fragmented tables are followed to the end.** The `$MFT` file's own extents came from the one `$DATA` attribute in record 0. A table that has grown in pieces keeps the later pieces in extension records named by record 0's `$ATTRIBUTE_LIST`, and those were never read, so such a drive scanned "successfully" with the tail of the table missing. The attribute list is now read (resident or not), every listed piece is located through the extents already known, checked to belong to `$MFT` (base reference, both sequence numbers, record number), decoded, sorted by VCN and verified gap-free from VCN 0 to the table's valid length. A layout the scanner cannot follow throws and the folder walk takes over.
17
+
-**Records are read as one logical stream.** The reader advanced by the bytes a read returned but parsed only whole records, so a read that ended inside a record resumed halfway through the next one, and the count read for record 0 was ignored. Reads are now exact (looped until full, or an error), a record cut by the end of a read or of an extent is carried into the next, and the scan fails rather than finishing if the extents end before the last record.
18
+
-**Every record is checked to be the record it should be.** The record's own number (NTFS 3.1 and later keep it in the header) is compared with its position in the table; a mismatch stops the scan, since nothing after it could be trusted. The update sequence array must have exactly one entry per 512-byte block plus one, must sit inside the header, and every protected word is compared before any is written back. Zeroed and `BAAD` slots are unused; a `FILE` record that fails these checks is skipped and counted (`DamagedRecords`), which is what a torn write on a live volume looks like. Record 0 failing them stops the scan.
19
+
-**Extension records belong to exactly one base.** A record whose attributes continue another record's was merged into that record by number alone, before or after the base was read. It is now held until the whole table is read and merged only when the base is in use with the sequence number the extension names and the base's attribute list names the extension; the rest are leftovers of deleted files and are counted (`StaleExtensions`). Names and sizes can no longer come from a record that was reused since.
20
+
-**Every field is bounded before it is read.** Attribute lengths against the record's used size, the resident value and the nonresident run list against their attribute, the form byte to 0 or 1, run-list fields against their width and the volume's cluster count with checked arithmetic, record references kept at 48 bits until range-checked, the `FSCTL_GET_NTFS_VOLUME_DATA` reply checked for length and the volume geometry (sector, cluster and record sizes powers of two in range, MFT start inside the volume) before anything is allocated. An eight-byte negative run offset was decoded wrongly (a shift by 64 bits); the sign extension is right for every width now. FILETIME values beyond what `DateTime` holds become "unknown" instead of throwing.
21
+
-**Sparse files report what they use.** The on-disk size of a compressed or sparse stream was read only when the compression unit was nonzero, and sparse-only streams have a unit of 0, so a 1 GB sparse file with 4 KB written counted as 1 GB on disk. The field is used whenever the stream is flagged compressed or sparse and the header is long enough to hold it, which is the structural test.
22
+
-**Hard links keep their size.** The first name in a record owned the file's size; when that name was excluded by a pattern, hidden, or under a folder that was not in the tree, the other names were still added as zero-size duplicates and the file's space vanished. The size now goes to the first name that actually makes it into the tree, the other names are duplicates, exclusion patterns apply to every name, and the same name recorded in two namespaces (Win32 and POSIX) is one link.
23
+
-**Deep and wide trees.** Folders were built recursively with a silent cut at depth 4000 (the folder appeared, its contents did not); the build and the final sums are iterative now with no limit, and cancellation is checked by work done in every stage (linking, building, extra links, summing), not only by depth.
24
+
-**Honest errors.** Opening the volume reports the actual Windows error; only access denied is called "needs administrator rights". Win32 codes are no longer passed where an HRESULT belongs. The per-slot table is released as soon as the tree stands, and the file carries a header comment with the format facts it depends on (record size from the volume, 512-byte fixup stride, 48+16-bit references, memory per slot).
25
+
-**Tests.**`SpaceSharp.Tests/MftImage.cs` builds NTFS volume images in memory (real `FILE` records with fixups, attributes, run lists and attribute lists over any extents) and `MftScannerTests.cs` runs the scanner against them: short reads, records straddling extents, fragmented tables with and without an attribute list, missing coverage, invalid record 0, torn and malformed records, a record claiming another number, stale parents and extensions, hard links under every exclusion, sparse and compressed sizes, reparse folders, a 12,000-deep tree, a 40,000-file folder, cancellation at read and build, and the final progress counts.
26
+
27
+
### Deleting
28
+
- Before a file goes to the Recycle Bin, its size and last-write time are compared with what the scan recorded. A file that changed since, or a path that now names another file, is left alone and listed in the result ("… has changed since the scan and was left alone. Scan again to delete it."). Folders are sent as they are; their contents were never a single snapshot. There is still a moment between the check and the shell call that Windows gives no way to close.
29
+
- Junctions and symbolic links sent to the Recycle Bin are removed as links (`FOF_NORECURSEREPARSE`), never what they point at.
30
+
13
31
### Fixes
32
+
- Recycling browser caches (and other busy or deep folders) could stop with "Windows reported error code 0x7C" and nothing moved. The Recycle Bin call takes the whole list at once and gives up on all of it when one path is too long for it or has vanished since the scan, which cache folders do all the time. Files that are already gone are left out, paths longer than the Recycle Bin allows go in their short form, and when the batch still fails each item is tried on its own, so the rest goes through and the message names what stayed and why (too long, in use, access denied).
33
+
- The Browser caches row in Safe to clear says what it does and does not touch: only the page caches go, never passwords, logins, history or open tabs, and it asks you to close the browser first.
34
+
- The icon in the taskbar and the title bar was jagged: the windows carried the 256 px PNG and Windows shrank it. They carry the .ico now, which has a drawn frame for every size Windows asks for, including 40 and 96 px for 125% and 150% scaling.
35
+
- The legend over the Largest items list ran into the next column with longer names ("Installasjonsfiler"); labels trim inside their cell now, with the full name as a tooltip.
36
+
- The sample drive in Settings and the palette editor had English folder names in every language; Photos, Videos, Programs, Documents and Music are translated.
37
+
- Deleting to the Recycle Bin no longer gives up with an untranslated "The operation was cancelled.": the report says which items were skipped and why, in your language.
38
+
- Message dialogs got a cleaner layout with an icon, a readable message and a separate details block; plain messages now close with a neutral button instead of a red one.
39
+
- Closing the app on the Insights view and opening it again showed an empty Insights page instead of the start screen, until you switched to the map. With nothing scanned the start screen shows whichever view was last used; the remembered view takes over as soon as a scan starts or a saved one is opened.
40
+
- The hover card over the map could flicker between two places on some files (#8): it sat in the map's own layout, so a card that did not fit beside the pointer was squeezed, measured again at full size, moved, squeezed again, and so on. It is on a layer of its own now, measured against its own width, and kept inside the map's edges.
14
41
- Switching themes kept the other theme's palette: going from dark to light left the map on Graphite instead of Pastel, and back again. Each theme now opens with its own last pick, or its default when there is none.
15
42
- The Inspect window is as tall as its content: a folder with four rows under "Where the space is" no longer sits in a window with half a screen of empty space under the rows, and a long selection grows the window up to what the screen allows before it scrolls. Only the width is remembered now.
16
43
- Amber text was hard to read in the light theme: links such as Open folder and Reload, the "Common:" suggestions under Leave out, breadcrumbs and the amber words in the start screen and the filter line. Text in amber uses a deep amber on light surfaces now (the dark theme is unchanged). The Files / Folders / Types buttons sit on the window color instead of a gray fill, which read heavy in the light theme, and Delete all saved scans is a red outline that fills red under the pointer, in both themes.
On Windows a whole NTFS drive is read from its Master File Table, in process when the app has administrator rights and otherwise by an elevated helper process (a second copy of the app with `--mft-helper`), which streams progress over a pipe and hands the finished tree back as a scan file, so the app itself never restarts. Anything else, a single folder, another file system, a Mac or Linux, or a declined UAC prompt, is walked with `DirectoryInfo.EnumerateFileSystemInfos` on background threads, the top three levels in parallel. Sizes are summed bottom-up and each folder's children are sorted largest first, which the layout depends on. Progress counters are read by the UI a few times per second, so the scan never waits on the UI.
8
+
On Windows a whole NTFS drive is read from its Master File Table (record size and extents from `FSCTL_GET_NTFS_VOLUME_DATA` and record 0, including a fragmented table's pieces named by its `$ATTRIBUTE_LIST`; the table is read as one logical stream so records that straddle a read or an extent are reassembled; extension records are merged only when their base record's sequence number and attribute list agree; anything the scanner cannot read completely throws `MftScanner.MftException` and the folder walk takes over), in process when the app has administrator rights and otherwise by an elevated helper process (a second copy of the app with `--mft-helper`), which streams progress over a pipe and hands the finished tree back as a scan file, so the app itself never restarts. Anything else, a single folder, another file system, a Mac or Linux, or a declined UAC prompt, is walked with `DirectoryInfo.EnumerateFileSystemInfos` on background threads, the top three levels in parallel. Sizes are summed bottom-up and each folder's children are sorted largest first, which the layout depends on. Progress counters are read by the UI a few times per second, so the scan never waits on the UI.
On Windows, size on disk comes from `GetCompressedFileSizeW` for compressed, sparse, offline and cloud-placeholder files and from the plain length for everything else, rounded up to the volume's cluster size; hard-link detection reads each file's link count and file ID with `GetFileInformationByHandle`, and a file whose ID was already seen stays in the tree but contributes no size. Elsewhere the plain implementation rounds to 4 KB clusters and treats every file as its own.
@@ -31,8 +31,8 @@ The largest files and folders are found with a priority queue in one pass, so th
Velopack installs per user (Setup.exe) or wherever the user picks (the MSI) on Windows, as a .pkg on macOS and an AppImage on Linux; all update the same way. `Program.Main` runs Velopack's hooks before Avalonia loads. Installed copies check GitHub Releases a few seconds after launch and offer "Install and restart", which downloads a delta package and swaps in the new version. What's new is read from CHANGELOG.md at the new release's tag, so skipped releases are listed too. The portable exe never updates itself.
33
33
34
-
**Deleting** (`Util/Trash.cs`)
35
-
Items go to the Recycle Bin through the Windows shell with undo enabled, to the Finder's Trash on a Mac (so Put Back works) and to the Freedesktop trash through `gio` on Linux. After a delete the node is removed from the tree and its size subtracted from every parent, so no rescan is needed.
The map is a snapshot, so before anything is sent each selected file is checked against the disk: a file whose size or last-write time differs from the scan is left alone and named in the result. Items go to the Recycle Bin through the Windows shell with undo enabled and reparse points treated as items (a junction is removed, never what it points at), to the Finder's Trash on a Mac (so Put Back works) and to the Freedesktop trash through `gio` on Linux. After a delete the node is removed from the tree and its size subtracted from every parent, so no rescan is needed.
<dataname="Cleanup_BrowserCaches_Note"xml:space="preserve"><value>{0:N0} من مجلدات التخزين المؤقت، {1:N0} ملف. تُحمَّل الصفحات أبطأ قليلًا في المرة الأولى بعدها.</value></data>
848
+
<dataname="Cleanup_BrowserCaches_Note"xml:space="preserve"><value>{0:N0} مجلدات ذاكرة مؤقتة، {1:N0} ملفات. ذاكرة الصفحات فقط: لا تُمسّ كلمات المرور وجلسات الدخول والسجل وعلامات التبويب المفتوحة. أغلق المتصفح أولاً؛ تُحمَّل الصفحات أبطأ قليلاً في المرة الأولى بعدها.</value></data>
<dataname="Cleanup_ShaderCaches_Note"xml:space="preserve"><value>{0:N0} مجلد، {1:N0} ملف. تعيد الألعاب وبرامج التشغيل بناءها عند التشغيل التالي.</value></data>
835
851
<dataname="Cleanup_WindowsUpdate"xml:space="preserve"><value>تنزيلات Windows Update</value></data>
0 commit comments