Эх сурвалжийг харах

Fixed lfs_file_truncate issue where internal state may not be flushed

This was caused by the new lfs_file_rawseek optimization that can skip
flushing when calculated file->pos is unchanged combined with an
implicit expectation in lfs_file_truncate that lfs_file_rawseek
unconditionally sets file->pos.

Because of this assumption, lfs_file_truncate could leave file->pos in
an outdated state while changing the internal file metadata. Humorously,
this was always gauranteed to trigger the skip in lfs_file_rawseek when
we try to restore the file->pos, leaving the file->cache used to do the
CTZ skip-list lookup in a potentially bad state.

The easiest fix is to just update file->pos correctly. Note we don't
want to explicitly flush since we can leverage the same noop
optimization if we truncate to the file position. Which I've added a
test for.
Christopher Haster 5 жил өмнө
parent
commit
745d98cde0
2 өөрчлөгдсөн 48 нэмэгдсэн , 0 устгасан
  1. 3 0
      lfs.c
  2. 45 0
      tests/test_truncate.toml

+ 3 - 0
lfs.c

@@ -3106,6 +3106,9 @@ static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
             return err;
         }
 
+        // need to set pos/block/off consistently so seeking back to
+        // the old position does not get confused
+        file->pos = size;
         file->ctz.head = file->block;
         file->ctz.size = size;
         file->flags |= LFS_F_DIRTY | LFS_F_READING;

+ 45 - 0
tests/test_truncate.toml

@@ -392,3 +392,48 @@ code = '''
 
     lfs_unmount(&lfs) => 0;
 '''
+
+[[case]] # noop truncate
+define.MEDIUMSIZE = [32, 2048]
+code = '''
+    lfs_format(&lfs, &cfg) => 0;
+    lfs_mount(&lfs, &cfg) => 0;
+    lfs_file_open(&lfs, &file, "baldynoop",
+            LFS_O_RDWR | LFS_O_CREAT) => 0;
+
+    strcpy((char*)buffer, "hair");
+    size = strlen((char*)buffer);
+    for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
+        lfs_file_write(&lfs, &file, buffer, size) => size;
+
+        // this truncate should do nothing
+        lfs_file_truncate(&lfs, &file, j+size) => 0;
+    }
+    lfs_file_size(&lfs, &file) => MEDIUMSIZE;
+
+    lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
+    // should do nothing again
+    lfs_file_truncate(&lfs, &file, MEDIUMSIZE) => 0;
+    lfs_file_size(&lfs, &file) => MEDIUMSIZE;
+
+    for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
+        lfs_file_read(&lfs, &file, buffer, size) => size;
+        memcmp(buffer, "hair", size) => 0;
+    }
+    lfs_file_read(&lfs, &file, buffer, size) => 0;
+
+    lfs_file_close(&lfs, &file) => 0;
+    lfs_unmount(&lfs) => 0;
+
+    // still there after reboot?
+    lfs_mount(&lfs, &cfg) => 0;
+    lfs_file_open(&lfs, &file, "baldynoop", LFS_O_RDWR) => 0;
+    lfs_file_size(&lfs, &file) => MEDIUMSIZE;
+    for (lfs_off_t j = 0; j < MEDIUMSIZE; j += size) {
+        lfs_file_read(&lfs, &file, buffer, size) => size;
+        memcmp(buffer, "hair", size) => 0;
+    }
+    lfs_file_read(&lfs, &file, buffer, size) => 0;
+    lfs_file_close(&lfs, &file) => 0;
+    lfs_unmount(&lfs) => 0;
+'''