1# Tests for recovering from conditions which shouldn't normally 2# happen during normal operation of littlefs 3 4# invalid pointer tests (outside of block_count) 5 6[[case]] # invalid tail-pointer test 7define.TAIL_TYPE = ['LFS_TYPE_HARDTAIL', 'LFS_TYPE_SOFTTAIL'] 8define.INVALSET = [0x3, 0x1, 0x2] 9in = "lfs.c" 10code = ''' 11 // create littlefs 12 lfs_format(&lfs, &cfg) => 0; 13 14 // change tail-pointer to invalid pointers 15 lfs_init(&lfs, &cfg) => 0; 16 lfs_mdir_t mdir; 17 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 18 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 19 {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), 20 (lfs_block_t[2]){ 21 (INVALSET & 0x1) ? 0xcccccccc : 0, 22 (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0; 23 lfs_deinit(&lfs) => 0; 24 25 // test that mount fails gracefully 26 lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; 27''' 28 29[[case]] # invalid dir pointer test 30define.INVALSET = [0x3, 0x1, 0x2] 31in = "lfs.c" 32code = ''' 33 // create littlefs 34 lfs_format(&lfs, &cfg) => 0; 35 // make a dir 36 lfs_mount(&lfs, &cfg) => 0; 37 lfs_mkdir(&lfs, "dir_here") => 0; 38 lfs_unmount(&lfs) => 0; 39 40 // change the dir pointer to be invalid 41 lfs_init(&lfs, &cfg) => 0; 42 lfs_mdir_t mdir; 43 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 44 // make sure id 1 == our directory 45 lfs_dir_get(&lfs, &mdir, 46 LFS_MKTAG(0x700, 0x3ff, 0), 47 LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("dir_here")), buffer) 48 => LFS_MKTAG(LFS_TYPE_DIR, 1, strlen("dir_here")); 49 assert(memcmp((char*)buffer, "dir_here", strlen("dir_here")) == 0); 50 // change dir pointer 51 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 52 {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, 8), 53 (lfs_block_t[2]){ 54 (INVALSET & 0x1) ? 0xcccccccc : 0, 55 (INVALSET & 0x2) ? 0xcccccccc : 0}})) => 0; 56 lfs_deinit(&lfs) => 0; 57 58 // test that accessing our bad dir fails, note there's a number 59 // of ways to access the dir, some can fail, but some don't 60 lfs_mount(&lfs, &cfg) => 0; 61 lfs_stat(&lfs, "dir_here", &info) => 0; 62 assert(strcmp(info.name, "dir_here") == 0); 63 assert(info.type == LFS_TYPE_DIR); 64 65 lfs_dir_open(&lfs, &dir, "dir_here") => LFS_ERR_CORRUPT; 66 lfs_stat(&lfs, "dir_here/file_here", &info) => LFS_ERR_CORRUPT; 67 lfs_dir_open(&lfs, &dir, "dir_here/dir_here") => LFS_ERR_CORRUPT; 68 lfs_file_open(&lfs, &file, "dir_here/file_here", 69 LFS_O_RDONLY) => LFS_ERR_CORRUPT; 70 lfs_file_open(&lfs, &file, "dir_here/file_here", 71 LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_CORRUPT; 72 lfs_unmount(&lfs) => 0; 73''' 74 75[[case]] # invalid file pointer test 76in = "lfs.c" 77define.SIZE = [10, 1000, 100000] # faked file size 78code = ''' 79 // create littlefs 80 lfs_format(&lfs, &cfg) => 0; 81 // make a file 82 lfs_mount(&lfs, &cfg) => 0; 83 lfs_file_open(&lfs, &file, "file_here", 84 LFS_O_WRONLY | LFS_O_CREAT) => 0; 85 lfs_file_close(&lfs, &file) => 0; 86 lfs_unmount(&lfs) => 0; 87 88 // change the file pointer to be invalid 89 lfs_init(&lfs, &cfg) => 0; 90 lfs_mdir_t mdir; 91 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 92 // make sure id 1 == our file 93 lfs_dir_get(&lfs, &mdir, 94 LFS_MKTAG(0x700, 0x3ff, 0), 95 LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer) 96 => LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here")); 97 assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0); 98 // change file pointer 99 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 100 {LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)), 101 &(struct lfs_ctz){0xcccccccc, lfs_tole32(SIZE)}})) => 0; 102 lfs_deinit(&lfs) => 0; 103 104 // test that accessing our bad file fails, note there's a number 105 // of ways to access the dir, some can fail, but some don't 106 lfs_mount(&lfs, &cfg) => 0; 107 lfs_stat(&lfs, "file_here", &info) => 0; 108 assert(strcmp(info.name, "file_here") == 0); 109 assert(info.type == LFS_TYPE_REG); 110 assert(info.size == SIZE); 111 112 lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0; 113 lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT; 114 lfs_file_close(&lfs, &file) => 0; 115 116 // any allocs that traverse CTZ must unfortunately must fail 117 if (SIZE > 2*LFS_BLOCK_SIZE) { 118 lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT; 119 } 120 lfs_unmount(&lfs) => 0; 121''' 122 123[[case]] # invalid pointer in CTZ skip-list test 124define.SIZE = ['2*LFS_BLOCK_SIZE', '3*LFS_BLOCK_SIZE', '4*LFS_BLOCK_SIZE'] 125in = "lfs.c" 126code = ''' 127 // create littlefs 128 lfs_format(&lfs, &cfg) => 0; 129 // make a file 130 lfs_mount(&lfs, &cfg) => 0; 131 lfs_file_open(&lfs, &file, "file_here", 132 LFS_O_WRONLY | LFS_O_CREAT) => 0; 133 for (int i = 0; i < SIZE; i++) { 134 char c = 'c'; 135 lfs_file_write(&lfs, &file, &c, 1) => 1; 136 } 137 lfs_file_close(&lfs, &file) => 0; 138 lfs_unmount(&lfs) => 0; 139 // change pointer in CTZ skip-list to be invalid 140 lfs_init(&lfs, &cfg) => 0; 141 lfs_mdir_t mdir; 142 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 143 // make sure id 1 == our file and get our CTZ structure 144 lfs_dir_get(&lfs, &mdir, 145 LFS_MKTAG(0x700, 0x3ff, 0), 146 LFS_MKTAG(LFS_TYPE_NAME, 1, strlen("file_here")), buffer) 147 => LFS_MKTAG(LFS_TYPE_REG, 1, strlen("file_here")); 148 assert(memcmp((char*)buffer, "file_here", strlen("file_here")) == 0); 149 struct lfs_ctz ctz; 150 lfs_dir_get(&lfs, &mdir, 151 LFS_MKTAG(0x700, 0x3ff, 0), 152 LFS_MKTAG(LFS_TYPE_STRUCT, 1, sizeof(struct lfs_ctz)), &ctz) 153 => LFS_MKTAG(LFS_TYPE_CTZSTRUCT, 1, sizeof(struct lfs_ctz)); 154 lfs_ctz_fromle32(&ctz); 155 // rewrite block to contain bad pointer 156 uint8_t bbuffer[LFS_BLOCK_SIZE]; 157 cfg.read(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0; 158 uint32_t bad = lfs_tole32(0xcccccccc); 159 memcpy(&bbuffer[0], &bad, sizeof(bad)); 160 memcpy(&bbuffer[4], &bad, sizeof(bad)); 161 cfg.erase(&cfg, ctz.head) => 0; 162 cfg.prog(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0; 163 lfs_deinit(&lfs) => 0; 164 165 // test that accessing our bad file fails, note there's a number 166 // of ways to access the dir, some can fail, but some don't 167 lfs_mount(&lfs, &cfg) => 0; 168 lfs_stat(&lfs, "file_here", &info) => 0; 169 assert(strcmp(info.name, "file_here") == 0); 170 assert(info.type == LFS_TYPE_REG); 171 assert(info.size == SIZE); 172 173 lfs_file_open(&lfs, &file, "file_here", LFS_O_RDONLY) => 0; 174 lfs_file_read(&lfs, &file, buffer, SIZE) => LFS_ERR_CORRUPT; 175 lfs_file_close(&lfs, &file) => 0; 176 177 // any allocs that traverse CTZ must unfortunately must fail 178 if (SIZE > 2*LFS_BLOCK_SIZE) { 179 lfs_mkdir(&lfs, "dir_here") => LFS_ERR_CORRUPT; 180 } 181 lfs_unmount(&lfs) => 0; 182''' 183 184 185[[case]] # invalid gstate pointer 186define.INVALSET = [0x3, 0x1, 0x2] 187in = "lfs.c" 188code = ''' 189 // create littlefs 190 lfs_format(&lfs, &cfg) => 0; 191 192 // create an invalid gstate 193 lfs_init(&lfs, &cfg) => 0; 194 lfs_mdir_t mdir; 195 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 196 lfs_fs_prepmove(&lfs, 1, (lfs_block_t [2]){ 197 (INVALSET & 0x1) ? 0xcccccccc : 0, 198 (INVALSET & 0x2) ? 0xcccccccc : 0}); 199 lfs_dir_commit(&lfs, &mdir, NULL, 0) => 0; 200 lfs_deinit(&lfs) => 0; 201 202 // test that mount fails gracefully 203 // mount may not fail, but our first alloc should fail when 204 // we try to fix the gstate 205 lfs_mount(&lfs, &cfg) => 0; 206 lfs_mkdir(&lfs, "should_fail") => LFS_ERR_CORRUPT; 207 lfs_unmount(&lfs) => 0; 208''' 209 210# cycle detection/recovery tests 211 212[[case]] # metadata-pair threaded-list loop test 213in = "lfs.c" 214code = ''' 215 // create littlefs 216 lfs_format(&lfs, &cfg) => 0; 217 218 // change tail-pointer to point to ourself 219 lfs_init(&lfs, &cfg) => 0; 220 lfs_mdir_t mdir; 221 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 222 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 223 {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), 224 (lfs_block_t[2]){0, 1}})) => 0; 225 lfs_deinit(&lfs) => 0; 226 227 // test that mount fails gracefully 228 lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; 229''' 230 231[[case]] # metadata-pair threaded-list 2-length loop test 232in = "lfs.c" 233code = ''' 234 // create littlefs with child dir 235 lfs_format(&lfs, &cfg) => 0; 236 lfs_mount(&lfs, &cfg) => 0; 237 lfs_mkdir(&lfs, "child") => 0; 238 lfs_unmount(&lfs) => 0; 239 240 // find child 241 lfs_init(&lfs, &cfg) => 0; 242 lfs_mdir_t mdir; 243 lfs_block_t pair[2]; 244 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 245 lfs_dir_get(&lfs, &mdir, 246 LFS_MKTAG(0x7ff, 0x3ff, 0), 247 LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair) 248 => LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)); 249 lfs_pair_fromle32(pair); 250 // change tail-pointer to point to root 251 lfs_dir_fetch(&lfs, &mdir, pair) => 0; 252 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 253 {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), 254 (lfs_block_t[2]){0, 1}})) => 0; 255 lfs_deinit(&lfs) => 0; 256 257 // test that mount fails gracefully 258 lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; 259''' 260 261[[case]] # metadata-pair threaded-list 1-length child loop test 262in = "lfs.c" 263code = ''' 264 // create littlefs with child dir 265 lfs_format(&lfs, &cfg) => 0; 266 lfs_mount(&lfs, &cfg) => 0; 267 lfs_mkdir(&lfs, "child") => 0; 268 lfs_unmount(&lfs) => 0; 269 270 // find child 271 lfs_init(&lfs, &cfg) => 0; 272 lfs_mdir_t mdir; 273 lfs_block_t pair[2]; 274 lfs_dir_fetch(&lfs, &mdir, (lfs_block_t[2]){0, 1}) => 0; 275 lfs_dir_get(&lfs, &mdir, 276 LFS_MKTAG(0x7ff, 0x3ff, 0), 277 LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)), pair) 278 => LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 1, sizeof(pair)); 279 lfs_pair_fromle32(pair); 280 // change tail-pointer to point to ourself 281 lfs_dir_fetch(&lfs, &mdir, pair) => 0; 282 lfs_dir_commit(&lfs, &mdir, LFS_MKATTRS( 283 {LFS_MKTAG(LFS_TYPE_HARDTAIL, 0x3ff, 8), pair})) => 0; 284 lfs_deinit(&lfs) => 0; 285 286 // test that mount fails gracefully 287 lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; 288''' 289