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
Files created from the XML's NFC paths on FSKit ExFAT cannot be unlinked by the name readdir lists (macOS 26): headroom backups, likely expressport --prune #154
Found while chasing a Mac app bug (Delete Backup refused with a permission error on an ExFAT library volume, Playlist mode only). The cause is in how files are named, and it applies to anything baken-core or baken-export creates or renames on an ExFAT or FAT32 volume from a path that came out of collection.xml.
Updated 2026-09-24 after re-measuring on an FSKit ExFAT disk image, which mounts exactly like a real stick (exfat, noowners, fskit). The first version of the table said an NFD-stored file can be unlinked by its NFC name; it cannot. It also missed the rename and expressport consequences below. Fix: #159.
What happens
macOS 26 mounts ExFAT and FAT through FSKit (mount shows exfat, noowners, fskit). On such a volume a name is stored in the normalisation form it was created or renamed with (checked on the raw image bytes), and readdir lists every name in NFD.
Operation
NFC-stored file
NFD-stored file
readdir lists it as
NFD
NFD
open / stat by NFC or NFD
ok
ok
unlink / rmdir by the listed (NFD) name
ENOENT
ok
unlink / rmdir by the NFC name
ok
ENOENT
mkdir, open(O_CREAT) with the other form
finds the existing entry
finds the existing entry
rename onto it with the other form
replaces it; stored in the new form
replaces it; stored in the new form
realpath returns the form it was given and F_GETPATH always returns NFC, so nothing recovers the stored form. renamex_np(RENAME_SWAP) is not supported. The consequences on macOS: rm -rf leaves the file behind and ends with "Directory not empty", Foundation's removeItem fails with a no-such-file or no-permission error, and Finder manages.
Why baken hits it
rekordbox writes every Location in NFC: 593 of the 593 non-ASCII locations in the reporting collection, none in NFD. Files put on the volume by Finder are stored NFD. Lookup accepts either form, so the NFC path from the XML opens the NFD file, and everything baken creates or renames under that path is stored NFC:
headroom::processor::backup_file creates copies at backup_dir.join(relative) from the XML path, so backups made in the Mac app's Playlist mode could not be removed by their listed names. Backups made from a directory scan (NFD names) were fine; that was the whole Folder-versus-Playlist difference in the app.
headroom's lossless (ffmpeg) path writes a temp file and renames it onto the XML path, which re-stores the user's own track under NFC. The same goes for the atomic XML write rbsort and cdjsafe use, and for cdjsafe's output files.
expressport writes the stick in NFC, which is right: rekordbox's export.pdb holds NFC paths and the player looks files up by them. But its own cleanup removed files by their listed names. On 4.0.1 every export ends with No such file or directory (os error 2) as soon as an artist, album or file name is non-ASCII, because the AppleDouble cleanup cannot remove the ._ files macOS leaves beside new files and directories (it does so for directories too, com.apple.provenance). Everything is written by then, so the stick works, but the command reports failure. --prune fails the same way, and it compared the NFD listing against the NFC paths it meant to keep.
Create and rename under the NFD form of the name on macOS (baken_core::fsname::native), and leave names alone elsewhere, where two forms are two files. This covers headroom backups, temp files and the lossless replace, cdjsafe outputs and the atomic XML write. The replace stays a single atomic rename.
Keep the stick in NFC, and make --prune compare in NFC and remove by the listed name with an NFC retry (macOS only). The AppleDouble cleanup does the same and also takes ._Contents and ._PIONEER at the stick root.
unicode-normalization is the dependency this takes.
The Mac app fixes deletion on its side regardless (unlink by listed name, retry in NFC, M-Igashi/baken-mac#46), because copies that already exist on users' drives have to be deletable.
Apple Feedback is worth filing with the table above; not blocking.
Discovered 2026-09-24 on macOS 26.0 (Darwin 27.0.0), baken-core 4.0.0.
Found while chasing a Mac app bug (Delete Backup refused with a permission error on an ExFAT library volume, Playlist mode only). The cause is in how files are named, and it applies to anything
baken-coreorbaken-exportcreates or renames on an ExFAT or FAT32 volume from a path that came out ofcollection.xml.Updated 2026-09-24 after re-measuring on an FSKit ExFAT disk image, which mounts exactly like a real stick (
exfat, noowners, fskit). The first version of the table said an NFD-stored file can be unlinked by its NFC name; it cannot. It also missed the rename and expressport consequences below. Fix: #159.What happens
macOS 26 mounts ExFAT and FAT through FSKit (
mountshowsexfat, noowners, fskit). On such a volume a name is stored in the normalisation form it was created or renamed with (checked on the raw image bytes), andreaddirlists every name in NFD.readdirlists it asopen/statby NFC or NFDunlink/rmdirby the listed (NFD) nameunlink/rmdirby the NFC namemkdir,open(O_CREAT)with the other formrenameonto it with the other formrealpathreturns the form it was given andF_GETPATHalways returns NFC, so nothing recovers the stored form.renamex_np(RENAME_SWAP)is not supported. The consequences on macOS:rm -rfleaves the file behind and ends with "Directory not empty", Foundation'sremoveItemfails with a no-such-file or no-permission error, and Finder manages.Why baken hits it
rekordbox writes every
Locationin NFC: 593 of the 593 non-ASCII locations in the reporting collection, none in NFD. Files put on the volume by Finder are stored NFD. Lookup accepts either form, so the NFC path from the XML opens the NFD file, and everything baken creates or renames under that path is stored NFC:headroom::processor::backup_filecreates copies atbackup_dir.join(relative)from the XML path, so backups made in the Mac app's Playlist mode could not be removed by their listed names. Backups made from a directory scan (NFD names) were fine; that was the whole Folder-versus-Playlist difference in the app.expressportwrites the stick in NFC, which is right: rekordbox'sexport.pdbholds NFC paths and the player looks files up by them. But its own cleanup removed files by their listed names. On 4.0.1 every export ends withNo such file or directory (os error 2)as soon as an artist, album or file name is non-ASCII, because the AppleDouble cleanup cannot remove the._files macOS leaves beside new files and directories (it does so for directories too,com.apple.provenance). Everything is written by then, so the stick works, but the command reports failure.--prunefails the same way, and it compared the NFD listing against the NFC paths it meant to keep.What to do (done in #159)
baken_core::fsname::native), and leave names alone elsewhere, where two forms are two files. This covers headroom backups, temp files and the lossless replace, cdjsafe outputs and the atomic XML write. The replace stays a single atomic rename.--prunecompare in NFC and remove by the listed name with an NFC retry (macOS only). The AppleDouble cleanup does the same and also takes._Contentsand._PIONEERat the stick root.unicode-normalizationis the dependency this takes.scripts/fskit-check.pyreproduces all of the above on a disk image: 4.0.1 fails 8 of its 11 checks, Name files the macOS way and remove stick files by their stored name (#154) #159 passes all of them. It runs locally before a release, since CI has no FSKit.Discovered 2026-09-24 on macOS 26.0 (Darwin 27.0.0),
baken-core4.0.0.