├── images ├── offsets.csv ├── ext3.img.gz ├── ext4.img.gz ├── jfs.img.gz ├── xfs.img.gz ├── btrfs.img.gz ├── exfat.img.gz ├── fat12.img.gz ├── ext3-largefile.img.gz ├── ext4-largefile.img.gz ├── jfs-largefile.img.gz ├── xfs-largefile.img.gz ├── btrfs-largefile.img.gz ├── fat12-largefile.img.gz ├── checksums.txt ├── mkfs.txt ├── test.txt └── large_test.txt ├── .gitignore ├── corrupt_images ├── btrfs.img.gz ├── btrfs.corrupt.img.gz └── RESULTS.md ├── Makefile ├── pwrite.c ├── mmap_read.c ├── inputs.csv ├── mmap_write.c ├── README.md ├── pread.c └── main.py /images/offsets.csv: -------------------------------------------------------------------------------- 1 | ext3.img.gz,3074 2 | ext4.img.gz,2390 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pread 2 | pwrite 3 | mmap_read 4 | fs-results.csv 5 | *~ 6 | -------------------------------------------------------------------------------- /images/ext3.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/ext3.img.gz -------------------------------------------------------------------------------- /images/ext4.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/ext4.img.gz -------------------------------------------------------------------------------- /images/jfs.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/jfs.img.gz -------------------------------------------------------------------------------- /images/xfs.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/xfs.img.gz -------------------------------------------------------------------------------- /images/btrfs.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/btrfs.img.gz -------------------------------------------------------------------------------- /images/exfat.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/exfat.img.gz -------------------------------------------------------------------------------- /images/fat12.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/fat12.img.gz -------------------------------------------------------------------------------- /corrupt_images/btrfs.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/corrupt_images/btrfs.img.gz -------------------------------------------------------------------------------- /images/ext3-largefile.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/ext3-largefile.img.gz -------------------------------------------------------------------------------- /images/ext4-largefile.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/ext4-largefile.img.gz -------------------------------------------------------------------------------- /images/jfs-largefile.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/jfs-largefile.img.gz -------------------------------------------------------------------------------- /images/xfs-largefile.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/xfs-largefile.img.gz -------------------------------------------------------------------------------- /images/btrfs-largefile.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/btrfs-largefile.img.gz -------------------------------------------------------------------------------- /images/fat12-largefile.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/images/fat12-largefile.img.gz -------------------------------------------------------------------------------- /images/checksums.txt: -------------------------------------------------------------------------------- 1 | 2e5358100bf5dee0a8c3a01b8c4d89c8 ext3.img 2 | deafbf8b0e316d82adad16b42975f24d ext4.img 3 | -------------------------------------------------------------------------------- /corrupt_images/btrfs.corrupt.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danluu/fs-errors/HEAD/corrupt_images/btrfs.corrupt.img.gz -------------------------------------------------------------------------------- /corrupt_images/RESULTS.md: -------------------------------------------------------------------------------- 1 | Btrfs corruption test: 2 | 3 | ``` 4 | cat /mnt/test/test.txt 5 | cat: /mnt/test/test.txt: Input/output error 6 | ``` 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | mmap_read: mmap_read.c 2 | gcc -o mmap_read mmap_read.c -I. 3 | 4 | mmap_write: mmap_write.c 5 | gcc -o mmap_write mmap_write.c -I. 6 | 7 | pread: pread.c 8 | gcc -o pread pread.c -I. 9 | 10 | pwrite: pwrite.c 11 | gcc -o pwrite pwrite.c -I. 12 | 13 | all: pread pwrite mmap_read mmap_write 14 | -------------------------------------------------------------------------------- /images/mkfs.txt: -------------------------------------------------------------------------------- 1 | mke2fs 1.43.4 (31-Jan-2017) 2 | Discarding device blocks: done 3 | Creating filesystem with 10240 1k blocks and 2560 inodes 4 | Filesystem UUID: 2a450cf8-eb68-4d03-b1dd-ef7b094ddc01 5 | Superblock backups stored on blocks: 6 | 8193 7 | 8 | Allocating group tables: done 9 | Writing inode tables: done 10 | Creating journal (1024 blocks): done 11 | Writing superblocks and filesystem accounting information: done 12 | -------------------------------------------------------------------------------- /images/test.txt: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi vel odio id turpis euismod suscipit ac finibus massa. Suspendisse lorem risus, malesuada mattis sodales id, placerat tincidunt erat. Aenean vitae lectus tellus. Vivamus ac enim lobortis, dignissim mauris sed, accumsan leo. Nullam ac erat nec justo consequat sollicitudin non a mauris. In ac pharetra ante. Aenean eget ultrices nisi. Donec pulvinar urna tincidunt bibendum pharetra. 2 | -------------------------------------------------------------------------------- /pwrite.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char *argv[]) { 10 | if (argc != 2) { 11 | printf("Expected 1 argument (filename), found %d\n", argc-1); 12 | return 1; 13 | } 14 | 15 | char buf[1024] = {0}; 16 | 17 | int fd = open(argv[1], O_WRONLY); 18 | if (fd < 0) { 19 | printf("open fail %s\n", strerror(errno)); 20 | return fd; 21 | } 22 | 23 | ssize_t rcode = pwrite(fd, &buf, 100, 0); 24 | asm volatile("" :: "m" (buf[0])); 25 | if (rcode < 0) { 26 | printf("write fail. errno: %s\n", strerror(errno)); 27 | return rcode; 28 | } else { 29 | return 0; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mmap_read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define SIZE 445 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | const char *memblock; 15 | int fd; 16 | 17 | if (argc != 2) { 18 | printf("Expected 1 argument (filename), found %d\n", argc-1); 19 | return 1; 20 | } 21 | 22 | fd = open(argv[1], O_RDONLY); 23 | if (fd < 0) { 24 | printf("open fail %s\n", strerror(errno)); 25 | return fd; 26 | } 27 | 28 | memblock = mmap(NULL, SIZE, PROT_WRITE, MAP_PRIVATE, fd, 0); 29 | if (memblock == MAP_FAILED) { 30 | perror("mmap"); 31 | return 2; 32 | } 33 | 34 | char c; 35 | for(uint64_t i = 0; i < SIZE; i++) 36 | { 37 | c = memblock[i]; 38 | asm volatile("" :: "m" (c)); 39 | // printf("%c", memblock[i]); 40 | } 41 | printf("%c", memblock[0]); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /inputs.csv: -------------------------------------------------------------------------------- 1 | image,offset,md5sum 2 | images/btrfs.img.gz,74078,58bada4cc010fa9632c66305464be039 3 | images/btrfs.img.gz,74142,58bada4cc010fa9632c66305464be039 4 | images/btrfs.img.gz,139614,58bada4cc010fa9632c66305464be039 5 | images/btrfs.img.gz,139678,58bada4cc010fa9632c66305464be039 6 | images/ext3.img.gz,3074,2e5358100bf5dee0a8c3a01b8c4d89c8 7 | images/ext4.img.gz,2390,deafbf8b0e316d82adad16b42975f24d 8 | images/exfat.img.gz,168,646b47badfa913ba32513e91dad32e92 9 | images/fat12.img.gz,49,58d3068fd2e814b42317d6304bb27100 10 | images/jfs.img.gz,280,a35d663ad92ad48819315d49c817b241 11 | images/xfs.img.gz,6976,39df40b11662be53c94f195cef9f3285 12 | images/btrfs-largefile.img.gz,24576,cc373cf71b96728374912e056cc4c065 13 | images/ext3-largefile.img.gz,76,d3dc8738942a7664e38391e3fb91f789 14 | images/ext4-largefile.img.gz,7778,2717e3c9509fc662ae67ebf5293e1c97 15 | images/fat12-largefile.img.gz,49,0559b8f3bf655a840f576b9157de2690 16 | images/jfs-largefile.img.gz,360,e2b14f7b4196fc74026b4b3c72f48ee4 17 | images/xfs-largefile.img.gz,6976,1d49afb6385df87742d8766d7c657b6c -------------------------------------------------------------------------------- /mmap_write.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define SIZE 445 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | char *memblock; 15 | int fd; 16 | 17 | if (argc != 2) { 18 | printf("Expected 1 argument (filename), found %d\n", argc-1); 19 | return 1; 20 | } 21 | 22 | fd = open(argv[1], O_RDONLY); 23 | if (fd < 0) { 24 | printf("open fail %s\n", strerror(errno)); 25 | return fd; 26 | } 27 | 28 | memblock = mmap(NULL, SIZE, PROT_WRITE, MAP_PRIVATE, fd, 0); 29 | if (memblock == MAP_FAILED) { 30 | perror("mmap"); 31 | return 2; 32 | } 33 | 34 | memblock[0] = '\0'; 35 | // Without use of this call there is no guarantee that changes are 36 | // written back before munmap(2) is called 37 | if (msync(memblock, 1, MS_SYNC)) { 38 | perror("msync"); 39 | return 3; 40 | } 41 | 42 | if (munmap(memblock, SIZE)) { 43 | perror("munmap"); 44 | return 4; 45 | } 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fs-errors 2 | 3 | This repo contains tooling for injecting errors into filesystems using device-mapper. 4 | 5 | To run: 6 | ~~~ 7 | make all 8 | sudo python3 main.py 9 | ~~~ 10 | 11 | ## Making files 12 | 13 | ~~~ 14 | dd if=/dev/zero of=filesystem.img bs=1M count=1 15 | mkfs. filesystem.img 16 | ~~~ 17 | 18 | ## Installation 19 | 20 | ~~~ 21 | sudo apt install dmsetup 22 | ~~~ 23 | 24 | Requires btrfs and exfat drivers to be installed. 25 | 26 | ## Notes to self 27 | 28 | Procedure to get error offset: 29 | 30 | Each filesystem contains `test.txt`. `test.txt` contains Lorem ipsum text (see images/text.txt). 31 | 32 | In the case of the -largefile images, `test.txt` contains the letters of the alphabet repeated enough to make 8K of text. See `images/large_test.txt`. 33 | 34 | To find the error offset for a new filesystem, you can open the file up in a hex editor, find the byte offset, and divide the byte offset by 512. 35 | 36 | This error injection mechanism seems similar or possibly equivalent to the mechanism used in the IRON file systems paper. 37 | 38 | md5sum correctly returns an error code of 1 if the file is corrupt in btrfs (and prints md5sum: /mnt/test/test.txt: Input/output error). 39 | -------------------------------------------------------------------------------- /pread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define SIZE 7999 10 | 11 | // #define DEBUG_JUNK 1 12 | 13 | int main(int argc, char *argv[]) { 14 | int error_seen = 0; 15 | 16 | if (argc != 2) { 17 | printf("Expected 1 argument (filename), found %d\n", argc-1); 18 | return 1; 19 | } 20 | 21 | // char buf[SIZE]; 22 | char c; 23 | 24 | int fd = open(argv[1], O_RDONLY); 25 | if (fd < 0) { 26 | printf("open fail %s\n", strerror(errno)); 27 | return fd; 28 | } 29 | 30 | int saved_errno; 31 | ssize_t saved_rcode; 32 | char saved_char; 33 | for (int i = 0; i < SIZE; ++i) { 34 | ssize_t rcode = pread(fd, &c, 1, i); 35 | asm volatile("" :: "m" (c)); 36 | if (i == 0) { 37 | saved_char = c; 38 | } 39 | if (rcode < 0) { 40 | error_seen = 1; 41 | saved_errno = errno; 42 | saved_rcode = rcode; 43 | // printf("%d,%zd,%c,%c\n", i, rcode, expect, c); 44 | } 45 | 46 | #ifdef DEBUG_JUNK 47 | char expect = (i % 16) + 'a'; 48 | if (expect != c) { 49 | printf("%d,%zd,%c,%c\n", i, rcode, expect, c); 50 | error_seen = 1; 51 | } 52 | #endif 53 | } 54 | 55 | // TODO: consider tracking more than one error. 56 | if (error_seen) { 57 | printf("read fail. errno: %s\n", strerror(saved_errno)); 58 | return saved_rcode; 59 | } else { 60 | printf("%c\n", saved_char); 61 | return 0; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /images/large_test.txt: -------------------------------------------------------------------------------- 1 | abcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnop 2 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import csv 4 | import hashlib 5 | import shutil 6 | import subprocess 7 | import tempfile 8 | import time 9 | import os 10 | 11 | def exec_command(command, exit_on_error=True): 12 | print(' '.join(command)) 13 | command_result = subprocess.run(command, 14 | stdout=subprocess.PIPE, 15 | stderr=subprocess.PIPE) 16 | if command_result.returncode != 0 and exit_on_error: 17 | print("Error running {}".format(command)) 18 | print(command_result.stderr) 19 | exit(1) 20 | 21 | return command_result 22 | 23 | # Verify md5sum 24 | def verify_md5sum(image_path, filesystem_md5sum): 25 | if hashlib.md5(open(image_path, 'rb').read()).hexdigest() != filesystem_md5sum: 26 | print("md5sum for filesystem image does not match") 27 | exit(1) 28 | 29 | # Make copy of file 30 | # This is done so that if any of the operations on the file done in this script 31 | # are destructive they will not destroy the original file. 32 | def make_tmpfile(image_path, filesystem_md5sum): 33 | tmp_image_path = tempfile.mkstemp()[1] 34 | gzip_path = tmp_image_path + ".gz" 35 | print('cp {} {}'.format(image_path, gzip_path)) 36 | shutil.copyfile(image_path, gzip_path) 37 | 38 | gzip_command = "gunzip -f {}".format(gzip_path).split() 39 | exec_command(gzip_command) 40 | 41 | verify_md5sum(tmp_image_path, filesystem_md5sum) 42 | return tmp_image_path 43 | 44 | 45 | # make loopback device 46 | def make_loopback_device(tmp_image_path): 47 | losetup_command_1 = "losetup -f".split() 48 | losetup_result = exec_command(losetup_command_1) 49 | 50 | loopback_name = losetup_result.stdout.strip().decode('utf-8') 51 | 52 | # Possible TOCTOU issue here but it's basically impossible to avoid while using 53 | # the losetup command line tool 54 | 55 | losetup_command_2 = ["losetup", 56 | loopback_name, 57 | tmp_image_path] 58 | exec_command(losetup_command_2) 59 | 60 | return loopback_name 61 | 62 | # Find device size (in sectors) 63 | def get_device_size(loopback_name): 64 | device_size_result = exec_command(["blockdev", 65 | "--getsize", 66 | loopback_name]) 67 | 68 | device_size = int(device_size_result.stdout.strip()) 69 | return device_size 70 | 71 | 72 | # Calculate dmsetup table 73 | def get_dmsetup_table(device_size, loop_name, error_block, do_corruption): 74 | if do_corruption: 75 | dm_table = """\ 76 | 0 {linear_end_size} linear {loop_name} 0""".format( 77 | loop_name=loop_name, 78 | linear_end_size=device_size) 79 | else: 80 | 81 | dm_table = """\ 82 | 0 {error_start} linear {loop_name} 0 83 | {error_start} {error_size} error 84 | {linear_start} {linear_end_size} linear {loop_name} {linear_start}""".format( 85 | error_start=error_block[0], 86 | loop_name=loop_name, 87 | error_size=error_block[1], 88 | linear_start=sum(error_block), 89 | linear_end_size=device_size-sum(error_block)) 90 | 91 | commented_table = '#' + '#'.join(dm_table.splitlines(True)) 92 | print(commented_table) 93 | 94 | return dm_table 95 | 96 | # Run dmsetup 97 | def run_dmsetup(dm_table): 98 | dm_volume_name = "fserror_test_{}".format(time.time()) 99 | # TODO: should work with exec_command. 100 | dm_command = ["dmsetup", 101 | "create", 102 | dm_volume_name] 103 | print(' '.join(dm_command)) 104 | dm_subprocess = subprocess.Popen(dm_command, 105 | stdout=subprocess.PIPE, 106 | stderr=subprocess.PIPE, 107 | stdin=subprocess.PIPE) 108 | dm_command_output = dm_subprocess.communicate(str.encode(dm_table)) 109 | 110 | if dm_subprocess.returncode != 0: 111 | print("Error setting up device-mapper volume") 112 | print(dm_command_output[1]) 113 | exit(1) 114 | 115 | return dm_volume_name 116 | 117 | # Mount dm-mapped device 118 | def mount_dm_device(dm_volume_name): 119 | mountpoint = "/mnt/{}/".format(dm_volume_name) 120 | os.makedirs(mountpoint, exist_ok=True) 121 | 122 | exec_command(["mount", 123 | "/dev/mapper/{}".format(dm_volume_name), 124 | mountpoint]) 125 | 126 | return mountpoint 127 | 128 | def exec_test(mountpoint, image_path, test_command, results_writer, do_corruption, do_overlay): 129 | test_file = mountpoint + "test.txt" 130 | # Run test programs 131 | # TODO: make sure binary is built. 132 | test_result = exec_command([test_command, 133 | "{}".format(test_file)], 134 | False) 135 | # TODO: use csv library 136 | # TODO: put result into output file. 137 | # print("{},{},{},{}".format(image_path, 138 | # test_result.returncode, 139 | # test_result.stdout.decode('unicode_escape').strip(), 140 | # test_result.stderr.decode('utf-8').strip())) 141 | 142 | if do_corruption: 143 | corruption_output = 'corrupt' 144 | else: 145 | corruption_output = 'error' 146 | 147 | if do_overlay: 148 | overlay_output = 'overlay' 149 | else: 150 | overlay_output = 'raw' 151 | 152 | results_writer.writerow([image_path, 153 | test_command, 154 | corruption_output, 155 | overlay_output, 156 | test_result.returncode, 157 | test_result.stdout.decode('unicode_escape').strip(), 158 | test_result.stderr.decode('utf-8').strip()]) 159 | 160 | def read_config(): 161 | input_path = 'inputs.csv' 162 | inputs = [] 163 | with open(input_path, 'r') as input_file: 164 | input_reader = csv.reader(input_file) 165 | next(input_reader, None) # skip header. 166 | for row in input_reader: 167 | inputs.append({'image': row[0], 168 | 'offset': int(row[1]), 169 | 'md5sum': row[2]}) 170 | return inputs 171 | 172 | def setup_and_run_test(config, results_writer, do_corruption, do_overlay): 173 | error_block = (config['offset'], 1) #TODO(Wesley) multi-section errors 174 | test_commands = ['./mmap_read', './mmap_write', './pread', './pwrite'] 175 | for command in test_commands: 176 | tmp_image_path = make_tmpfile(config['image'], config['md5sum']) 177 | 178 | if do_corruption: 179 | corruption_commands = [['sed', '-i', '0,/abcdef/ s//watwat/', tmp_image_path], 180 | ['sed', '-i', '0,/Lorem / s//watwat/', tmp_image_path]] 181 | 182 | for corruption_command in corruption_commands: 183 | exec_command(corruption_command) 184 | 185 | 186 | loopback_name = make_loopback_device(tmp_image_path) 187 | device_size = get_device_size(loopback_name) 188 | dm_table = get_dmsetup_table(device_size, loopback_name, error_block, do_corruption) 189 | dm_volume_name = run_dmsetup(dm_table) 190 | mountpoint = mount_dm_device(dm_volume_name) 191 | 192 | if do_overlay: 193 | overlay_upperdir = tempfile.mkdtemp() 194 | overlay_workdir = tempfile.mkdtemp() 195 | overlay_mount = tempfile.mkdtemp() + '/' 196 | overlay_command = 'mount -t overlay -o lowerdir={},upperdir={},workdir={} overlay {}'.format( 197 | mountpoint, 198 | overlay_upperdir, 199 | overlay_workdir, 200 | overlay_mount) 201 | exec_command(overlay_command.split(' ')) 202 | target_mount = overlay_mount 203 | else: 204 | target_mount = mountpoint 205 | 206 | 207 | exec_test(target_mount, config['image'], command, results_writer, do_corruption, do_overlay) 208 | 209 | # TODO: unmount, remove, etc., when an error occurs and the script terminates early. 210 | if do_overlay: 211 | exec_command(["umount", overlay_mount]) 212 | print('rm -rf {}'.format(overlay_upperdir)) 213 | shutil.rmtree(overlay_upperdir) 214 | print('rm -rf {}'.format(overlay_workdir)) 215 | shutil.rmtree(overlay_workdir) 216 | 217 | exec_command(["umount", mountpoint]) 218 | exec_command(["dmsetup", "remove", dm_volume_name]) 219 | exec_command(["losetup", "-d", loopback_name]) 220 | print('rm {}'.format(tmp_image_path)) 221 | os.remove(tmp_image_path) 222 | 223 | def run_all_tests(): 224 | results_path = 'fs-results.csv' 225 | configs = read_config() 226 | 227 | no_overlay_support = {'images/fat12.img.gz', 228 | 'images/fat12-largefile.img.gz'} 229 | 230 | with open(results_path, 'w') as results_file: 231 | for do_corruption in [False, True]: 232 | for do_overlay in [True, False]: 233 | 234 | results_writer = csv.writer(results_file) 235 | 236 | for config in configs: 237 | if do_overlay and config['image'] in no_overlay_support: 238 | print('# skipping {} due to lack of overlay support'.format( 239 | config['image'])) 240 | continue 241 | 242 | 243 | setup_and_run_test(config, 244 | results_writer, 245 | do_corruption, 246 | do_overlay) 247 | 248 | def main(): 249 | if os.geteuid() != 0: 250 | print("You must be root to use this script") 251 | exit(1) 252 | 253 | run_all_tests() 254 | 255 | main() 256 | --------------------------------------------------------------------------------