Repository navigation
fs/fat: Preserve live chains and cursors across truncation. - #21462
yushuailong wants to merge 2 commits into
Conversation
Terminate retained clusters with an EOC marker rather than marking live file data free. Restore the directory sector after FAT traversal and use the shared start cluster when the file has not been synced. Position zero extension at actual EOF and reset shared traversal cursors after success or failure. Mark changed metadata for sync so subsequent writes and remounts retain the intended file contents. Validation: RAM-disk simulator regressions covered FAT12/16/32, linked and unlinked shrink/extend, caller offsets, shared handles, remounts and writes after ENOSPC. Upstream failed 66 of 327 checks; this fix passed all 327 in direct and forced-indirect modes. The nucleo-h743zi:composite ARM build, image size and style checks passed. No new hardware runtime testing was performed. Assisted-by: Codex:GPT-6 Signed-off-by: yushuailong <yyyusl@qq.com>
Align adjacent assignments when positioning the EOF cursor and preserving the directory sector, following the columnar layout preferred by the NuttX coding style. Only whitespace changes. Verified an empty whitespace-insensitive diff, identical non-whitespace text and passing full-file checkpatch. Assisted-by: Codex:GPT-6 Signed-off-by: yushuailong <yyyusl@qq.com>
|
casaroli
left a comment
There was a problem hiding this comment.
fat_open() with O_TRUNC (fs_fat32.c:558) still truncates the on-disk start cluster, the stale case this PR fixes in fat_truncate(). Create and write 3 clusters on fd1 with no sync, then open(O_WRONLY | O_TRUNC) on fd2: the free count drops by 3 and the raw FAT has 3 lost clusters.
Fix: when the file is already open, put s_startcluster into direntry before fat_dirtruncate(), as fat_truncate() does now.
The Testing section logs only sim:ostest, which does not mount FAT. The commit message cites 327 RAM-disk checks, but the test and its log are not in the PR.
Fix: add the test program or a link to it, and its log, to the PR body.
|
|
||
| savepos = filep->f_pos; | ||
| filep->f_pos = oldsize; | ||
| ret = fat_get_sectors(filep, false); |
There was a problem hiding this comment.
ftruncate() on a full volume returns EIO when the file is empty or ends at a cluster boundary, and ENOSPC when it ends inside a cluster: fat_get_sectors() turns a 0 from fat_extendchain() into -EIO. On a RAM disk with FAT12/16/32: errno 5, 5, 28.
Fix: return -ENOSPC from fat_get_sectors() when fat_extendchain() returns 0 (both calls).
| } | ||
|
|
||
| ff->ff_bflags |= FFBUFF_MODIFIED; | ||
| ret = fat_dirextend(fs, ff, length); |
There was a problem hiding this comment.
A failed extension keeps the clusters it allocated. With 1 free cluster, ftruncate(fd, size + 10 clusters) returns ENOSPC, the free count stays 0, and after a remount the chain has 3 clusters for a size that needs 2. The PR body lists recovery after an extension runs out of space.
Fix: when fat_dirextend() fails, cut the chain back to oldsize.
Summary
Fix FAT file truncation leaving retained cluster chains, directory cache contents and traversal cursors inconsistent with the file's new length.
When shrinking a file, the retained tail cluster is currently marked free instead of being terminated with an end-of-chain marker. FAT traversal also reuses the directory cache, and a file that has grown without synchronization may still have an outdated start cluster in its directory entry.
When extending a file, the traversal cursor may not point to the actual EOF. This can affect retained data, zero-filling and subsequent writes, including recovery after an extension runs out of space.
This PR:
The first commit contains the functional fix. The second commit only aligns three assignment lines introduced by the fix; it changes no non-whitespace text.
Impact
Testing
Build host: macOS 26.6.2 ARM64, Apple Clang 21.0.0.
Target: sim:ostest.
Build: make completes successfully with LD: nuttx.
Static checks: nxstyle, checkpatch pass.
Runtime smoke test: ./nuttx starts and proceeds through enabled OSTest cases without an assertion in the observed run.