From 6e6a6b15cbd3630e6368ac8da89493f76f47a466 Mon Sep 17 00:00:00 2001 From: "E. C. Masloch" Date: Thu, 23 Apr 2026 21:09:26 +0200 Subject: [PATCH 1/3] fatfs.c: move directory size adjust to dos_extend, if map_cluster failed Original issue: https://github.com/FDOS/kernel/issues/241 Test cases as Script for lDebug files: test$ cat testw1b.sld e 200 "A:\test.dat" 0 f 300 l 400 26 a mov ah, 3C mov cx, 0 mov dx, 200 int 21 xchg bx, ax mov dx, 300 mov cx, 1 mov ah, 40 int 21 mov ah, 68 int 21 int3 int3 jmp 100 . test$ cat testwp1b.sld e 200 "A:\test.dat" 0 f 300 l 400 26 a mov ah, 3C mov cx, 0 mov dx, 200 int 21 xchg bx, ax mov ax, 4200 xor cx, cx mov dx, 200 int 21 mov dx, 300 mov cx, 1 mov ah, 40 int 21 mov ah, 68 int 21 int3 int3 jmp 100 . test$ cat testwpp1.sld e 200 "A:\test.dat" 0 f 300 l 400 26 a mov ah, 3C mov cx, 0 mov dx, 200 int 21 xchg bx, ax mov ax, 4200 xor cx, cx mov dx, 201 int 21 mov dx, 300 mov cx, 1 mov ah, 40 int 21 mov ah, 68 int 21 int3 int3 jmp 100 . test$ cat testw1c.sld e 200 "A:\test.dat" 0 f 300 l 400 26 a mov ah, 3C mov cx, 0 mov dx, 200 int 21 xchg bx, ax mov dx, 300 mov cx, 201 mov ah, 40 int 21 mov ah, 68 int 21 int3 int3 jmp 100 . test$ cat testwp1c.sld e 200 "A:\test.dat" 0 f 300 l 400 26 a mov ah, 3C mov cx, 0 mov dx, 200 int 21 xchg bx, ax mov ax, 4200 xor cx, cx mov dx, 200 int 21 mov dx, 300 mov cx, 201 mov ah, 40 int 21 mov ah, 68 int 21 int3 int3 jmp 100 . test$ --- kernel/fatfs.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/kernel/fatfs.c b/kernel/fatfs.c index 43d2d180..62ebeec3 100644 --- a/kernel/fatfs.c +++ b/kernel/fatfs.c @@ -1024,17 +1024,6 @@ COUNT map_cluster(REG f_node_ptr fnp, COUNT mode) cluster = extend(fnp); if (cluster == LONG_LAST_CLUSTER) return DE_HNDLDSKFULL; - - /* ecm: finally the right solution, only extend/modify - file size after having just appended a cluster. */ - fnp->f_dir.dir_size = - (((ULONG)fnp->f_cluster_offset + 2) - /* at 0 we will increment to 1, having allocated the second - cluster, so + 2 for the then-total size. */ - * (ULONG)fnp->f_dpb->dpb_secsize) - << - fnp->f_dpb->dpb_shftcnt; - merge_file_changes(fnp, FALSE); } fnp->f_cluster = cluster; @@ -1075,8 +1064,21 @@ STATIC COUNT dos_extend(f_node_ptr fnp) while (count > 0) #endif { - if (map_cluster(fnp, XFR_WRITE) != SUCCESS) + if (map_cluster(fnp, XFR_WRITE) != SUCCESS) { + if (fnp->f_cluster != FREE) { + /* ecm: write size if couldn't satisfy full request, + but at least one cluster is allocated. */ + fnp->f_dir.dir_size = + (((ULONG)fnp->f_cluster_offset + 1) + /* at 0 we have incremented to 1, having allocated the second + cluster, so + 1 for the then-total size. */ + * (ULONG)fnp->f_dpb->dpb_secsize) + << + fnp->f_dpb->dpb_shftcnt; + merge_file_changes(fnp, FALSE); + } return DE_HNDLDSKFULL; + } #ifdef WRITEZEROS /* Compute the block within the cluster and the offset */ @@ -1106,6 +1108,18 @@ STATIC COUNT dos_extend(f_node_ptr fnp) } if (bp == NULL) { + if (fnp->f_cluster != FREE) { + /* ecm: write size if couldn't satisfy full request, + but at least one cluster is allocated. */ + fnp->f_dir.dir_size = + (((ULONG)fnp->f_cluster_offset + 1) + /* at 0 we have incremented to 1, having allocated the second + cluster, so + 1 for the then-total size. */ + * (ULONG)fnp->f_dpb->dpb_secsize) + << + fnp->f_dpb->dpb_shftcnt; + merge_file_changes(fnp, FALSE); + } return DE_ACCESS; } From d08f0ec57ac353e0ac58aea11b6644a906ff2f05 Mon Sep 17 00:00:00 2001 From: "E. C. Masloch" Date: Thu, 23 Apr 2026 21:44:27 +0200 Subject: [PATCH 2/3] fatfs.c: no alloc cluster too much if CX = 0 and seek on boundary The possibility of fragmentation due to this misbehaviour was mentioned in https://github.com/FDOS/kernel/pull/245 And it's also just slower to allocate the unneeded cluster just to free it again truncating the file. --- kernel/fatfs.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/kernel/fatfs.c b/kernel/fatfs.c index 62ebeec3..dfd05e10 100644 --- a/kernel/fatfs.c +++ b/kernel/fatfs.c @@ -1044,7 +1044,7 @@ COUNT map_cluster(REG f_node_ptr fnp, COUNT mode) /* #define WRITEZEROS 1 */ /* but because we want to be compatible, we don't do this by */ /* default */ -STATIC COUNT dos_extend(f_node_ptr fnp) +STATIC COUNT dos_extend(f_node_ptr fnp, BOOL emptywrite) { #ifdef WRITEZEROS struct buffer FAR *bp; @@ -1064,7 +1064,20 @@ STATIC COUNT dos_extend(f_node_ptr fnp) while (count > 0) #endif { + BOOL special = 0; + if (emptywrite + /* && fnp->f_offset != 0 */ /* always true here */ + && ((fnp->f_offset & + (((ULONG)fnp->f_dpb->dpb_secsize + << fnp->f_dpb->dpb_shftcnt) - 1) + ) + == 0) + ) { + special = 1; + -- fnp->f_offset; + } if (map_cluster(fnp, XFR_WRITE) != SUCCESS) { + if (special) ++ fnp->f_offset; if (fnp->f_cluster != FREE) { /* ecm: write size if couldn't satisfy full request, but at least one cluster is allocated. */ @@ -1079,6 +1092,7 @@ STATIC COUNT dos_extend(f_node_ptr fnp) } return DE_HNDLDSKFULL; } + if (special) ++ fnp->f_offset; #ifdef WRITEZEROS /* Compute the block within the cluster and the offset */ @@ -1235,7 +1249,7 @@ long rwblock(COUNT fd, VOID FAR * buffer, UCOUNT count, int mode) /* mark file as modified and set date not valid any more */ fnp->f_flags &= ~(SFT_FCLEAN|SFT_FDATE); - if (dos_extend(fnp) != SUCCESS) + if (dos_extend(fnp, count == 0) != SUCCESS) { /* ecm: control flow may end up here if CX = 0000h and the extending failed to allocate a cluster From 5aa0aa53cff63d7eb52e068581ecb901be8ef1ed Mon Sep 17 00:00:00 2001 From: "E. C. Masloch" Date: Thu, 23 Apr 2026 22:00:07 +0200 Subject: [PATCH 3/3] fatfs.c: fix to prior commit if WRITEZEROS defined --- kernel/fatfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/fatfs.c b/kernel/fatfs.c index dfd05e10..4c0103e7 100644 --- a/kernel/fatfs.c +++ b/kernel/fatfs.c @@ -1066,7 +1066,11 @@ STATIC COUNT dos_extend(f_node_ptr fnp, BOOL emptywrite) { BOOL special = 0; if (emptywrite - /* && fnp->f_offset != 0 */ /* always true here */ +#ifdef WRITEZEROS + && fnp->f_offset != 0 +#else + /* f_offset != 0 always true here */ +#endif && ((fnp->f_offset & (((ULONG)fnp->f_dpb->dpb_secsize << fnp->f_dpb->dpb_shftcnt) - 1)