mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			v2.3.0
			...
			test-revam
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 20b46ded5a | ||
|  | eecb06a9dc | ||
|  | 3ee291de59 | ||
|  | f7bc22937a | ||
|  | 26ed6dee7d | ||
|  | e33bef55d3 | 
							
								
								
									
										10
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -111,12 +111,14 @@ enum lfs_type { | ||||
|     LFS_TYPE_INLINESTRUCT   = 0x201, | ||||
|     LFS_TYPE_SOFTTAIL       = 0x600, | ||||
|     LFS_TYPE_HARDTAIL       = 0x601, | ||||
|     LFS_TYPE_BRANCH         = 0x681, | ||||
|     LFS_TYPE_MOVESTATE      = 0x7ff, | ||||
|  | ||||
|     // internal chip sources | ||||
|     LFS_FROM_NOOP           = 0x000, | ||||
|     LFS_FROM_MOVE           = 0x101, | ||||
|     LFS_FROM_USERATTRS      = 0x102, | ||||
|     LFS_FROM_DROP           = 0x102, | ||||
|     LFS_FROM_USERATTRS      = 0x103, | ||||
| }; | ||||
|  | ||||
| // File open flags | ||||
| @@ -310,8 +312,11 @@ typedef struct lfs_mdir { | ||||
|     uint32_t etag; | ||||
|     uint16_t count; | ||||
|     bool erased; | ||||
|     bool first; // TODO come on | ||||
|     bool split; | ||||
|     bool mustrelocate; // TODO not great either | ||||
|     lfs_block_t tail[2]; | ||||
|     lfs_block_t branch[2]; | ||||
| } lfs_mdir_t; | ||||
|  | ||||
| // littlefs directory type | ||||
| @@ -366,6 +371,9 @@ typedef struct lfs { | ||||
|     lfs_cache_t pcache; | ||||
|  | ||||
|     lfs_block_t root[2]; | ||||
|     lfs_block_t relocate_tail[2]; | ||||
|     lfs_block_t relocate_end[2]; | ||||
|     bool relocate_do_hack; // TODO fixme | ||||
|     struct lfs_mlist { | ||||
|         struct lfs_mlist *next; | ||||
|         uint16_t id; | ||||
|   | ||||
| @@ -18,9 +18,10 @@ TAG_TYPES = { | ||||
|     'ctzstruct':    (0x7ff, 0x202), | ||||
|     'inlinestruct': (0x7ff, 0x201), | ||||
|     'userattr':     (0x700, 0x300), | ||||
|     'tail':         (0x700, 0x600), | ||||
|     'tail':         (0x700, 0x600), # TODO rename these? | ||||
|     'softtail':     (0x7ff, 0x600), | ||||
|     'hardtail':     (0x7ff, 0x601), | ||||
|     'branch':       (0x7ff, 0x681), | ||||
|     'gstate':       (0x700, 0x700), | ||||
|     'movestate':    (0x7ff, 0x7ff), | ||||
|     'crc':          (0x700, 0x500), | ||||
| @@ -103,7 +104,7 @@ class Tag: | ||||
|  | ||||
|     def mkmask(self): | ||||
|         return Tag( | ||||
|             0x700 if self.isunique else 0x7ff, | ||||
|             0x780 if self.is_('tail') else 0x700 if self.isunique else 0x7ff, # TODO best way? | ||||
|             0x3ff if self.isattr else 0, | ||||
|             0) | ||||
|  | ||||
| @@ -233,8 +234,8 @@ class MetadataPair: | ||||
|  | ||||
|     def __lt__(self, other): | ||||
|         # corrupt blocks don't count | ||||
|         if not self and other: | ||||
|             return True | ||||
|         if not self or not other: | ||||
|             return bool(other) | ||||
|  | ||||
|         # use sequence arithmetic to avoid overflow | ||||
|         return not ((other.rev - self.rev) & 0x80000000) | ||||
| @@ -325,7 +326,7 @@ def main(args): | ||||
|         mdir.rev, | ||||
|         ' (was %s)' % ', '.join('%d' % m.rev for m in mdir.pair[1:]) | ||||
|         if len(mdir.pair) > 1 else '', | ||||
|         ' (corrupted)' if not mdir else '')) | ||||
|         ' (corrupted!)' if not mdir else '')) | ||||
|     if args.all: | ||||
|         mdir.dump_all(truncate=not args.no_truncate) | ||||
|     elif args.log: | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import sys | ||||
| import json | ||||
| import io | ||||
| import itertools as it | ||||
| import collections as c | ||||
| from readmdir import Tag, MetadataPair | ||||
|  | ||||
| def popc(x): | ||||
| @@ -13,7 +14,7 @@ def popc(x): | ||||
| def ctz(x): | ||||
|     return len(bin(x)) - len(bin(x).rstrip('0')) | ||||
|  | ||||
| def dumpentries(args, mdir, f): | ||||
| def dumpentries(args, mdir, mdirs, f): | ||||
|     for k, id_ in enumerate(mdir.ids): | ||||
|         name = mdir[Tag('name', id_, 0)] | ||||
|         struct_ = mdir[Tag('struct', id_, 0)] | ||||
| @@ -22,8 +23,10 @@ def dumpentries(args, mdir, f): | ||||
|             id_, name.typerepr(), | ||||
|             json.dumps(name.data.decode('utf8'))) | ||||
|         if struct_.is_('dirstruct'): | ||||
|             desc += " dir {%#x, %#x}" % struct.unpack( | ||||
|                 '<II', struct_.data[:8].ljust(8, b'\xff')) | ||||
|             pair = struct.unpack('<II', struct_.data[:8].ljust(8, b'\xff')) | ||||
|             desc += " dir {%#x, %#x}%s" % ( | ||||
|                 pair[0], pair[1], | ||||
|                 '?' if frozenset(pair) not in mdirs else '') | ||||
|         if struct_.is_('ctzstruct'): | ||||
|             desc += " ctz {%#x} size %d" % struct.unpack( | ||||
|                 '<II', struct_.data[:8].ljust(8, b'\xff')) | ||||
| @@ -92,20 +95,17 @@ def dumpentries(args, mdir, f): | ||||
|                         for c in map(chr, data[i:i+16])))) | ||||
|  | ||||
| def main(args): | ||||
|     superblock = None | ||||
|     gstate = b'\0\0\0\0\0\0\0\0\0\0\0\0' | ||||
|     mdirs = c.OrderedDict() | ||||
|     corrupted = [] | ||||
|     cycle = False | ||||
|     with open(args.disk, 'rb') as f: | ||||
|         dirs = [] | ||||
|         superblock = None | ||||
|         gstate = b'' | ||||
|         mdirs = [] | ||||
|         cycle = False | ||||
|         tail = (args.block1, args.block2) | ||||
|         hard = False | ||||
|         while True: | ||||
|             for m in it.chain((m for d in dirs for m in d), mdirs): | ||||
|                 if set(m.blocks) == set(tail): | ||||
|                     # cycle detected | ||||
|                     cycle = m.blocks | ||||
|             if cycle: | ||||
|         while tail: | ||||
|             if frozenset(tail) in mdirs: | ||||
|                 # cycle detected | ||||
|                 cycle = tail | ||||
|                 break | ||||
|  | ||||
|             # load mdir | ||||
| @@ -128,6 +128,13 @@ def main(args): | ||||
|             except KeyError: | ||||
|                 mdir.tail = None | ||||
|  | ||||
|             try: | ||||
|                 mdir.branch = mdir[Tag('branch', 0, 0)] | ||||
|                 if mdir.branch.size != 8 or mdir.branch.data == 8*b'\xff': | ||||
|                     mdir.branch = None | ||||
|             except KeyError: | ||||
|                 mdir.branch = None | ||||
|  | ||||
|             # have superblock? | ||||
|             try: | ||||
|                 nsuperblock = mdir[ | ||||
| @@ -144,41 +151,59 @@ def main(args): | ||||
|             except KeyError: | ||||
|                 pass | ||||
|  | ||||
|             # add to directories | ||||
|             mdirs.append(mdir) | ||||
|             if mdir.tail is None or not mdir.tail.is_('hardtail'): | ||||
|                 dirs.append(mdirs) | ||||
|                 mdirs = [] | ||||
|             # corrupted? | ||||
|             if not mdir: | ||||
|                 corrupted.append(mdir) | ||||
|  | ||||
|             if mdir.tail is None: | ||||
|                 break | ||||
|             # add to metadata-pairs | ||||
|             mdirs[frozenset(mdir.blocks)] = mdir | ||||
|             tail = (struct.unpack('<II', mdir.tail.data) | ||||
|                 if mdir.tail else None) | ||||
|  | ||||
|             tail = struct.unpack('<II', mdir.tail.data) | ||||
|             hard = mdir.tail.is_('hardtail') | ||||
|  | ||||
|     # find paths | ||||
|     dirtable = {} | ||||
|     for dir in dirs: | ||||
|         dirtable[frozenset(dir[0].blocks)] = dir | ||||
|  | ||||
|     pending = [("/", dirs[0])] | ||||
|     # derive paths and build directories | ||||
|     dirs = {} | ||||
|     rogue = {} | ||||
|     pending = [('/', (args.block1, args.block2))] | ||||
|     while pending: | ||||
|         path, dir = pending.pop(0) | ||||
|         for mdir in dir: | ||||
|         path, branch = pending.pop(0) | ||||
|         dir = [] | ||||
|         while branch and frozenset(branch) in mdirs: | ||||
|             mdir = mdirs[frozenset(branch)] | ||||
|             dir.append(mdir) | ||||
|  | ||||
|             for tag in mdir.tags: | ||||
|                 if tag.is_('dir'): | ||||
|                     try: | ||||
|                         npath = tag.data.decode('utf8') | ||||
|                         npath = path + '/' + tag.data.decode('utf8') | ||||
|                         npath = npath.replace('//', '/') | ||||
|                         dirstruct = mdir[Tag('dirstruct', tag.id, 0)] | ||||
|                         nblocks = struct.unpack('<II', dirstruct.data) | ||||
|                         nmdir = dirtable[frozenset(nblocks)] | ||||
|                         pending.append(((path + '/' + npath), nmdir)) | ||||
|                         npair = struct.unpack('<II', dirstruct.data) | ||||
|                         pending.append((npath, npair)) | ||||
|                     except KeyError: | ||||
|                         pass | ||||
|  | ||||
|         dir[0].path = path.replace('//', '/') | ||||
|             branch = (struct.unpack('<II', mdir.branch.data) | ||||
|                 if mdir.branch else None) | ||||
|  | ||||
|     # dump tree | ||||
|         if not dir: | ||||
|             rogue[path] = branch | ||||
|         else: | ||||
|             dirs[path] = dir | ||||
|  | ||||
|     # also find orphans | ||||
|     not_orphans = {frozenset(mdir.blocks) | ||||
|         for dir in dirs.values() | ||||
|         for mdir in dir} | ||||
|     orphans = [] | ||||
|     for pair, mdir in mdirs.items(): | ||||
|         if pair not in not_orphans: | ||||
|             if len(orphans) > 0 and (pair == frozenset( | ||||
|                     struct.unpack('<II', orphans[-1][-1].tail.data))): | ||||
|                 orphans[-1].append(mdir) | ||||
|             else: | ||||
|                 orphans.append([mdir]) | ||||
|  | ||||
|     # print littlefs + version info | ||||
|     version = ('?', '?') | ||||
|     if superblock: | ||||
|         version = tuple(reversed( | ||||
| @@ -187,24 +212,33 @@ def main(args): | ||||
|         "data (truncated, if it fits)" | ||||
|         if not any([args.no_truncate, args.tags, args.log, args.all]) else "")) | ||||
|  | ||||
|     if gstate: | ||||
|         print("gstate 0x%s" % ''.join('%02x' % c for c in gstate)) | ||||
|         tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0]) | ||||
|         blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff')) | ||||
|         if tag.size or not tag.isvalid: | ||||
|             print("  orphans >=%d" % max(tag.size, 1)) | ||||
|         if tag.type: | ||||
|             print("  move dir {%#x, %#x} id %d" % ( | ||||
|                 blocks[0], blocks[1], tag.id)) | ||||
|     # print gstate | ||||
|     badgstate = None | ||||
|     print("gstate 0x%s" % ''.join('%02x' % c for c in gstate)) | ||||
|     tag = Tag(struct.unpack('<I', gstate[0:4].ljust(4, b'\xff'))[0]) | ||||
|     blocks = struct.unpack('<II', gstate[4:4+8].ljust(8, b'\xff')) | ||||
|     if tag.size or not tag.isvalid: | ||||
|         print("  orphans >=%d" % max(tag.size, 1)) | ||||
|     if tag.type: | ||||
|         if frozenset(blocks) not in mdirs: | ||||
|             badgstate = gstate | ||||
|         print("  move dir {%#x, %#x}%s id %d" % ( | ||||
|             blocks[0], blocks[1], | ||||
|             '?' if frozenset(blocks) not in mdirs else '', | ||||
|             tag.id)) | ||||
|  | ||||
|     for i, dir in enumerate(dirs): | ||||
|         print("dir %s" % (json.dumps(dir[0].path) | ||||
|             if hasattr(dir[0], 'path') else '(orphan)')) | ||||
|     # print dir info | ||||
|     for path, dir in it.chain( | ||||
|             sorted(dirs.items()), | ||||
|             zip(it.repeat(None), orphans)): | ||||
|         print("dir %s" % json.dumps(path) if path else "orphaned") | ||||
|  | ||||
|         for j, mdir in enumerate(dir): | ||||
|             print("mdir {%#x, %#x} rev %d%s" % ( | ||||
|                 mdir.blocks[0], mdir.blocks[1], mdir.rev, | ||||
|                 ' (corrupted)' if not mdir else '')) | ||||
|             print("mdir {%#x, %#x} rev %d (was %d)%s%s" % ( | ||||
|                 mdir.blocks[0], mdir.blocks[1], mdir.rev, mdir.pair[1].rev, | ||||
|                 ' (corrupted!)' if not mdir else '', | ||||
|                 ' -> {%#x, %#x}' % struct.unpack('<II', mdir.tail.data) | ||||
|                 if mdir.tail else '')) | ||||
|  | ||||
|             f = io.StringIO() | ||||
|             if args.tags: | ||||
| @@ -214,26 +248,38 @@ def main(args): | ||||
|             elif args.all: | ||||
|                 mdir.dump_all(f, truncate=not args.no_truncate) | ||||
|             else: | ||||
|                 dumpentries(args, mdir, f) | ||||
|                 dumpentries(args, mdir, mdirs, f) | ||||
|  | ||||
|             lines = list(filter(None, f.getvalue().split('\n'))) | ||||
|             for k, line in enumerate(lines): | ||||
|                 print("%s %s" % ( | ||||
|                     ' ' if i == len(dirs)-1 and j == len(dir)-1 else | ||||
|                     ' ' if j == len(dir)-1 else | ||||
|                     'v' if k == len(lines)-1 else | ||||
|                     '.' if j == len(dir)-1 else | ||||
|                     '|', | ||||
|                     '|' if path else '.', | ||||
|                     line)) | ||||
|  | ||||
|     if cycle: | ||||
|         print("*** cycle detected! -> {%#x, %#x} ***" % (cycle[0], cycle[1])) | ||||
|     errcode = 0 | ||||
|     for mdir in corrupted: | ||||
|         errcode = errcode or 1 | ||||
|         print("*** corrupted mdir {%#x, %#x}! ***" % ( | ||||
|             mdir.blocks[0], mdir.blocks[1])) | ||||
|  | ||||
|     for path, pair in rogue.items(): | ||||
|         errcode = errcode or 2 | ||||
|         print("*** couldn't find dir %s {%#x, %#x}! ***" % ( | ||||
|             json.dumps(path), pair[0], pair[1])) | ||||
|  | ||||
|     if badgstate: | ||||
|         errcode = errcode or 3 | ||||
|         print("*** bad gstate 0x%s! ***" % | ||||
|             ''.join('%02x' % c for c in gstate)) | ||||
|  | ||||
|     if cycle: | ||||
|         return 2 | ||||
|     elif not all(mdir for dir in dirs for mdir in dir): | ||||
|         return 1 | ||||
|     else: | ||||
|         return 0; | ||||
|         errcode = errcode or 4 | ||||
|         print("*** cycle detected {%#x, %#x}! ***" % ( | ||||
|             cycle[0], cycle[1])) | ||||
|  | ||||
|     return errcode | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     import argparse | ||||
|   | ||||
| @@ -231,7 +231,7 @@ class TestCase: | ||||
|                 ncmd.extend(['-ex', 'r']) | ||||
|                 if failure.assert_: | ||||
|                     ncmd.extend(['-ex', 'up 2']) | ||||
|             elif gdb == 'start': | ||||
|             elif gdb == 'start' or isinstance(gdb, int): | ||||
|                 ncmd.extend([ | ||||
|                     '-ex', 'b %s:%d' % (self.suite.path, self.code_lineno), | ||||
|                     '-ex', 'r']) | ||||
| @@ -329,7 +329,9 @@ class ReentrantTestCase(TestCase): | ||||
|                 persist = 'noerase' | ||||
|  | ||||
|             # exact cycle we should drop into debugger? | ||||
|             if gdb and failure and failure.cycleno == cycles: | ||||
|             if gdb and failure and ( | ||||
|                     failure.cycleno == cycles or | ||||
|                     (isinstance(gdb, int) and gdb == cycles)): | ||||
|                 return super().test(gdb=gdb, persist=persist, cycles=cycles, | ||||
|                     failure=failure, **args) | ||||
|  | ||||
| @@ -760,7 +762,8 @@ if __name__ == "__main__": | ||||
|         help="Store disk image in a file.") | ||||
|     parser.add_argument('-b', '--build', action='store_true', | ||||
|         help="Only build the tests, do not execute.") | ||||
|     parser.add_argument('-g', '--gdb', choices=['init', 'start', 'assert'], | ||||
|     parser.add_argument('-g', '--gdb', metavar='{init,start,assert},CYCLE', | ||||
|         type=lambda n: n if n in {'init', 'start', 'assert'} else int(n, 0), | ||||
|         nargs='?', const='assert', | ||||
|         help="Drop into gdb on test failure.") | ||||
|     parser.add_argument('--no-internal', action='store_true', | ||||
|   | ||||
| @@ -246,6 +246,8 @@ code = ''' | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||
|         lfs_file_close(&lfs, &file) => 0; | ||||
|     } | ||||
|     // TODO rm me | ||||
|     lfs_mkdir(&lfs, "a") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
| @@ -256,6 +258,9 @@ code = ''' | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(strcmp(info.name, "..") == 0); | ||||
|     lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|     assert(strcmp(info.name, "a") == 0); | ||||
|     for (int i = 0; i < N; i++) { | ||||
|         sprintf(path, "file%03d", i); | ||||
|         lfs_dir_read(&lfs, &dir, &info) => 1; | ||||
|   | ||||
							
								
								
									
										285
									
								
								tests/test_evil.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								tests/test_evil.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,285 @@ | ||||
| # Tests for recovering from conditions which shouldn't normally | ||||
| # happen during normal operation of littlefs | ||||
|  | ||||
| # invalid pointer tests (outside of block_count) | ||||
|  | ||||
| [[case]] # invalid tail-pointer test | ||||
| define.TAIL_TYPE = ['LFS_TYPE_HARDTAIL', 'LFS_TYPE_SOFTTAIL'] | ||||
| define.INVALSET = [0x3, 0x1, 0x2] | ||||
| in = "lfs.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // change tail-pointer to invalid pointers | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), | ||||
|                 (lfs_block_t[2]){ | ||||
|                     (INVALSET & 0x1) ? 0xcccccccc : 0, | ||||
|                     (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|  | ||||
|     // test that mount fails gracefully | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
| ''' | ||||
|  | ||||
| [[case]] # invalid dir pointer test | ||||
| define.INVALSET = [0x3, 0x1, 0x2] | ||||
| in = "lfs.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     // make a dir | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "dir_here") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     // change the dir pointer to be invalid | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     // make sure id 1 == our directory | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x700, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("dir_here")), buffer) | ||||
|                 => LFS_MKTAG(LFS_TYPE_DIR, 1, strlen("dir_here")); | ||||
|     assert(memcmp((char*)buffer, "dir_here", strlen("dir_here")) == 0); | ||||
|     // change dir pointer | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, 8), | ||||
|                 (lfs_block_t[2]){ | ||||
|                     (INVALSET & 0x1) ? 0xcccccccc : 0, | ||||
|                     (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|  | ||||
|     // test that accessing our bad dir fails, note there's a number | ||||
|     // of ways to access the dir, some can fail, but some don't | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "dir_here", &info) => 0; | ||||
|     assert(strcmp(info.name, "dir_here") == 0); | ||||
|     assert(info.type == LFS_TYPE_DIR); | ||||
|  | ||||
|     lfs_dir_open(&lfs, &dir, "dir_here") => LFS_ERR_CORRUPT; | ||||
|     lfs_stat(&lfs, "dir_here/file_here", &info) => LFS_ERR_CORRUPT; | ||||
|     lfs_dir_open(&lfs, &dir, "dir_here/dir_here") => LFS_ERR_CORRUPT; | ||||
|     lfs_file_open(&lfs, &file, "dir_here/file_here", | ||||
|             LFS_O_RDONLY) => LFS_ERR_CORRUPT; | ||||
|     lfs_file_open(&lfs, &file, "dir_here/file_here", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_CORRUPT; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # invalid file pointer test | ||||
| in = "lfs.c" | ||||
| define.SIZE = [10, 1000, 100000] # faked file size | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     // make a file | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "file_here", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     // change the file pointer to be invalid | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     // make sure id 1 == our file | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x700, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer) | ||||
|                 => LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here")); | ||||
|     assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0); | ||||
|     // change file pointer | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)), | ||||
|                 &(struct lfs_ctz){0xcccccccc, lfs_tole32(SIZE)}})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|  | ||||
|     // test that accessing our bad file fails, note there's a number | ||||
|     // of ways to access the dir, some can fail, but some don't | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "file_here", &info) => 0; | ||||
|     assert(strcmp(info.name, "file_here") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     assert(info.size == SIZE); | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     // any allocs that traverse CTZ must unfortunately must fail | ||||
|     if (SIZE > 2*LFS_BLOCK_SIZE) { | ||||
|         lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| ''' | ||||
|  | ||||
| [[case]] # invalid pointer in CTZ skip-list test | ||||
| define.SIZE = ['2*LFS_BLOCK_SIZE', '3*LFS_BLOCK_SIZE', '4*LFS_BLOCK_SIZE'] | ||||
| in = "lfs.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     // make a file | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_file_open(&lfs, &file, "file_here", | ||||
|             LFS_O_WRONLY | LFS_O_CREAT) => 0; | ||||
|     for (int i = 0; i < SIZE; i++) { | ||||
|         char c = 'c'; | ||||
|         lfs_file_write(&lfs, &file, &c, 1) => 1; | ||||
|     } | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|     // change pointer in CTZ skip-list to be invalid | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     // make sure id 1 == our file and get our CTZ structure | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x700, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer) | ||||
|                 => LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here")); | ||||
|     assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0); | ||||
|     struct lfs_ctz ctz; | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x700, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_STRUCT, 1, sizeof(struct lfs_ctz)), &ctz) | ||||
|                 => LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)); | ||||
|     // rewrite block to contain bad pointer | ||||
|     uint8_t bbuffer[LFS_BLOCK_SIZE]; | ||||
|     cfg.read(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0; | ||||
|     uint32_t bad = lfs_tole32(0xcccccccc); | ||||
|     memcpy(&bbuffer[0], &bad, sizeof(bad)); | ||||
|     memcpy(&bbuffer[4], &bad, sizeof(bad)); | ||||
|     cfg.erase(&cfg, ctz.head) => 0; | ||||
|     cfg.prog(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|  | ||||
|     // test that accessing our bad file fails, note there's a number | ||||
|     // of ways to access the dir, some can fail, but some don't | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_stat(&lfs, "file_here", &info) => 0; | ||||
|     assert(strcmp(info.name, "file_here") == 0); | ||||
|     assert(info.type == LFS_TYPE_REG); | ||||
|     assert(info.size == SIZE); | ||||
|  | ||||
|     lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0; | ||||
|     lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT; | ||||
|     lfs_file_close(&lfs, &file) => 0; | ||||
|  | ||||
|     // any allocs that traverse CTZ must unfortunately must fail | ||||
|     if (SIZE > 2*LFS_BLOCK_SIZE) { | ||||
|         lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT; | ||||
|     } | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| ''' | ||||
|  | ||||
|  | ||||
| [[case]] # invalid gstate pointer | ||||
| define.INVALSET = [0x3, 0x1, 0x2] | ||||
| in = "lfs.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // create an invalid gstate | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs_fs_prepmove(&lfs, 1, (lfs_block_t [2]){ | ||||
|             (INVALSET & 0x1) ? 0xcccccccc : 0, | ||||
|             (INVALSET & 0x2) ? 0xcccccccc : 0}); | ||||
|     lfs_dir_commit(&lfs, &mdir, NULL, 0) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|  | ||||
|     // test that mount fails gracefully | ||||
|     // mount may not fail, but our first alloc should fail when | ||||
|     // we try to fix the gstate | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "should_fail") => LFS_ERR_CORRUPT; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| ''' | ||||
|  | ||||
| # cycle detection/recovery tests | ||||
|  | ||||
| [[case]] # metadata-pair threaded-list loop test | ||||
| in = "lfs.c" | ||||
| code = ''' | ||||
|     // create littlefs | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|  | ||||
|     // change tail-pointer to point to ourself | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), | ||||
|                 (lfs_block_t[2]){0, 1}})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|  | ||||
|     // test that mount fails gracefully | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
| ''' | ||||
|  | ||||
| [[case]] # metadata-pair threaded-list 2-length loop test | ||||
| in = "lfs.c" | ||||
| code = ''' | ||||
|     // create littlefs with child dir | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "child") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     // find child | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_block_t pair[2]; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x7ff, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair) | ||||
|                 => LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)); | ||||
|     // change tail-pointer to point to root | ||||
|     lfs_dir_fetch(&lfs, &mdir, pair) => 0; | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), | ||||
|                 (lfs_block_t[2]){0, 1}})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|  | ||||
|     // test that mount fails gracefully | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
| ''' | ||||
|  | ||||
| [[case]] # metadata-pair threaded-list 1-length child loop test | ||||
| in = "lfs.c" | ||||
| code = ''' | ||||
|     // create littlefs with child dir | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_mkdir(&lfs, "child") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
|  | ||||
|     // find child | ||||
|     lfs_init(&lfs, &cfg) => 0; | ||||
|     lfs_mdir_t mdir; | ||||
|     lfs_block_t pair[2]; | ||||
|     lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; | ||||
|     lfs_dir_get(&lfs, &mdir, | ||||
|             LFS_MKTAG(0x7ff, 0x3ff, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair) | ||||
|                 => LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)); | ||||
|     // change tail-pointer to point to ourself | ||||
|     lfs_dir_fetch(&lfs, &mdir, pair) => 0; | ||||
|     lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( | ||||
|             {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), pair})) => 0; | ||||
|     lfs_deinit(&lfs) => 0; | ||||
|  | ||||
|     // test that mount fails gracefully | ||||
|     lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; | ||||
| ''' | ||||
| @@ -148,7 +148,7 @@ code = ''' | ||||
|          # almost every tree operation needs a relocation | ||||
| reentrant = true | ||||
| # TODO fix this case, caused by non-DAG trees | ||||
| if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| #if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| define = [ | ||||
|     {FILES=6,  DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, | ||||
|     {FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, | ||||
| @@ -210,7 +210,7 @@ code = ''' | ||||
| [[case]] # reentrant testing for relocations, but now with random renames! | ||||
| reentrant = true | ||||
| # TODO fix this case, caused by non-DAG trees | ||||
| if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| #if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' | ||||
| define = [ | ||||
|     {FILES=6,  DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, | ||||
|     {FILES=26, DEPTH=1, CYCLES=20, LFS_BLOCK_CYCLES=1}, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user