1[[case]] # orphan test 2in = "lfs.c" 3if = 'LFS_PROG_SIZE <= 0x3fe' # only works with one crc per commit 4code = ''' 5 lfs_format(&lfs, &cfg) => 0; 6 lfs_mount(&lfs, &cfg) => 0; 7 lfs_mkdir(&lfs, "parent") => 0; 8 lfs_mkdir(&lfs, "parent/orphan") => 0; 9 lfs_mkdir(&lfs, "parent/child") => 0; 10 lfs_remove(&lfs, "parent/orphan") => 0; 11 lfs_unmount(&lfs) => 0; 12 13 // corrupt the child's most recent commit, this should be the update 14 // to the linked-list entry, which should orphan the orphan. Note this 15 // makes a lot of assumptions about the remove operation. 16 lfs_mount(&lfs, &cfg) => 0; 17 lfs_dir_open(&lfs, &dir, "parent/child") => 0; 18 lfs_block_t block = dir.m.pair[0]; 19 lfs_dir_close(&lfs, &dir) => 0; 20 lfs_unmount(&lfs) => 0; 21 uint8_t bbuffer[LFS_BLOCK_SIZE]; 22 cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; 23 int off = LFS_BLOCK_SIZE-1; 24 while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { 25 off -= 1; 26 } 27 memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); 28 cfg.erase(&cfg, block) => 0; 29 cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; 30 cfg.sync(&cfg) => 0; 31 32 lfs_mount(&lfs, &cfg) => 0; 33 lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; 34 lfs_stat(&lfs, "parent/child", &info) => 0; 35 lfs_fs_size(&lfs) => 8; 36 lfs_unmount(&lfs) => 0; 37 38 lfs_mount(&lfs, &cfg) => 0; 39 lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; 40 lfs_stat(&lfs, "parent/child", &info) => 0; 41 lfs_fs_size(&lfs) => 8; 42 // this mkdir should both create a dir and deorphan, so size 43 // should be unchanged 44 lfs_mkdir(&lfs, "parent/otherchild") => 0; 45 lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; 46 lfs_stat(&lfs, "parent/child", &info) => 0; 47 lfs_stat(&lfs, "parent/otherchild", &info) => 0; 48 lfs_fs_size(&lfs) => 8; 49 lfs_unmount(&lfs) => 0; 50 51 lfs_mount(&lfs, &cfg) => 0; 52 lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; 53 lfs_stat(&lfs, "parent/child", &info) => 0; 54 lfs_stat(&lfs, "parent/otherchild", &info) => 0; 55 lfs_fs_size(&lfs) => 8; 56 lfs_unmount(&lfs) => 0; 57''' 58 59[[case]] # reentrant testing for orphans, basically just spam mkdir/remove 60reentrant = true 61# TODO fix this case, caused by non-DAG trees 62if = '!(DEPTH == 3 && LFS_CACHE_SIZE != 64)' 63define = [ 64 {FILES=6, DEPTH=1, CYCLES=20}, 65 {FILES=26, DEPTH=1, CYCLES=20}, 66 {FILES=3, DEPTH=3, CYCLES=20}, 67] 68code = ''' 69 err = lfs_mount(&lfs, &cfg); 70 if (err) { 71 lfs_format(&lfs, &cfg) => 0; 72 lfs_mount(&lfs, &cfg) => 0; 73 } 74 75 srand(1); 76 const char alpha[] = "abcdefghijklmnopqrstuvwxyz"; 77 for (int i = 0; i < CYCLES; i++) { 78 // create random path 79 char full_path[256]; 80 for (int d = 0; d < DEPTH; d++) { 81 sprintf(&full_path[2*d], "/%c", alpha[rand() % FILES]); 82 } 83 84 // if it does not exist, we create it, else we destroy 85 int res = lfs_stat(&lfs, full_path, &info); 86 if (res == LFS_ERR_NOENT) { 87 // create each directory in turn, ignore if dir already exists 88 for (int d = 0; d < DEPTH; d++) { 89 strcpy(path, full_path); 90 path[2*d+2] = '\0'; 91 err = lfs_mkdir(&lfs, path); 92 assert(!err || err == LFS_ERR_EXIST); 93 } 94 95 for (int d = 0; d < DEPTH; d++) { 96 strcpy(path, full_path); 97 path[2*d+2] = '\0'; 98 lfs_stat(&lfs, path, &info) => 0; 99 assert(strcmp(info.name, &path[2*d+1]) == 0); 100 assert(info.type == LFS_TYPE_DIR); 101 } 102 } else { 103 // is valid dir? 104 assert(strcmp(info.name, &full_path[2*(DEPTH-1)+1]) == 0); 105 assert(info.type == LFS_TYPE_DIR); 106 107 // try to delete path in reverse order, ignore if dir is not empty 108 for (int d = DEPTH-1; d >= 0; d--) { 109 strcpy(path, full_path); 110 path[2*d+2] = '\0'; 111 err = lfs_remove(&lfs, path); 112 assert(!err || err == LFS_ERR_NOTEMPTY); 113 } 114 115 lfs_stat(&lfs, full_path, &info) => LFS_ERR_NOENT; 116 } 117 } 118 lfs_unmount(&lfs) => 0; 119''' 120 121