Selaa lähdekoodia

Merge branch 'master' of https://github.com/geky/littlefs

xieyangrun 6 vuotta sitten
vanhempi
sitoutus
946a7956aa
7 muutettua tiedostoa jossa 291 lisäystä ja 46 poistoa
  1. 1 0
      .travis.yml
  2. 1 0
      Makefile
  3. 1 1
      README.md
  4. 65 45
      lfs.c
  5. 10 0
      tests/test_alloc.sh
  6. 139 0
      tests/test_relocations.sh
  7. 74 0
      tests/test_seek.sh

+ 1 - 0
.travis.yml

@@ -20,6 +20,7 @@ script:
   # run tests with a few different configurations
   # run tests with a few different configurations
   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1      -DLFS_CACHE_SIZE=4"
   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1      -DLFS_CACHE_SIZE=4"
   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512    -DLFS_CACHE_SIZE=512 -DLFS_BLOCK_CYCLES=16"
   - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512    -DLFS_CACHE_SIZE=512 -DLFS_BLOCK_CYCLES=16"
+  - make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=8      -DLFS_CACHE_SIZE=16  -DLFS_BLOCK_CYCLES=2"
   - make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256"
   - make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256"
 
 
   - make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0"
   - make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0"

+ 1 - 0
Makefile

@@ -55,6 +55,7 @@ test: \
 	test_attrs \
 	test_attrs \
 	test_move \
 	test_move \
 	test_orphan \
 	test_orphan \
+	test_relocations \
 	test_corrupt
 	test_corrupt
 	@rm test.c
 	@rm test.c
 test_%: tests/test_%.sh
 test_%: tests/test_%.sh

+ 1 - 1
README.md

@@ -110,7 +110,7 @@ directory functions, with the deviation that the allocation of filesystem
 structures must be provided by the user.
 structures must be provided by the user.
 
 
 All POSIX operations, such as remove and rename, are atomic, even in event
 All POSIX operations, such as remove and rename, are atomic, even in event
-of power-loss. Additionally, no file updates are not actually committed to
+of power-loss. Additionally, file updates are not actually committed to
 the filesystem until sync or close is called on the file.
 the filesystem until sync or close is called on the file.
 
 
 ## Other notes
 ## Other notes

+ 65 - 45
lfs.c

@@ -929,6 +929,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
                 if (res == LFS_CMP_EQ) {
                 if (res == LFS_CMP_EQ) {
                     // found a match
                     // found a match
                     tempbesttag = tag;
                     tempbesttag = tag;
+                } else if ((LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) ==
+                        (LFS_MKTAG(0x7ff, 0x3ff, 0) & tempbesttag)) {
+                    // found an identical tag, but contents didn't match
+                    // this must mean that our besttag has been overwritten
+                    tempbesttag = -1;
                 } else if (res == LFS_CMP_GT &&
                 } else if (res == LFS_CMP_GT &&
                         lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) {
                         lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) {
                     // found a greater match, keep track to keep things sorted
                     // found a greater match, keep track to keep things sorted
@@ -975,9 +980,10 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
 
 
 static int lfs_dir_fetch(lfs_t *lfs,
 static int lfs_dir_fetch(lfs_t *lfs,
         lfs_mdir_t *dir, const lfs_block_t pair[2]) {
         lfs_mdir_t *dir, const lfs_block_t pair[2]) {
-    // note, mask=-1, tag=0 can never match a tag since this
+    // note, mask=-1, tag=-1 can never match a tag since this
     // pattern has the invalid bit set
     // pattern has the invalid bit set
-    return (int)lfs_dir_fetchmatch(lfs, dir, pair, -1, 0, NULL, NULL, NULL);
+    return (int)lfs_dir_fetchmatch(lfs, dir, pair,
+            (lfs_tag_t)-1, (lfs_tag_t)-1, NULL, NULL, NULL);
 }
 }
 
 
 static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir,
 static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir,
@@ -1377,6 +1383,7 @@ static int lfs_dir_split(lfs_t *lfs,
         lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
         lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
         lfs_mdir_t *source, uint16_t split, uint16_t end) {
         lfs_mdir_t *source, uint16_t split, uint16_t end) {
     // create tail directory
     // create tail directory
+    lfs_alloc_ack(lfs);
     lfs_mdir_t tail;
     lfs_mdir_t tail;
     int err = lfs_dir_alloc(lfs, &tail);
     int err = lfs_dir_alloc(lfs, &tail);
     if (err) {
     if (err) {
@@ -1503,9 +1510,13 @@ static int lfs_dir_compact(lfs_t *lfs,
                 }
                 }
             }
             }
 #ifdef LFS_MIGRATE
 #ifdef LFS_MIGRATE
-        } else if (lfs_pair_cmp(dir->pair, lfs->root) == 0 && lfs->lfs1) {
-            // we can't relocate our root during migrations, as this would
-            // cause the superblock to get updated, which would clobber v1
+        } else if (lfs->lfs1) {
+            // do not proactively relocate blocks during migrations, this
+            // can cause a number of failure states such: clobbering the
+            // v1 superblock if we relocate root, and invalidating directory
+            // pointers if we relocate the head of a directory. On top of
+            // this, relocations increase the overall complexity of
+            // lfs_migration, which is already a delicate operation.
 #endif
 #endif
         } else {
         } else {
             // we're writing too much, time to relocate
             // we're writing too much, time to relocate
@@ -1643,7 +1654,6 @@ relocate:
         if (err && (err != LFS_ERR_NOSPC && !exhausted)) {
         if (err && (err != LFS_ERR_NOSPC && !exhausted)) {
             return err;
             return err;
         }
         }
-
         continue;
         continue;
     }
     }
 
 
@@ -1936,7 +1946,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
     LFS_TRACE("lfs_dir_open(%p, %p, \"%s\")", (void*)lfs, (void*)dir, path);
     LFS_TRACE("lfs_dir_open(%p, %p, \"%s\")", (void*)lfs, (void*)dir, path);
     lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL);
     lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL);
     if (tag < 0) {
     if (tag < 0) {
-        LFS_TRACE("lfs_dir_open -> %d", tag);
+        LFS_TRACE("lfs_dir_open -> %"PRId32, tag);
         return tag;
         return tag;
     }
     }
 
 
@@ -1955,7 +1965,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
         lfs_stag_t res = lfs_dir_get(lfs, &dir->m, LFS_MKTAG(0x700, 0x3ff, 0),
         lfs_stag_t res = lfs_dir_get(lfs, &dir->m, LFS_MKTAG(0x700, 0x3ff, 0),
                 LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair);
                 LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair);
         if (res < 0) {
         if (res < 0) {
-            LFS_TRACE("lfs_dir_open -> %d", res);
+            LFS_TRACE("lfs_dir_open -> %"PRId32, res);
             return res;
             return res;
         }
         }
         lfs_pair_fromle32(pair);
         lfs_pair_fromle32(pair);
@@ -2064,10 +2074,14 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
     dir->pos = lfs_min(2, off);
     dir->pos = lfs_min(2, off);
     off -= dir->pos;
     off -= dir->pos;
 
 
-    while (off != 0) {
-        dir->id = lfs_min(dir->m.count, off);
-        dir->pos += dir->id;
-        off -= dir->id;
+    // skip superblock entry
+    dir->id = (off > 0 && lfs_pair_cmp(dir->head, lfs->root) == 0);
+
+    while (off > 0) {
+        int diff = lfs_min(dir->m.count - dir->id, off);
+        dir->id += diff;
+        dir->pos += diff;
+        off -= diff;
 
 
         if (dir->id == dir->m.count) {
         if (dir->id == dir->m.count) {
             if (!dir->m.split) {
             if (!dir->m.split) {
@@ -2080,6 +2094,8 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
                 LFS_TRACE("lfs_dir_seek -> %d", err);
                 LFS_TRACE("lfs_dir_seek -> %d", err);
                 return err;
                 return err;
             }
             }
+
+            dir->id = 0;
         }
         }
     }
     }
 
 
@@ -2103,8 +2119,6 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) {
         return err;
         return err;
     }
     }
 
 
-    dir->m.pair[0] = dir->head[0];
-    dir->m.pair[1] = dir->head[1];
     dir->id = 0;
     dir->id = 0;
     dir->pos = 0;
     dir->pos = 0;
     LFS_TRACE("lfs_dir_rewind -> %d", 0);
     LFS_TRACE("lfs_dir_rewind -> %d", 0);
@@ -2738,14 +2752,14 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
         // flush out any writes
         // flush out any writes
         int err = lfs_file_flush(lfs, file);
         int err = lfs_file_flush(lfs, file);
         if (err) {
         if (err) {
-            LFS_TRACE("lfs_file_read -> %"PRId32, err);
+            LFS_TRACE("lfs_file_read -> %d", err);
             return err;
             return err;
         }
         }
     }
     }
 
 
     if (file->pos >= file->ctz.size) {
     if (file->pos >= file->ctz.size) {
         // eof if past end
         // eof if past end
-        LFS_TRACE("lfs_file_read -> %"PRId32, 0);
+        LFS_TRACE("lfs_file_read -> %d", 0);
         return 0;
         return 0;
     }
     }
 
 
@@ -2761,7 +2775,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
                         file->ctz.head, file->ctz.size,
                         file->ctz.head, file->ctz.size,
                         file->pos, &file->block, &file->off);
                         file->pos, &file->block, &file->off);
                 if (err) {
                 if (err) {
-                    LFS_TRACE("lfs_file_read -> %"PRId32, err);
+                    LFS_TRACE("lfs_file_read -> %d", err);
                     return err;
                     return err;
                 }
                 }
             } else {
             } else {
@@ -2781,7 +2795,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
                     LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0),
                     LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0),
                     file->off, data, diff);
                     file->off, data, diff);
             if (err) {
             if (err) {
-                LFS_TRACE("lfs_file_read -> %"PRId32, err);
+                LFS_TRACE("lfs_file_read -> %d", err);
                 return err;
                 return err;
             }
             }
         } else {
         } else {
@@ -2789,7 +2803,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
                     NULL, &file->cache, lfs->cfg->block_size,
                     NULL, &file->cache, lfs->cfg->block_size,
                     file->block, file->off, data, diff);
                     file->block, file->off, data, diff);
             if (err) {
             if (err) {
-                LFS_TRACE("lfs_file_read -> %"PRId32, err);
+                LFS_TRACE("lfs_file_read -> %d", err);
                 return err;
                 return err;
             }
             }
         }
         }
@@ -2818,7 +2832,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
         // drop any reads
         // drop any reads
         int err = lfs_file_flush(lfs, file);
         int err = lfs_file_flush(lfs, file);
         if (err) {
         if (err) {
-            LFS_TRACE("lfs_file_write -> %"PRId32, err);
+            LFS_TRACE("lfs_file_write -> %d", err);
             return err;
             return err;
         }
         }
     }
     }
@@ -2829,7 +2843,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
 
 
     if (file->pos + size > lfs->file_max) {
     if (file->pos + size > lfs->file_max) {
         // Larger than file limit?
         // Larger than file limit?
-        LFS_TRACE("lfs_file_write -> %"PRId32, LFS_ERR_FBIG);
+        LFS_TRACE("lfs_file_write -> %d", LFS_ERR_FBIG);
         return LFS_ERR_FBIG;
         return LFS_ERR_FBIG;
     }
     }
 
 
@@ -2855,7 +2869,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
         int err = lfs_file_outline(lfs, file);
         int err = lfs_file_outline(lfs, file);
         if (err) {
         if (err) {
             file->flags |= LFS_F_ERRED;
             file->flags |= LFS_F_ERRED;
-            LFS_TRACE("lfs_file_write -> %"PRId32, err);
+            LFS_TRACE("lfs_file_write -> %d", err);
             return err;
             return err;
         }
         }
     }
     }
@@ -2872,7 +2886,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
                             file->pos-1, &file->block, &file->off);
                             file->pos-1, &file->block, &file->off);
                     if (err) {
                     if (err) {
                         file->flags |= LFS_F_ERRED;
                         file->flags |= LFS_F_ERRED;
-                        LFS_TRACE("lfs_file_write -> %"PRId32, err);
+                        LFS_TRACE("lfs_file_write -> %d", err);
                         return err;
                         return err;
                     }
                     }
 
 
@@ -2887,7 +2901,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
                         &file->block, &file->off);
                         &file->block, &file->off);
                 if (err) {
                 if (err) {
                     file->flags |= LFS_F_ERRED;
                     file->flags |= LFS_F_ERRED;
-                    LFS_TRACE("lfs_file_write -> %"PRId32, err);
+                    LFS_TRACE("lfs_file_write -> %d", err);
                     return err;
                     return err;
                 }
                 }
             } else {
             } else {
@@ -2908,7 +2922,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
                     goto relocate;
                     goto relocate;
                 }
                 }
                 file->flags |= LFS_F_ERRED;
                 file->flags |= LFS_F_ERRED;
-                LFS_TRACE("lfs_file_write -> %"PRId32, err);
+                LFS_TRACE("lfs_file_write -> %d", err);
                 return err;
                 return err;
             }
             }
 
 
@@ -2917,7 +2931,7 @@ relocate:
             err = lfs_file_relocate(lfs, file);
             err = lfs_file_relocate(lfs, file);
             if (err) {
             if (err) {
                 file->flags |= LFS_F_ERRED;
                 file->flags |= LFS_F_ERRED;
-                LFS_TRACE("lfs_file_write -> %"PRId32, err);
+                LFS_TRACE("lfs_file_write -> %d", err);
                 return err;
                 return err;
             }
             }
         }
         }
@@ -2944,7 +2958,7 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
     // write out everything beforehand, may be noop if rdonly
     // write out everything beforehand, may be noop if rdonly
     int err = lfs_file_flush(lfs, file);
     int err = lfs_file_flush(lfs, file);
     if (err) {
     if (err) {
-        LFS_TRACE("lfs_file_seek -> %"PRId32, err);
+        LFS_TRACE("lfs_file_seek -> %d", err);
         return err;
         return err;
     }
     }
 
 
@@ -2960,7 +2974,7 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
 
 
     if (npos > lfs->file_max) {
     if (npos > lfs->file_max) {
         // file position out of range
         // file position out of range
-        LFS_TRACE("lfs_file_seek -> %"PRId32, LFS_ERR_INVAL);
+        LFS_TRACE("lfs_file_seek -> %d", LFS_ERR_INVAL);
         return LFS_ERR_INVAL;
         return LFS_ERR_INVAL;
     }
     }
 
 
@@ -3008,7 +3022,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
         if (file->pos != oldsize) {
         if (file->pos != oldsize) {
             lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_END);
             lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_END);
             if (res < 0) {
             if (res < 0) {
-                LFS_TRACE("lfs_file_truncate -> %d", res);
+                LFS_TRACE("lfs_file_truncate -> %"PRId32, res);
                 return (int)res;
                 return (int)res;
             }
             }
         }
         }
@@ -3017,7 +3031,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
         while (file->pos < size) {
         while (file->pos < size) {
             lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1);
             lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1);
             if (res < 0) {
             if (res < 0) {
-                LFS_TRACE("lfs_file_truncate -> %d", res);
+                LFS_TRACE("lfs_file_truncate -> %"PRId32, res);
                 return (int)res;
                 return (int)res;
             }
             }
         }
         }
@@ -3026,7 +3040,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
     // restore pos
     // restore pos
     lfs_soff_t res = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET);
     lfs_soff_t res = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET);
     if (res < 0) {
     if (res < 0) {
-      LFS_TRACE("lfs_file_truncate -> %d", res);
+      LFS_TRACE("lfs_file_truncate -> %"PRId32, res);
       return (int)res;
       return (int)res;
     }
     }
 
 
@@ -3046,7 +3060,7 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) {
     LFS_TRACE("lfs_file_rewind(%p, %p)", (void*)lfs, (void*)file);
     LFS_TRACE("lfs_file_rewind(%p, %p)", (void*)lfs, (void*)file);
     lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_SET);
     lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_SET);
     if (res < 0) {
     if (res < 0) {
-        LFS_TRACE("lfs_file_rewind -> %d", res);
+        LFS_TRACE("lfs_file_rewind -> %"PRId32, res);
         return (int)res;
         return (int)res;
     }
     }
 
 
@@ -3075,7 +3089,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
     lfs_mdir_t cwd;
     lfs_mdir_t cwd;
     lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
     lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
     if (tag < 0) {
     if (tag < 0) {
-        LFS_TRACE("lfs_stat -> %d", tag);
+        LFS_TRACE("lfs_stat -> %"PRId32, tag);
         return (int)tag;
         return (int)tag;
     }
     }
 
 
@@ -3096,7 +3110,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
     lfs_mdir_t cwd;
     lfs_mdir_t cwd;
     lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
     lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
     if (tag < 0 || lfs_tag_id(tag) == 0x3ff) {
     if (tag < 0 || lfs_tag_id(tag) == 0x3ff) {
-        LFS_TRACE("lfs_remove -> %d", (tag < 0) ? tag : LFS_ERR_INVAL);
+        LFS_TRACE("lfs_remove -> %"PRId32, (tag < 0) ? tag : LFS_ERR_INVAL);
         return (tag < 0) ? (int)tag : LFS_ERR_INVAL;
         return (tag < 0) ? (int)tag : LFS_ERR_INVAL;
     }
     }
 
 
@@ -3107,7 +3121,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
         lfs_stag_t res = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x700, 0x3ff, 0),
         lfs_stag_t res = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x700, 0x3ff, 0),
                 LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair);
                 LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair);
         if (res < 0) {
         if (res < 0) {
-            LFS_TRACE("lfs_remove -> %d", res);
+            LFS_TRACE("lfs_remove -> %"PRId32, res);
             return (int)res;
             return (int)res;
         }
         }
         lfs_pair_fromle32(pair);
         lfs_pair_fromle32(pair);
@@ -3170,7 +3184,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
     lfs_mdir_t oldcwd;
     lfs_mdir_t oldcwd;
     lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath, NULL);
     lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath, NULL);
     if (oldtag < 0 || lfs_tag_id(oldtag) == 0x3ff) {
     if (oldtag < 0 || lfs_tag_id(oldtag) == 0x3ff) {
-        LFS_TRACE("lfs_rename -> %d", (oldtag < 0) ? oldtag : LFS_ERR_INVAL);
+        LFS_TRACE("lfs_rename -> %"PRId32, (oldtag < 0) ? oldtag : LFS_ERR_INVAL);
         return (oldtag < 0) ? (int)oldtag : LFS_ERR_INVAL;
         return (oldtag < 0) ? (int)oldtag : LFS_ERR_INVAL;
     }
     }
 
 
@@ -3180,7 +3194,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
     lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid);
     lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid);
     if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) &&
     if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) &&
             !(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) {
             !(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) {
-        LFS_TRACE("lfs_rename -> %d", (prevtag < 0) ? prevtag : LFS_ERR_INVAL);
+        LFS_TRACE("lfs_rename -> %"PRId32, (prevtag < 0) ? prevtag : LFS_ERR_INVAL);
         return (prevtag < 0) ? (int)prevtag : LFS_ERR_INVAL;
         return (prevtag < 0) ? (int)prevtag : LFS_ERR_INVAL;
     }
     }
 
 
@@ -3201,7 +3215,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
         lfs_stag_t res = lfs_dir_get(lfs, &newcwd, LFS_MKTAG(0x700, 0x3ff, 0),
         lfs_stag_t res = lfs_dir_get(lfs, &newcwd, LFS_MKTAG(0x700, 0x3ff, 0),
                 LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair);
                 LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair);
         if (res < 0) {
         if (res < 0) {
-            LFS_TRACE("lfs_rename -> %d", res);
+            LFS_TRACE("lfs_rename -> %"PRId32, res);
             return (int)res;
             return (int)res;
         }
         }
         lfs_pair_fromle32(prevpair);
         lfs_pair_fromle32(prevpair);
@@ -3296,7 +3310,7 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
         id = 0;
         id = 0;
         int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
         int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
         if (err) {
         if (err) {
-            LFS_TRACE("lfs_getattr -> %"PRId32, err);
+            LFS_TRACE("lfs_getattr -> %d", err);
             return err;
             return err;
         }
         }
     }
     }
@@ -3307,7 +3321,7 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
             buffer);
             buffer);
     if (tag < 0) {
     if (tag < 0) {
         if (tag == LFS_ERR_NOENT) {
         if (tag == LFS_ERR_NOENT) {
-            LFS_TRACE("lfs_getattr -> %"PRId32, LFS_ERR_NOATTR);
+            LFS_TRACE("lfs_getattr -> %d", LFS_ERR_NOATTR);
             return LFS_ERR_NOATTR;
             return LFS_ERR_NOATTR;
         }
         }
 
 
@@ -3763,7 +3777,7 @@ int lfs_fs_traverse(lfs_t *lfs,
                 if (tag == LFS_ERR_NOENT) {
                 if (tag == LFS_ERR_NOENT) {
                     continue;
                     continue;
                 }
                 }
-                LFS_TRACE("lfs_fs_traverse -> %d", tag);
+                LFS_TRACE("lfs_fs_traverse -> %"PRId32, tag);
                 return tag;
                 return tag;
             }
             }
             lfs_ctz_fromle32(&ctz);
             lfs_ctz_fromle32(&ctz);
@@ -3887,6 +3901,12 @@ static int lfs_fs_relocate(lfs_t *lfs,
             d->m.pair[0] = newpair[0];
             d->m.pair[0] = newpair[0];
             d->m.pair[1] = newpair[1];
             d->m.pair[1] = newpair[1];
         }
         }
+
+        if (d->type == LFS_TYPE_DIR &&
+                lfs_pair_cmp(oldpair, ((lfs_dir_t*)d)->head) == 0) {
+            ((lfs_dir_t*)d)->head[0] = newpair[0];
+            ((lfs_dir_t*)d)->head[1] = newpair[1];
+        }
     }
     }
 
 
     // find parent
     // find parent
@@ -4072,11 +4092,11 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs) {
     lfs_size_t size = 0;
     lfs_size_t size = 0;
     int err = lfs_fs_traverse(lfs, lfs_fs_size_count, &size);
     int err = lfs_fs_traverse(lfs, lfs_fs_size_count, &size);
     if (err) {
     if (err) {
-        LFS_TRACE("lfs_fs_size -> %"PRId32, err);
+        LFS_TRACE("lfs_fs_size -> %d", err);
         return err;
         return err;
     }
     }
 
 
-    LFS_TRACE("lfs_fs_size -> %"PRId32, err);
+    LFS_TRACE("lfs_fs_size -> %d", err);
     return size;
     return size;
 }
 }
 
 
@@ -4614,7 +4634,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
                             id, entry1.d.nlen), name},
                             id, entry1.d.nlen), name},
                         {LFS_MKTAG(
                         {LFS_MKTAG(
                             isdir ? LFS_TYPE_DIRSTRUCT : LFS_TYPE_CTZSTRUCT,
                             isdir ? LFS_TYPE_DIRSTRUCT : LFS_TYPE_CTZSTRUCT,
-                            id, sizeof(&entry1.d.u)), &entry1.d.u}));
+                            id, sizeof(entry1.d.u)), &entry1.d.u}));
                 lfs1_entry_fromle32(&entry1.d);
                 lfs1_entry_fromle32(&entry1.d);
                 if (err) {
                 if (err) {
                     goto cleanup;
                     goto cleanup;
@@ -4637,7 +4657,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
 
 
                 lfs_pair_tole32(dir2.pair);
                 lfs_pair_tole32(dir2.pair);
                 err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS(
                 err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS(
-                        {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 0),
+                        {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8),
                             dir1.d.tail}));
                             dir1.d.tail}));
                 lfs_pair_fromle32(dir2.pair);
                 lfs_pair_fromle32(dir2.pair);
                 if (err) {
                 if (err) {

+ 10 - 0
tests/test_alloc.sh

@@ -250,6 +250,14 @@ scripts/test.py << TEST
     lfs_unmount(&lfs) => 0;
     lfs_unmount(&lfs) => 0;
 TEST
 TEST
 
 
+## Below, these tests depend _very_ heavily on the geometry of the
+## block device being tested, they should be removed and replaced
+## by generalized tests. For now we'll just skip if the geometry
+## is customized.
+
+if [[ ! $MAKEFLAGS =~ "LFS_BLOCK_CYCLES" ]]
+then
+
 echo "--- Chained dir exhaustion test ---"
 echo "--- Chained dir exhaustion test ---"
 scripts/test.py << TEST
 scripts/test.py << TEST
     lfs_mount(&lfs, &cfg) => 0;
     lfs_mount(&lfs, &cfg) => 0;
@@ -481,4 +489,6 @@ scripts/test.py << TEST
     lfs_unmount(&lfs) => 0;
     lfs_unmount(&lfs) => 0;
 TEST
 TEST
 
 
+fi
+
 scripts/results.py
 scripts/results.py

+ 139 - 0
tests/test_relocations.sh

@@ -0,0 +1,139 @@
+#!/bin/bash
+set -eu
+export TEST_FILE=$0
+trap 'export TEST_LINE=$LINENO' DEBUG
+
+ITERATIONS=20
+COUNT=10
+
+echo "=== Relocation tests ==="
+rm -rf blocks
+scripts/test.py << TEST
+    lfs_format(&lfs, &cfg) => 0;
+    // fill up filesystem so only ~16 blocks are left
+    lfs_mount(&lfs, &cfg) => 0;
+    lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0;
+    memset(buffer, 0, 512);
+    while (LFS_BLOCK_COUNT - lfs_fs_size(&lfs) > 16) {
+        lfs_file_write(&lfs, &file, buffer, 512) => 512;
+    }
+    lfs_file_close(&lfs, &file) => 0;
+    // make a child dir to use in bounded space
+    lfs_mkdir(&lfs, "child") => 0;
+    lfs_unmount(&lfs) => 0;
+TEST
+
+echo "--- Dangling split dir test ---"
+scripts/test.py << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+    for (int j = 0; j < $ITERATIONS; j++) {
+        for (int i = 0; i < $COUNT; i++) {
+            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
+            lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
+            lfs_file_close(&lfs, &file) => 0;
+        }
+
+        lfs_dir_open(&lfs, &dir, "child") => 0;
+        lfs_dir_read(&lfs, &dir, &info) => 1;
+        lfs_dir_read(&lfs, &dir, &info) => 1;
+        for (int i = 0; i < $COUNT; i++) {
+            sprintf(path, "test%03d_loooooooooooooooooong_name", i);
+            lfs_dir_read(&lfs, &dir, &info) => 1;
+            strcmp(info.name, path) => 0;
+        }
+        lfs_dir_read(&lfs, &dir, &info) => 0;
+        lfs_dir_close(&lfs, &dir) => 0;
+
+        if (j == $ITERATIONS-1) {
+            break;
+        }
+
+        for (int i = 0; i < $COUNT; i++) {
+            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
+            lfs_remove(&lfs, path) => 0;
+        }
+    }
+    lfs_unmount(&lfs) => 0;
+TEST
+scripts/test.py << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+    lfs_dir_open(&lfs, &dir, "child") => 0;
+    lfs_dir_read(&lfs, &dir, &info) => 1;
+    lfs_dir_read(&lfs, &dir, &info) => 1;
+    for (int i = 0; i < $COUNT; i++) {
+        sprintf(path, "test%03d_loooooooooooooooooong_name", i);
+        lfs_dir_read(&lfs, &dir, &info) => 1;
+        strcmp(info.name, path) => 0;
+    }
+    lfs_dir_read(&lfs, &dir, &info) => 0;
+    lfs_dir_close(&lfs, &dir) => 0;
+    for (int i = 0; i < $COUNT; i++) {
+        sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
+        lfs_remove(&lfs, path) => 0;
+    }
+    lfs_unmount(&lfs) => 0;
+TEST
+
+echo "--- Outdated head test ---"
+scripts/test.py << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+    for (int j = 0; j < $ITERATIONS; j++) {
+        for (int i = 0; i < $COUNT; i++) {
+            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
+            lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
+            lfs_file_close(&lfs, &file) => 0;
+        }
+
+        lfs_dir_open(&lfs, &dir, "child") => 0;
+        lfs_dir_read(&lfs, &dir, &info) => 1;
+        lfs_dir_read(&lfs, &dir, &info) => 1;
+        for (int i = 0; i < $COUNT; i++) {
+            sprintf(path, "test%03d_loooooooooooooooooong_name", i);
+            lfs_dir_read(&lfs, &dir, &info) => 1;
+            strcmp(info.name, path) => 0;
+            info.size => 0;
+
+            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
+            lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
+            lfs_file_write(&lfs, &file, "hi", 2) => 2;
+            lfs_file_close(&lfs, &file) => 0;
+        }
+        lfs_dir_read(&lfs, &dir, &info) => 0;
+
+        lfs_dir_rewind(&lfs, &dir) => 0;
+        lfs_dir_read(&lfs, &dir, &info) => 1;
+        lfs_dir_read(&lfs, &dir, &info) => 1;
+        for (int i = 0; i < $COUNT; i++) {
+            sprintf(path, "test%03d_loooooooooooooooooong_name", i);
+            lfs_dir_read(&lfs, &dir, &info) => 1;
+            strcmp(info.name, path) => 0;
+            info.size => 2;
+
+            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
+            lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
+            lfs_file_write(&lfs, &file, "hi", 2) => 2;
+            lfs_file_close(&lfs, &file) => 0;
+        }
+        lfs_dir_read(&lfs, &dir, &info) => 0;
+
+        lfs_dir_rewind(&lfs, &dir) => 0;
+        lfs_dir_read(&lfs, &dir, &info) => 1;
+        lfs_dir_read(&lfs, &dir, &info) => 1;
+        for (int i = 0; i < $COUNT; i++) {
+            sprintf(path, "test%03d_loooooooooooooooooong_name", i);
+            lfs_dir_read(&lfs, &dir, &info) => 1;
+            strcmp(info.name, path) => 0;
+            info.size => 2;
+        }
+        lfs_dir_read(&lfs, &dir, &info) => 0;
+        lfs_dir_close(&lfs, &dir) => 0;
+
+        for (int i = 0; i < $COUNT; i++) {
+            sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
+            lfs_remove(&lfs, path) => 0;
+        }
+    }
+    lfs_unmount(&lfs) => 0;
+TEST
+
+scripts/results.py

+ 74 - 0
tests/test_seek.sh

@@ -428,4 +428,78 @@ scripts/test.py << TEST
 TEST
 TEST
 done
 done
 
 
+echo "--- Root seek test ---"
+./scripts/test.py << TEST
+    lfs_mount(&lfs, &cfg) => 0;
+    for (int i = 3; i < $MEDIUMSIZE; i++) {
+        sprintf(path, "hi%03d", i);
+        lfs_mkdir(&lfs, path) => 0;
+    }
+
+    lfs_dir_open(&lfs, &dir, "/") => 0;
+    for (int i = 0; i < $MEDIUMSIZE; i++) {
+        if (i == 0) {
+            sprintf(path, ".");
+        } else if (i == 1) {
+            sprintf(path, "..");
+        } else if (i == 2) {
+            sprintf(path, "hello");
+        } else {
+            sprintf(path, "hi%03d", i);
+        }
+        lfs_dir_read(&lfs, &dir, &info) => 1;
+        strcmp(path, info.name) => 0;
+    }
+    lfs_dir_read(&lfs, &dir, &info) => 0;
+    lfs_dir_close(&lfs, &dir) => 0;
+
+    for (int j = 0; j < $MEDIUMSIZE; j++) {
+        lfs_soff_t off = -1;
+
+        lfs_dir_open(&lfs, &dir, "/") => 0;
+        for (int i = 0; i < $MEDIUMSIZE; i++) {
+            if (i == 0) {
+                sprintf(path, ".");
+            } else if (i == 1) {
+                sprintf(path, "..");
+            } else if (i == 2) {
+                sprintf(path, "hello");
+            } else {
+                sprintf(path, "hi%03d", i);
+            }
+
+            if (i == j) {
+                off = lfs_dir_tell(&lfs, &dir);
+                off >= 0 => true;
+            }
+
+            lfs_dir_read(&lfs, &dir, &info) => 1;
+            strcmp(path, info.name) => 0;
+        }
+        lfs_dir_read(&lfs, &dir, &info) => 0;
+        lfs_dir_close(&lfs, &dir) => 0;
+
+        lfs_dir_open(&lfs, &dir, "/") => 0;
+        lfs_dir_seek(&lfs, &dir, off) => 0;
+        for (int i = j; i < $MEDIUMSIZE; i++) {
+            if (i == 0) {
+                sprintf(path, ".");
+            } else if (i == 1) {
+                sprintf(path, "..");
+            } else if (i == 2) {
+                sprintf(path, "hello");
+            } else {
+                sprintf(path, "hi%03d", i);
+            }
+
+            lfs_dir_read(&lfs, &dir, &info) => 1;
+            strcmp(path, info.name) => 0;
+        }
+        lfs_dir_read(&lfs, &dir, &info) => 0;
+        lfs_dir_close(&lfs, &dir) => 0;
+    }
+
+    lfs_unmount(&lfs) => 0;
+TEST
+
 scripts/results.py
 scripts/results.py