jkent / frogfs

Fast Read-Only General-purpose File System (ESP-IDF and ESP8266_RTOS_SDK compatible)
https://frogfs.readthedocs.io
Mozilla Public License 2.0
25 stars 32 forks source link

Invalid file position when seeking in heatshrink compressed file #40

Closed arex-ebee closed 1 year ago

arex-ebee commented 1 year ago

The implementation of frogfs_fseek() (or espfs_fseek in latest 3.0.0 branch) has a weakness with heatshrink compressed files when seeking backwards - at least when new position is not a multiple of 16. This could be made visible when performing either an fread() afterwards (comparing the read data with the known file content at that position) or just calling ftell() right after fseek(). I prepared a standalone test app to verify and analyze the issue (find attached below).

The example has a filesystem attached as byte array. It only consists of one heatshrink-compressed file named "file.bin". This file's raw content is included as byte array as well. When running the example it performs four seek and read operations and compares them against the expected result. Test 4 seeks backwards from file position left at test 3. At this point file position as well as read data do not match the expectation.

I TESTAPP: Test #1
I TESTAPP: Seek to offset 0x0000
I TESTAPP: Read 12 bytes:  DE 12 04 95 00 00 00 00 31 00 00 00
I TESTAPP: Test #2
I TESTAPP: Seek to offset 0x000c
I TESTAPP: Read 8 bytes:  1C 00 00 00 A4 01 00 00
I TESTAPP: Test #3
I TESTAPP: Seek to offset 0x03d4
I TESTAPP: Read 4 bytes:  26 00 00 00
I TESTAPP: Test #4
I TESTAPP: Seek to offset 0x0384
E TESTAPP: SEEK MISMATCH! Sought to 0x0384 but ftell() reports 0x0390
I TESTAPP: Read 4 bytes:  18 00 00 00
E TESTAPP: CONTENT MISMATCH - offset 0x0384 expects  28 00 00 00 
I TESTAPP: Done...

The root cause seems to be in frogfs_fseek() method at the end of the "heatshrink" branch. There all data from file begin to some position is read in order to "pump" it through heatshrink decoder (I guess this is due to the fact that heatshrinked data blocks rely on each other). But here the data is read in fixed 16 byte blocks, hence the exact seek position is usually missed.

        while (new_pos > f->file_pos) {
            uint8_t buf[16];
            frogfs_fread(f, buf, sizeof(buf));
        }

Test application: