Hi, fsck_msdos checks for linked cluster chains, which means that two chains cross each other at the same cluster. If 1 links to 3 and 2 links to 3, the cluster chains starting at 1 and 2 are linked.
This error condition can be fixed during phase 2, either one or both chains are dropped or the first gets truncated. Now _if_ the chain gets truncated, the length of the cluster head still contains the old value, which means that length can be longer than the actual chain. This becomes an issue in phase 3, in which the length variable is accessed again to retrieve the "physical size" of a file. If this physical size is longer than the size in the directory record, fsck_msdos offers the possibility to truncate the chain. And that will lead to an out of boundary access of the fat array. I did not see a crash on my i386 system, but wrong is wrong anyway. If you want to test on your system, you can retrieve a modified ISO image from my website: http://stoeckmann.org/openbsd/truncate.iso In this ISO file, two chains cross. After truncation, chain will be too short compared to the directory entry's size (FILE1.TXT). Yet fsck_msdos tries to drop superfluous clusters. # vnconfig vnd0c truncate.iso # fsck_msdos vnd0c ** /dev/rvnd0c (vnd0c) ** Phase 1 - Read and Compare FATs ** Phase 2 - Check Cluster Chains Cluster chains starting at 8 and 18 are linked at cluster 5 Clear chain starting at 8? [Fyn] n Truncate? [Fyn] y Clear chain starting at 18? [Fyn] n ** Phase 3 - Check Directories /FILE1.TXT has too many clusters allocated Drop superfluous clusters? [Fyn] y /FILE2.TXT has too many clusters allocated Drop superfluous clusters? [Fyn] y ** Phase 4 - Check for Lost Files Lost cluster chain at cluster 9 9 Cluster(s) lost Reconnect? [Fyn] n Clear? [Fyn] y 3 files, 36 free (9 clusters) It's hard to see any form of error with these file systems, but you will notice difference in behaviour if you stop fsck_msdos after fixing the linked clusters: # fsck_msdos vnd0c ** /dev/rvnd0c (vnd0c) ** Phase 1 - Read and Compare FATs ** Phase 2 - Check Cluster Chains Cluster chains starting at 8 and 18 are linked at cluster 5 Clear chain starting at 8? [Fyn] n Truncate? [Fyn] y Clear chain starting at 18? [Fyn] n ** Phase 3 - Check Directories /FILE1.TXT has too many clusters allocated Drop superfluous clusters? [Fyn] ^C # fsck_msdos vnd0c ** /dev/rvnd0c (vnd0c) ** Phase 1 - Read and Compare FATs ** Phase 2 - Check Cluster Chains ** Phase 3 - Check Directories size of /FILE1.TXT is 8192, should at most be 4096 Truncate? [Fyn] ^C Instead of dropping superfluous clusters, it actually wants to fix the directory entry that is now "too large". The diff verifies that the length is always kept up to date after truncation. Tobias Index: dir.c =================================================================== RCS file: /cvs/src/sbin/fsck_msdos/dir.c,v retrieving revision 1.22 diff -u -p -r1.22 dir.c --- dir.c 16 Jun 2014 18:33:33 -0000 1.22 +++ dir.c 17 Jun 2014 21:50:57 -0000 @@ -374,7 +374,7 @@ checksize(struct bootblock *boot, struct /* * Check size on ordinary files */ - int32_t physicalSize; + u_int32_t physicalSize; if (dir->head == CLUST_FREE) physicalSize = 0; @@ -400,12 +400,16 @@ checksize(struct bootblock *boot, struct fullpath(dir)); if (ask(1, "Drop superfluous clusters")) { cl_t cl; - u_int32_t sz = 0; + u_int32_t len, sz; - for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;) + len = sz = 0; + for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;) { cl = fat[cl].next; + len++; + } clearchain(boot, fat, fat[cl].next); fat[cl].next = CLUST_EOF; + fat[dir->head].length = len; return (FSFATMOD); } else return (FSERROR); Index: fat.c =================================================================== RCS file: /cvs/src/sbin/fsck_msdos/fat.c,v retrieving revision 1.23 diff -u -p -r1.23 fat.c --- fat.c 16 Jun 2014 18:33:33 -0000 1.23 +++ fat.c 17 Jun 2014 21:50:58 -0000 @@ -302,11 +302,20 @@ clearchain(struct bootblock *boot, struc int tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc) { + u_int len; + cl_t p; + if (ask(0, "Clear chain starting at %u", head)) { clearchain(boot, fat, head); return FSFATMOD; } else if (ask(0, "Truncate")) { *trunc = CLUST_EOF; + len = 0; + for (p = head; p >= CLUST_FIRST && p < boot->NumClusters; + p = fat[p].next) { + len++; + } + fat[head].length = len; return FSFATMOD; } else return FSERROR;