1# simple formatting test 2[cases.test_superblocks_format] 3code = ''' 4 lfs_t lfs; 5 lfs_format(&lfs, cfg) => 0; 6''' 7 8# mount/unmount 9[cases.test_superblocks_mount] 10code = ''' 11 lfs_t lfs; 12 lfs_format(&lfs, cfg) => 0; 13 lfs_mount(&lfs, cfg) => 0; 14 lfs_unmount(&lfs) => 0; 15''' 16 17# mount/unmount from interpretting a previous superblock block_count 18[cases.test_superblocks_mount_unknown_block_count] 19code = ''' 20 lfs_t lfs; 21 lfs_format(&lfs, cfg) => 0; 22 23 memset(&lfs, 0, sizeof(lfs)); 24 struct lfs_config tweaked_cfg = *cfg; 25 tweaked_cfg.block_count = 0; 26 lfs_mount(&lfs, &tweaked_cfg) => 0; 27 assert(lfs.block_count == cfg->block_count); 28 lfs_unmount(&lfs) => 0; 29''' 30 31 32# reentrant format 33[cases.test_superblocks_reentrant_format] 34reentrant = true 35code = ''' 36 lfs_t lfs; 37 int err = lfs_mount(&lfs, cfg); 38 if (err) { 39 lfs_format(&lfs, cfg) => 0; 40 lfs_mount(&lfs, cfg) => 0; 41 } 42 lfs_unmount(&lfs) => 0; 43''' 44 45# invalid mount 46[cases.test_superblocks_invalid_mount] 47code = ''' 48 lfs_t lfs; 49 lfs_mount(&lfs, cfg) => LFS_ERR_CORRUPT; 50''' 51 52# test we can read superblock info through lfs_fs_stat 53[cases.test_superblocks_stat] 54if = 'DISK_VERSION == 0' 55code = ''' 56 lfs_t lfs; 57 lfs_format(&lfs, cfg) => 0; 58 59 // test we can mount and read fsinfo 60 lfs_mount(&lfs, cfg) => 0; 61 62 struct lfs_fsinfo fsinfo; 63 lfs_fs_stat(&lfs, &fsinfo) => 0; 64 assert(fsinfo.disk_version == LFS_DISK_VERSION); 65 assert(fsinfo.name_max == LFS_NAME_MAX); 66 assert(fsinfo.file_max == LFS_FILE_MAX); 67 assert(fsinfo.attr_max == LFS_ATTR_MAX); 68 69 lfs_unmount(&lfs) => 0; 70''' 71 72[cases.test_superblocks_stat_tweaked] 73if = 'DISK_VERSION == 0' 74defines.TWEAKED_NAME_MAX = 63 75defines.TWEAKED_FILE_MAX = '(1 << 16)-1' 76defines.TWEAKED_ATTR_MAX = 512 77code = ''' 78 // create filesystem with tweaked params 79 struct lfs_config tweaked_cfg = *cfg; 80 tweaked_cfg.name_max = TWEAKED_NAME_MAX; 81 tweaked_cfg.file_max = TWEAKED_FILE_MAX; 82 tweaked_cfg.attr_max = TWEAKED_ATTR_MAX; 83 84 lfs_t lfs; 85 lfs_format(&lfs, &tweaked_cfg) => 0; 86 87 // test we can mount and read these params with the original config 88 lfs_mount(&lfs, cfg) => 0; 89 90 struct lfs_fsinfo fsinfo; 91 lfs_fs_stat(&lfs, &fsinfo) => 0; 92 assert(fsinfo.disk_version == LFS_DISK_VERSION); 93 assert(fsinfo.name_max == TWEAKED_NAME_MAX); 94 assert(fsinfo.file_max == TWEAKED_FILE_MAX); 95 assert(fsinfo.attr_max == TWEAKED_ATTR_MAX); 96 97 lfs_unmount(&lfs) => 0; 98''' 99 100# expanding superblock 101[cases.test_superblocks_expand] 102defines.BLOCK_CYCLES = [32, 33, 1] 103defines.N = [10, 100, 1000] 104code = ''' 105 lfs_t lfs; 106 lfs_format(&lfs, cfg) => 0; 107 lfs_mount(&lfs, cfg) => 0; 108 for (int i = 0; i < N; i++) { 109 lfs_file_t file; 110 lfs_file_open(&lfs, &file, "dummy", 111 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; 112 lfs_file_close(&lfs, &file) => 0; 113 struct lfs_info info; 114 lfs_stat(&lfs, "dummy", &info) => 0; 115 assert(strcmp(info.name, "dummy") == 0); 116 assert(info.type == LFS_TYPE_REG); 117 lfs_remove(&lfs, "dummy") => 0; 118 } 119 lfs_unmount(&lfs) => 0; 120 121 // one last check after power-cycle 122 lfs_mount(&lfs, cfg) => 0; 123 lfs_file_t file; 124 lfs_file_open(&lfs, &file, "dummy", 125 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; 126 lfs_file_close(&lfs, &file) => 0; 127 struct lfs_info info; 128 lfs_stat(&lfs, "dummy", &info) => 0; 129 assert(strcmp(info.name, "dummy") == 0); 130 assert(info.type == LFS_TYPE_REG); 131 lfs_unmount(&lfs) => 0; 132''' 133 134# expanding superblock with power cycle 135[cases.test_superblocks_expand_power_cycle] 136defines.BLOCK_CYCLES = [32, 33, 1] 137defines.N = [10, 100, 1000] 138code = ''' 139 lfs_t lfs; 140 lfs_format(&lfs, cfg) => 0; 141 for (int i = 0; i < N; i++) { 142 lfs_mount(&lfs, cfg) => 0; 143 // remove lingering dummy? 144 struct lfs_info info; 145 int err = lfs_stat(&lfs, "dummy", &info); 146 assert(err == 0 || (err == LFS_ERR_NOENT && i == 0)); 147 if (!err) { 148 assert(strcmp(info.name, "dummy") == 0); 149 assert(info.type == LFS_TYPE_REG); 150 lfs_remove(&lfs, "dummy") => 0; 151 } 152 153 lfs_file_t file; 154 lfs_file_open(&lfs, &file, "dummy", 155 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; 156 lfs_file_close(&lfs, &file) => 0; 157 lfs_stat(&lfs, "dummy", &info) => 0; 158 assert(strcmp(info.name, "dummy") == 0); 159 assert(info.type == LFS_TYPE_REG); 160 lfs_unmount(&lfs) => 0; 161 } 162 163 // one last check after power-cycle 164 lfs_mount(&lfs, cfg) => 0; 165 struct lfs_info info; 166 lfs_stat(&lfs, "dummy", &info) => 0; 167 assert(strcmp(info.name, "dummy") == 0); 168 assert(info.type == LFS_TYPE_REG); 169 lfs_unmount(&lfs) => 0; 170''' 171 172# reentrant expanding superblock 173[cases.test_superblocks_reentrant_expand] 174defines.BLOCK_CYCLES = [2, 1] 175defines.N = 24 176reentrant = true 177code = ''' 178 lfs_t lfs; 179 int err = lfs_mount(&lfs, cfg); 180 if (err) { 181 lfs_format(&lfs, cfg) => 0; 182 lfs_mount(&lfs, cfg) => 0; 183 } 184 185 for (int i = 0; i < N; i++) { 186 // remove lingering dummy? 187 struct lfs_info info; 188 err = lfs_stat(&lfs, "dummy", &info); 189 assert(err == 0 || (err == LFS_ERR_NOENT && i == 0)); 190 if (!err) { 191 assert(strcmp(info.name, "dummy") == 0); 192 assert(info.type == LFS_TYPE_REG); 193 lfs_remove(&lfs, "dummy") => 0; 194 } 195 196 lfs_file_t file; 197 lfs_file_open(&lfs, &file, "dummy", 198 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; 199 lfs_file_close(&lfs, &file) => 0; 200 lfs_stat(&lfs, "dummy", &info) => 0; 201 assert(strcmp(info.name, "dummy") == 0); 202 assert(info.type == LFS_TYPE_REG); 203 } 204 205 lfs_unmount(&lfs) => 0; 206 207 // one last check after power-cycle 208 lfs_mount(&lfs, cfg) => 0; 209 struct lfs_info info; 210 lfs_stat(&lfs, "dummy", &info) => 0; 211 assert(strcmp(info.name, "dummy") == 0); 212 assert(info.type == LFS_TYPE_REG); 213 lfs_unmount(&lfs) => 0; 214''' 215 216# mount with unknown block_count 217[cases.test_superblocks_unknown_blocks] 218code = ''' 219 lfs_t lfs; 220 lfs_format(&lfs, cfg) => 0; 221 222 // known block_size/block_count 223 cfg->block_size = BLOCK_SIZE; 224 cfg->block_count = BLOCK_COUNT; 225 lfs_mount(&lfs, cfg) => 0; 226 struct lfs_fsinfo fsinfo; 227 lfs_fs_stat(&lfs, &fsinfo) => 0; 228 assert(fsinfo.block_size == BLOCK_SIZE); 229 assert(fsinfo.block_count == BLOCK_COUNT); 230 lfs_unmount(&lfs) => 0; 231 232 // unknown block_count 233 cfg->block_size = BLOCK_SIZE; 234 cfg->block_count = 0; 235 lfs_mount(&lfs, cfg) => 0; 236 lfs_fs_stat(&lfs, &fsinfo) => 0; 237 assert(fsinfo.block_size == BLOCK_SIZE); 238 assert(fsinfo.block_count == BLOCK_COUNT); 239 lfs_unmount(&lfs) => 0; 240 241 // do some work 242 lfs_mount(&lfs, cfg) => 0; 243 lfs_fs_stat(&lfs, &fsinfo) => 0; 244 assert(fsinfo.block_size == BLOCK_SIZE); 245 assert(fsinfo.block_count == BLOCK_COUNT); 246 lfs_file_t file; 247 lfs_file_open(&lfs, &file, "test", 248 LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0; 249 lfs_file_write(&lfs, &file, "hello!", 6) => 6; 250 lfs_file_close(&lfs, &file) => 0; 251 lfs_unmount(&lfs) => 0; 252 253 lfs_mount(&lfs, cfg) => 0; 254 lfs_fs_stat(&lfs, &fsinfo) => 0; 255 assert(fsinfo.block_size == BLOCK_SIZE); 256 assert(fsinfo.block_count == BLOCK_COUNT); 257 lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; 258 uint8_t buffer[256]; 259 lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6; 260 lfs_file_close(&lfs, &file) => 0; 261 assert(memcmp(buffer, "hello!", 6) == 0); 262 lfs_unmount(&lfs) => 0; 263''' 264 265# mount with blocks fewer than the erase_count 266[cases.test_superblocks_fewer_blocks] 267defines.BLOCK_COUNT = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2'] 268code = ''' 269 lfs_t lfs; 270 lfs_format(&lfs, cfg) => 0; 271 272 // known block_size/block_count 273 cfg->block_size = BLOCK_SIZE; 274 cfg->block_count = BLOCK_COUNT; 275 lfs_mount(&lfs, cfg) => 0; 276 struct lfs_fsinfo fsinfo; 277 lfs_fs_stat(&lfs, &fsinfo) => 0; 278 assert(fsinfo.block_size == BLOCK_SIZE); 279 assert(fsinfo.block_count == BLOCK_COUNT); 280 lfs_unmount(&lfs) => 0; 281 282 // incorrect block_count 283 cfg->block_size = BLOCK_SIZE; 284 cfg->block_count = ERASE_COUNT; 285 lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; 286 287 // unknown block_count 288 cfg->block_size = BLOCK_SIZE; 289 cfg->block_count = 0; 290 lfs_mount(&lfs, cfg) => 0; 291 lfs_fs_stat(&lfs, &fsinfo) => 0; 292 assert(fsinfo.block_size == BLOCK_SIZE); 293 assert(fsinfo.block_count == BLOCK_COUNT); 294 lfs_unmount(&lfs) => 0; 295 296 // do some work 297 lfs_mount(&lfs, cfg) => 0; 298 lfs_fs_stat(&lfs, &fsinfo) => 0; 299 assert(fsinfo.block_size == BLOCK_SIZE); 300 assert(fsinfo.block_count == BLOCK_COUNT); 301 lfs_file_t file; 302 lfs_file_open(&lfs, &file, "test", 303 LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0; 304 lfs_file_write(&lfs, &file, "hello!", 6) => 6; 305 lfs_file_close(&lfs, &file) => 0; 306 lfs_unmount(&lfs) => 0; 307 308 lfs_mount(&lfs, cfg) => 0; 309 lfs_fs_stat(&lfs, &fsinfo) => 0; 310 assert(fsinfo.block_size == BLOCK_SIZE); 311 assert(fsinfo.block_count == BLOCK_COUNT); 312 lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; 313 uint8_t buffer[256]; 314 lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6; 315 lfs_file_close(&lfs, &file) => 0; 316 assert(memcmp(buffer, "hello!", 6) == 0); 317 lfs_unmount(&lfs) => 0; 318''' 319 320# mount with more blocks than the erase_count 321[cases.test_superblocks_more_blocks] 322defines.FORMAT_BLOCK_COUNT = '2*ERASE_COUNT' 323in = 'lfs.c' 324code = ''' 325 lfs_t lfs; 326 lfs_init(&lfs, cfg) => 0; 327 lfs.block_count = BLOCK_COUNT; 328 329 lfs_mdir_t root = { 330 .pair = {0, 0}, // make sure this goes into block 0 331 .rev = 0, 332 .off = sizeof(uint32_t), 333 .etag = 0xffffffff, 334 .count = 0, 335 .tail = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}, 336 .erased = false, 337 .split = false, 338 }; 339 340 lfs_superblock_t superblock = { 341 .version = LFS_DISK_VERSION, 342 .block_size = BLOCK_SIZE, 343 .block_count = FORMAT_BLOCK_COUNT, 344 .name_max = LFS_NAME_MAX, 345 .file_max = LFS_FILE_MAX, 346 .attr_max = LFS_ATTR_MAX, 347 }; 348 349 lfs_superblock_tole32(&superblock); 350 lfs_dir_commit(&lfs, &root, LFS_MKATTRS( 351 {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, 352 {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, 353 {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), 354 &superblock})) => 0; 355 lfs_deinit(&lfs) => 0; 356 357 // known block_size/block_count 358 cfg->block_size = BLOCK_SIZE; 359 cfg->block_count = BLOCK_COUNT; 360 lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; 361''' 362 363# mount and grow the filesystem 364[cases.test_superblocks_grow] 365defines.BLOCK_COUNT = ['ERASE_COUNT/2', 'ERASE_COUNT/4', '2'] 366defines.BLOCK_COUNT_2 = 'ERASE_COUNT' 367defines.KNOWN_BLOCK_COUNT = [true, false] 368code = ''' 369 lfs_t lfs; 370 lfs_format(&lfs, cfg) => 0; 371 372 if (KNOWN_BLOCK_COUNT) { 373 cfg->block_count = BLOCK_COUNT; 374 } else { 375 cfg->block_count = 0; 376 } 377 378 // mount with block_size < erase_size 379 lfs_mount(&lfs, cfg) => 0; 380 struct lfs_fsinfo fsinfo; 381 lfs_fs_stat(&lfs, &fsinfo) => 0; 382 assert(fsinfo.block_size == BLOCK_SIZE); 383 assert(fsinfo.block_count == BLOCK_COUNT); 384 lfs_unmount(&lfs) => 0; 385 386 // same size is a noop 387 lfs_mount(&lfs, cfg) => 0; 388 lfs_fs_grow(&lfs, BLOCK_COUNT) => 0; 389 lfs_fs_stat(&lfs, &fsinfo) => 0; 390 assert(fsinfo.block_size == BLOCK_SIZE); 391 assert(fsinfo.block_count == BLOCK_COUNT); 392 lfs_unmount(&lfs) => 0; 393 394 lfs_mount(&lfs, cfg) => 0; 395 lfs_fs_stat(&lfs, &fsinfo) => 0; 396 assert(fsinfo.block_size == BLOCK_SIZE); 397 assert(fsinfo.block_count == BLOCK_COUNT); 398 lfs_unmount(&lfs) => 0; 399 400 // grow to new size 401 lfs_mount(&lfs, cfg) => 0; 402 lfs_fs_grow(&lfs, BLOCK_COUNT_2) => 0; 403 lfs_fs_stat(&lfs, &fsinfo) => 0; 404 assert(fsinfo.block_size == BLOCK_SIZE); 405 assert(fsinfo.block_count == BLOCK_COUNT_2); 406 lfs_unmount(&lfs) => 0; 407 408 if (KNOWN_BLOCK_COUNT) { 409 cfg->block_count = BLOCK_COUNT_2; 410 } else { 411 cfg->block_count = 0; 412 } 413 414 lfs_mount(&lfs, cfg) => 0; 415 lfs_fs_stat(&lfs, &fsinfo) => 0; 416 assert(fsinfo.block_size == BLOCK_SIZE); 417 assert(fsinfo.block_count == BLOCK_COUNT_2); 418 lfs_unmount(&lfs) => 0; 419 420 // mounting with the previous size should fail 421 cfg->block_count = BLOCK_COUNT; 422 lfs_mount(&lfs, cfg) => LFS_ERR_INVAL; 423 424 if (KNOWN_BLOCK_COUNT) { 425 cfg->block_count = BLOCK_COUNT_2; 426 } else { 427 cfg->block_count = 0; 428 } 429 430 // same size is a noop 431 lfs_mount(&lfs, cfg) => 0; 432 lfs_fs_grow(&lfs, BLOCK_COUNT_2) => 0; 433 lfs_fs_stat(&lfs, &fsinfo) => 0; 434 assert(fsinfo.block_size == BLOCK_SIZE); 435 assert(fsinfo.block_count == BLOCK_COUNT_2); 436 lfs_unmount(&lfs) => 0; 437 438 lfs_mount(&lfs, cfg) => 0; 439 lfs_fs_stat(&lfs, &fsinfo) => 0; 440 assert(fsinfo.block_size == BLOCK_SIZE); 441 assert(fsinfo.block_count == BLOCK_COUNT_2); 442 lfs_unmount(&lfs) => 0; 443 444 // do some work 445 lfs_mount(&lfs, cfg) => 0; 446 lfs_fs_stat(&lfs, &fsinfo) => 0; 447 assert(fsinfo.block_size == BLOCK_SIZE); 448 assert(fsinfo.block_count == BLOCK_COUNT_2); 449 lfs_file_t file; 450 lfs_file_open(&lfs, &file, "test", 451 LFS_O_CREAT | LFS_O_EXCL | LFS_O_WRONLY) => 0; 452 lfs_file_write(&lfs, &file, "hello!", 6) => 6; 453 lfs_file_close(&lfs, &file) => 0; 454 lfs_unmount(&lfs) => 0; 455 456 lfs_mount(&lfs, cfg) => 0; 457 lfs_fs_stat(&lfs, &fsinfo) => 0; 458 assert(fsinfo.block_size == BLOCK_SIZE); 459 assert(fsinfo.block_count == BLOCK_COUNT_2); 460 lfs_file_open(&lfs, &file, "test", LFS_O_RDONLY) => 0; 461 uint8_t buffer[256]; 462 lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => 6; 463 lfs_file_close(&lfs, &file) => 0; 464 assert(memcmp(buffer, "hello!", 6) == 0); 465 lfs_unmount(&lfs) => 0; 466''' 467