Skip to content

Commit

Permalink
Merge pull request #1 from soypat/fatfs-crosstest
Browse files Browse the repository at this point in the history
Add tests and more bugfixes
  • Loading branch information
soypat authored Jan 27, 2024
2 parents 2d2b921 + 8e07b3a commit 0b818a6
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 20 deletions.
61 changes: 56 additions & 5 deletions fat.go
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ func (fsys *FS) pick_lfn(dir []byte) bool {

// mount initializes the FS with the given BlockDevice.
func (fsys *FS) mount_volume(bd BlockDevice, ssize uint16, mode uint8) (fr fileResult) {
_ = str16(fsys.lfnbuf[:0]) // include str16 utility into build for debugging.
fsys.trace("fs:mount_volume", slog.Int("mode", int(mode)))
fsys.fstype = fstypeUnknown // Invalidate any previous mount.
// From here on out we call mount_volume since we don't care about
Expand Down Expand Up @@ -824,7 +825,7 @@ func (fsys *FS) init_fat() fileResult { // Part of mount_volume.
if fsys.nFATs != 1 && fsys.nFATs != 2 {
return frNoFilesystem
}

sectorsPerFAT *= uint32(fsys.nFATs)
fsys.csize = uint16(fsys.win[bpbSecPerClus])
if fsys.csize == 0 || (fsys.csize&(fsys.csize-1)) != 0 {
// Zero or not power of two.
Expand Down Expand Up @@ -1544,15 +1545,15 @@ func (dp *dir) find() fileResult {
dp.blk_ofs = 0xffff_ffff // Reset LFN sequence.
} else {
if attr == amLFN {
if dp.fn[nsFLAG]&nsNOLFN != 0 {
if dp.fn[nsFLAG]&nsNOLFN == 0 {
if c&mskLLEF != 0 {
// Start of LFN sequence.
sum = dp.dir[ldirChksumOff]
c &^= mskLLEF
ord = c
dp.blk_ofs = dp.dptr
}
if c == ord && sum == dp.dir[ldirChksumOff] {
if c == ord && sum == dp.dir[ldirChksumOff] && fsys.cmp_lfn(dp.dir[:]) {
ord--
} else {
ord = 0xff
Expand Down Expand Up @@ -1761,6 +1762,7 @@ func (dp *dir) create_name(path string) (string, fileResult) {
}
}
if wc >= 0x100 {
// This is a DBC.
if i >= ni-1 {
// Possible field overflow.
cf |= nsLOSS | nsLFN
Expand Down Expand Up @@ -1824,7 +1826,6 @@ func (dp *dir) sdi(ofs uint32) fileResult {
clst = uint32(fsys.dirbase)
dp.obj.stat = 0 // exFAT: Root dir has a FAT chain.
}

}

if clst == 0 {
Expand Down Expand Up @@ -2227,7 +2228,40 @@ func sum_sfn(sfn []byte) (sum byte) {
}

func memcmp(a, b *byte, n int) bool {
return unsafe.String(a, n) == unsafe.String(b, n)
return unsafe.String(a, n) != unsafe.String(b, n)
}

// cmp_lfn returns true if entry matches LFN.
func (fsys *FS) cmp_lfn(dir []byte) bool {
fsys.trace("fs:cmp_lfn")
lfn := fsys.lfnbuf[:]
if binary.LittleEndian.Uint16(dir[ldirFstClusLO_Off:]) != 0 {
return false
}
i := int(dir[ldirOrdOff]&0x3F-1) * 13 // Offset in the LFN buffer.

var wc uint16 = 1
for s := 0; s < 13; s++ {
uc := binary.LittleEndian.Uint16(dir[lfnOffsets[s]:])
if wc != 0 {
// TODO: optimize branching below after validated.
lfnc := rune(lfn[i])
w1 := ff_wtoupper(rune(uc))
w2 := ff_wtoupper(lfnc)
if i >= lfnBufSize+1 || w1 != w2 {
return false
}
i++
wc = uc
} else {
if uc != 0xFFFF {
return false
}
}
}
return !(dir[ldirOrdOff]&mskLLEF != 0 && wc != 0 && lfn[i] != 0)
// TODO(soypat): check if below is equivalent:
// return dir[ldirOrdOff]&mskLLEF == 0 || wc == 0 || lfn[i] == 0
}

func (fsys *FS) gen_numname(dst, src []byte, lfn []uint16, seq uint32) {
Expand Down Expand Up @@ -2292,3 +2326,20 @@ func (fsys *FS) gen_numname(dst, src []byte, lfn []uint16, seq uint32) {
}
}
}

func str16(s []uint16) string {
if len(s) == 0 {
return ""
}
var buf []byte
for i := 0; i < len(s); i++ {
b := s[i]
if b == 0 || b >= utf8.RuneError {
return string(buf)
} else if b >= 0x80 {
buf = append(buf, byte(b>>8))
}
buf = append(buf, byte(b))
}
return string(buf)
}
80 changes: 65 additions & 15 deletions fat_test.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,61 @@
package fat

import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"log/slog"
"os"
"testing"
)

func TestOpenRootFile(t *testing.T) {
fs, _ := initTestFAT()
var fp File
fr := fs.f_open(&fp, "rootfile\x00", faRead)
if fr != frOK {
t.Fatal(fr.Error())
}
}

func TestFileInfo(t *testing.T) {
fs, _ := initTestFAT()
var dir dir
fr := fs.f_opendir(&dir, "rootdir\x00")
if fr != frOK {
t.Fatal(fr.Error())
}
var finfo fileinfo
fr = dir.f_readdir(&finfo)
if fr != frOK {
t.Fatal(fr.Error())
}
}

func attachLogger(fs *FS) *slog.Logger {
fs.log = slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
Level: slogLevelTrace,
}))
return fs.log
}

func ExampleRead() {
const (
filename = "test.txt\x00"
data = "abc123"
)
var fs FS
log := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
Level: slogLevelTrace,
}))
fs.log = log // Uncomment to see debug output.
log := attachLogger(&fs)
dev := DefaultFATByteBlocks(32000)
fr := fs.mount_volume(dev, uint16(dev.blk.size()), faRead|faWrite)
if fr != frOK {
log.Error("mount failed:" + fr.Error())
return
}

var fp File
// var root dir
// fr = fs.f_opendir(&root, "rootdir")
// if fr != frOK {
// log.Error("open dir failed:" + fr.Error())
// return
// }
// fmt.Println("ok")
// return

fr = fs.f_open(&fp, filename, faRead|faWrite|faCreateNew)
if fr != frOK {
log.Error("open for write failed:" + fr.Error())
Expand All @@ -47,13 +71,13 @@ func ExampleRead() {
log.Error("write failed: short write")
return
}
fmt.Printf("start file data: %q...\n", string(dev.buf[fp.sect*512:][:20]))

fr = fp.f_close()
if fr != frOK {
log.Error("close failed:" + fr.Error())
return
}
fmt.Printf("start file data: %q...\n", string(dev.buf[fp.sect*512:][:20]))

// Read back data.
fr = fs.f_open(&fp, filename, faRead)
if fr != frOK {
Expand All @@ -77,7 +101,6 @@ func ExampleRead() {
return
}
fmt.Println("wrote and read back file OK!")
// Output: aa
}

func DefaultFATByteBlocks(numBlocks int) *BytesBlocks {
Expand Down Expand Up @@ -154,6 +177,33 @@ func (b *BytesBlocks) Mode() uint8 {
return 3
}

func fatInitDiff(data []byte) (s string) {
max := int64(len(data)) / 512
for block := int64(0); block < max; block++ {
b := data[block*512 : (block+1)*512]
expect := fatInit[block]
if !bytes.Equal(b, expect[:]) {
s += fmt.Sprintf("block %d got!=want:\n%s\n%s\n", block, hex.Dump(b), hex.Dump(expect[:]))
}
}
if len(s) == 0 {
return "no differences"
}
return s
}

func initTestFAT() (*FS, *BytesBlocks) {
dev := DefaultFATByteBlocks(32000)
var fs FS
attachLogger(&fs)
ss := uint16(dev.blk.size())
fr := fs.mount_volume(dev, ss, faRead|faWrite)
if fr != frOK {
panic(fr.Error())
}
return &fs, dev
}

// Start of clean slate FAT32 filesystem image with name `keylargo`, 8GB in size.
// Contains a folder structure with a rootfile with some test, a rootdir directory
// with a file in it.
Expand Down

0 comments on commit 0b818a6

Please sign in to comment.