Pārlūkot izejas kodu

lfs: do not reposition seek pointer on truncate

When using lfs_file_truncate() to make a file shorter the file block and
off were incorrectly positioned at the new end, resulting in invalid
data accessed when reading.  Lift the seek pointer restoration to apply
to both increasing and reducing truncates.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
Peter A. Bigot 6 gadi atpakaļ
vecāks
revīzija
5bf71fa43e
2 mainītis faili ar 58 papildinājumiem un 8 dzēšanām
  1. 7 8
      lfs.c
  2. 51 0
      tests/test_truncate.sh

+ 7 - 8
lfs.c

@@ -2981,6 +2981,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
         return LFS_ERR_INVAL;
     }
 
+    lfs_off_t pos = file->pos;
     lfs_off_t oldsize = lfs_file_size(lfs, file);
     if (size < oldsize) {
         // need to flush since directly changing metadata
@@ -3003,8 +3004,6 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
         file->ctz.size = size;
         file->flags |= LFS_F_DIRTY | LFS_F_READING;
     } else if (size > oldsize) {
-        lfs_off_t pos = file->pos;
-
         // flush+seek if not already at end
         if (file->pos != oldsize) {
             int err = lfs_file_seek(lfs, file, 0, LFS_SEEK_END);
@@ -3022,13 +3021,13 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
                 return res;
             }
         }
+    }
 
-        // restore pos
-        int err = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET);
-        if (err < 0) {
-            LFS_TRACE("lfs_file_truncate -> %d", err);
-            return err;
-        }
+    // restore pos
+    int err = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET);
+    if (err < 0) {
+      LFS_TRACE("lfs_file_truncate -> %d", err);
+      return err;
     }
 
     LFS_TRACE("lfs_file_truncate -> %d", 0);

+ 51 - 0
tests/test_truncate.sh

@@ -107,6 +107,57 @@ scripts/test.py << TEST
     lfs_unmount(&lfs) => 0;
 TEST
 
+echo "--- Write, truncate, and read ---"
+scripts/test.py << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+    lfs_file_open(&lfs, &file, "sequence",
+            LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0;
+
+    lfs_size_t size = lfs.cfg->cache_size;
+    lfs_size_t qsize = size / 4;
+    uint8_t *wb = buffer;
+    uint8_t *rb = buffer + size;
+    for (lfs_off_t j = 0; j < size; ++j) {
+        wb[j] = j;
+    }
+
+    /* Spread sequence over size */
+    lfs_file_write(&lfs, &file, wb, size) => size;
+    lfs_file_size(&lfs, &file) => size;
+    lfs_file_tell(&lfs, &file) => size;
+
+    lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
+    lfs_file_tell(&lfs, &file) => 0;
+
+    /* Chop off the last quarter */
+    lfs_size_t trunc = size - qsize;
+    lfs_file_truncate(&lfs, &file, trunc) => 0;
+    lfs_file_tell(&lfs, &file) => 0;
+    lfs_file_size(&lfs, &file) => trunc;
+
+    /* Read should produce first 3/4 */
+    lfs_file_read(&lfs, &file, rb, size) => trunc;
+    memcmp(rb, wb, trunc) => 0;
+
+    /* Move to 1/4 */
+    lfs_file_size(&lfs, &file) => trunc;
+    lfs_file_seek(&lfs, &file, qsize, LFS_SEEK_SET) => qsize;
+    lfs_file_tell(&lfs, &file) => qsize;
+
+    /* Chop to 1/2 */
+    trunc -= qsize;
+    lfs_file_truncate(&lfs, &file, trunc) => 0;
+    lfs_file_tell(&lfs, &file) => qsize;
+    lfs_file_size(&lfs, &file) => trunc;
+    
+    /* Read should produce second quarter */
+    lfs_file_read(&lfs, &file, rb, size) => trunc - qsize;
+    memcmp(rb, wb + qsize, trunc - qsize) => 0;
+
+    lfs_file_close(&lfs, &file) => 0;
+    lfs_unmount(&lfs) => 0;
+TEST
+
 echo "--- Truncate and write ---"
 scripts/test.py << TEST
     lfs_mount(&lfs, &cfg) => 0;