实现一个文件系统
偶然看到sysplay(印度科学研究院的一家伙的blog,实现了一个用户态的文件系统 File Systems: The Semester Project, 做个笔记。
说已经存在了很多的文件系统,但是没有最好的。每个文件系统都是针对特定的场景。
而了解系统最好的办法不是看文档,而是看代码。
所以产生了这个 模拟文件系统(simula-ting file system file) 项目 。该项目是用户态的一个文件系统。
Part 1:
通过文件模拟一个块设备,并格式化成定义的文件系统格式
首先要了解问价系统的概念,一个基本的文件系统包含两个重要的结构体:
超级块(supper block)和文件条目(file entry)
1 #ifndef SFS_DS_H 2 #define SFS_DS_H 3 4 #define SIMULA_FS_TYPE 0x13090D15 /* Magic Number for our file system */ 5 #define SIMULA_FS_BLOCK_SIZE 512 /* in bytes */ 6 #define SIMULA_FS_ENTRY_SIZE 64 /* in bytes */ 7 #define SIMULA_FS_DATA_BLOCK_CNT ((SIMULA_FS_ENTRY_SIZE - (16 + 3 * 4)) / 4) 8 9 #define SIMULA_DEFAULT_FILE ".sfsf" 10 11 typedef unsigned int uint4_t; 12 13 typedef struct sfs_super_block 14 { 15 uint4_t type; /* Magic number to identify the file system */ 16 uint4_t block_size; /* Unit of allocation */ 17 uint4_t partition_size; /* in blocks */ 18 uint4_t entry_size; /* in bytes */ 19 uint4_t entry_table_size; /* in blocks */ 20 uint4_t entry_table_block_start; /* in blocks */ 21 uint4_t entry_count; /* Total entries in the file system */ 22 uint4_t data_block_start; /* in blocks */ 23 uint4_t reserved[SIMULA_FS_BLOCK_SIZE / 4 - 8]; 24 } sfs_super_block_t; /* Making it of SIMULA_FS_BLOCK_SIZE */ 25 26 typedef struct sfs_file_entry 27 { 28 char name[16]; 29 uint4_t size; /* in bytes */ 30 uint4_t timestamp; /* Seconds since Epoch */ 31 uint4_t perms; /* Permissions for user */ 32 uint4_t blocks[SIMULA_FS_DATA_BLOCK_CNT]; 33 } sfs_file_entry_t; 34 35 #endif
执行一下代码创建sfs_ds.h
cat << EOF | sed -e "s/\\\t/\t/" | tee sfs_ds.h #ifndef SFS_DS_H #define SFS_DS_H #define SIMULA_FS_TYPE 0x13090D15 /* Magic Number for our file system */ #define SIMULA_FS_BLOCK_SIZE 512 /* in bytes */ #define SIMULA_FS_ENTRY_SIZE 64 /* in bytes */ #define SIMULA_FS_DATA_BLOCK_CNT ((SIMULA_FS_ENTRY_SIZE - (16 + 3 * 4)) / 4) #define SIMULA_DEFAULT_FILE ".sfsf" typedef unsigned int uint4_t; typedef struct sfs_super_block { \tuint4_t type; /* Magic number to identify the file system */ \tuint4_t block_size; /* Unit of allocation */ \tuint4_t partition_size; /* in blocks */ \tuint4_t entry_size; /* in bytes */ \tuint4_t entry_table_size; /* in blocks */ \tuint4_t entry_table_block_start; /* in blocks */ \tuint4_t entry_count; /* Total entries in the file system */ \tuint4_t data_block_start; /* in blocks */ \tuint4_t reserved[SIMULA_FS_BLOCK_SIZE / 4 - 8]; } sfs_super_block_t; /* Making it of SIMULA_FS_BLOCK_SIZE */ typedef struct sfs_file_entry { \tchar name[16]; \tuint4_t size; /* in bytes */ \tuint4_t timestamp; /* Seconds since Epoch */ \tuint4_t perms; /* Permissions for user */ \tuint4_t blocks[SIMULA_FS_DATA_BLOCK_CNT]; } sfs_file_entry_t; #endif EOF
这里面有很多冗余字段,都是可以通过其他字段可以计算得知。
代码实现 format_sfs.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 #include <unistd.h> 7 8 #include "sfs_ds.h" 9 10 #define SFS_ENTRY_RATIO 0.10 /* 10% of all blocks */ 11 #define SFS_ENTRY_TABLE_BLOCK_START 1 12 13 sfs_super_block_t sb = 14 { 15 .type = SIMULA_FS_TYPE, 16 .block_size = SIMULA_FS_BLOCK_SIZE, 17 .entry_size = SIMULA_FS_ENTRY_SIZE, 18 .entry_table_block_start = SFS_ENTRY_TABLE_BLOCK_START 19 }; 20 sfs_file_entry_t fe; /* All 0's */ 21 22 void write_super_block(int sfs_handle, sfs_super_block_t *sb) 23 { 24 write(sfs_handle, sb, sizeof(sfs_super_block_t)); 25 } 26 void clear_file_entries(int sfs_handle, sfs_super_block_t *sb) 27 { 28 int i; 29 30 for (i = 0; i < sb->entry_count; i++) 31 { 32 write(sfs_handle, &fe, sizeof(fe)); 33 } 34 } 35 void mark_data_blocks(int sfs_handle, sfs_super_block_t *sb) 36 { 37 char c = 0; 38 39 lseek(sfs_handle, sb->partition_size * sb->block_size - 1, SEEK_SET); 40 write(sfs_handle, &c, 1); /* To make the file size to partition size */ 41 } 42 43 int main(int argc, char *argv[]) 44 { 45 int sfs_handle; 46 47 if (argc != 2) 48 { 49 fprintf(stderr, "Usage: %s <partition size in 512-byte blocks>\n", 50 argv[0]); 51 return 1; 52 } 53 sb.partition_size = atoi(argv[1]); 54 sb.entry_table_size = sb.partition_size * SFS_ENTRY_RATIO; 55 sb.entry_count = sb.entry_table_size * sb.block_size / sb.entry_size; 56 sb.data_block_start = SFS_ENTRY_TABLE_BLOCK_START + sb.entry_table_size; 57 58 sfs_handle = creat(SIMULA_DEFAULT_FILE, 59 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 60 if (sfs_handle == -1) 61 { 62 perror("No permissions to format"); 63 return 2; 64 } 65 write_super_block(sfs_handle, &sb); 66 clear_file_entries(sfs_handle, &sb); 67 mark_data_blocks(sfs_handle, &sb); 68 close(sfs_handle); 69 return 0; 70 }
执行一下代码创建format_sfs.c
cat << EOF | sed -e "s/\\\t/\t/g" | tee format_sfs.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include "sfs_ds.h" #define SFS_ENTRY_RATIO 0.10 /* 10% of all blocks */ #define SFS_ENTRY_TABLE_BLOCK_START 1 sfs_super_block_t sb = { \t.type = SIMULA_FS_TYPE, \t.block_size = SIMULA_FS_BLOCK_SIZE, \t.entry_size = SIMULA_FS_ENTRY_SIZE, \t.entry_table_block_start = SFS_ENTRY_TABLE_BLOCK_START }; sfs_file_entry_t fe; /* All 0's */ void write_super_block(int sfs_handle, sfs_super_block_t *sb) { \twrite(sfs_handle, sb, sizeof(sfs_super_block_t)); } void clear_file_entries(int sfs_handle, sfs_super_block_t *sb) { \tint i; \tfor (i = 0; i < sb->entry_count; i++) \t{ \t\twrite(sfs_handle, &fe, sizeof(fe)); \t} } void mark_data_blocks(int sfs_handle, sfs_super_block_t *sb) { \tchar c = 0; \tlseek(sfs_handle, sb->partition_size * sb->block_size - 1, SEEK_SET); \twrite(sfs_handle, &c, 1); /* To make the file size to partition size */ } int main(int argc, char *argv[]) { \tint sfs_handle; \tif (argc != 2) \t{ \t\tfprintf(stderr, "Usage: %s <partition size in 512-byte blocks>\n", \t\t\targv[0]); \t\treturn 1; \t} \tsb.partition_size = atoi(argv[1]); \tsb.entry_table_size = sb.partition_size * SFS_ENTRY_RATIO; \tsb.entry_count = sb.entry_table_size * sb.block_size / sb.entry_size; \tsb.data_block_start = SFS_ENTRY_TABLE_BLOCK_START + sb.entry_table_size; \tsfs_handle = creat(SIMULA_DEFAULT_FILE, \t\tS_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); \tif (sfs_handle == -1) \t{ \t\tperror("No permissions to format"); \t\treturn 2; \t} \twrite_super_block(sfs_handle, &sb); \tclear_file_entries(sfs_handle, &sb); \tmark_data_blocks(sfs_handle, &sb); \tclose(sfs_handle); \treturn 0; } EOF
采用gcc模拟mkfs,生成一个.sfsf
gcc format_sfs.c -o format_sfs ./format_sfs 1024 # Partition size in blocks of 512-bytes ls -al # List the .sfsf created with a size of 512 KiBytes
Part 2:
工具browse_sfs.c,显示文件系统的信息
- quit – 退出
- list – 显示文件
- create <filename> – 创建文件
- remove <filename> – 删除文件
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <string.h> 7 #include <time.h> 8 9 #include "sfs_ds.h" 10 11 sfs_super_block_t sb; 12 13 void sfs_list(int sfs_handle) 14 { 15 int i; 16 sfs_file_entry_t fe; 17 18 lseek(sfs_handle, sb.entry_table_block_start * sb.block_size, SEEK_SET); 19 for (i = 0; i < sb.entry_count; i++) 20 { 21 read(sfs_handle, &fe, sizeof(sfs_file_entry_t)); 22 if (!fe.name[0]) continue; 23 printf("%-15s %10d bytes %c%c%c %s", 24 fe.name, fe.size, 25 fe.perms & 04 ? 'r' : '-', 26 fe.perms & 02 ? 'w' : '-', 27 fe.perms & 01 ? 'x' : '-', 28 ctime((time_t *)&fe.timestamp) 29 ); 30 } 31 } 32 void sfs_create(int sfs_handle, char *fn) 33 { 34 int i; 35 sfs_file_entry_t fe; 36 37 lseek(sfs_handle, sb.entry_table_block_start * sb.block_size, SEEK_SET); 38 for (i = 0; i < sb.entry_count; i++) 39 { 40 read(sfs_handle, &fe, sizeof(sfs_file_entry_t)); 41 if (!fe.name[0]) break; 42 if (strcmp(fe.name, fn) == 0) 43 { 44 printf("File %s already exists\n", fn); 45 return; 46 } 47 } 48 if (i == sb.entry_count) 49 { 50 printf("No entries left for : %s\n", fn); 51 return; 52 } 53 54 lseek(sfs_handle, -(off_t)(sb.entry_size), SEEK_CUR); 55 56 strncpy(fe.name, fn, 15); 57 fe.name[15] = 0; 58 fe.size = 0; 59 fe.timestamp = time(NULL); 60 fe.perms = 07; 61 for (i = 0; i < SIMULA_FS_DATA_BLOCK_CNT; i++) 62 { 63 fe.blocks[i] = 0; 64 } 65 66 write(sfs_handle, &fe, sizeof(sfs_file_entry_t)); 67 } 68 void sfs_remove(int sfs_handle, char *fn) 69 { 70 int i; 71 sfs_file_entry_t fe; 72 73 lseek(sfs_handle, sb.entry_table_block_start * sb.block_size, SEEK_SET); 74 for (i = 0; i < sb.entry_count; i++) 75 { 76 read(sfs_handle, &fe, sizeof(sfs_file_entry_t)); 77 if (!fe.name[0]) continue; 78 if (strcmp(fe.name, fn) == 0) break; 79 } 80 if (i == sb.entry_count) 81 { 82 printf("File %s doesn't exist\n", fn); 83 return; 84 } 85 86 lseek(sfs_handle, -(off_t)(sb.entry_size), SEEK_CUR); 87 88 memset(&fe, 0, sizeof(sfs_file_entry_t)); 89 write(sfs_handle, &fe, sizeof(sfs_file_entry_t)); 90 } 91 92 void browse_sfs(int sfs_handle) 93 { 94 int done; 95 char cmd[256], *fn; 96 int ret; 97 98 done = 0; 99 100 printf("Welcome to SFS Browsing Shell v1.0\n\n"); 101 printf("Block size : %d bytes\n", sb.block_size); 102 printf("Partition size : %d blocks\n", sb.partition_size); 103 printf("File entry size: %d bytes\n", sb.entry_size); 104 printf("Entry tbl size : %d blocks\n", sb.entry_table_size); 105 printf("Entry count : %d\n", sb.entry_count); 106 printf("\n"); 107 while (!done) 108 { 109 printf(" $> "); 110 ret = scanf("%[^\n]", cmd); 111 if (ret < 0) 112 { 113 done = 1; 114 printf("\n"); 115 continue; 116 } 117 else 118 { 119 getchar(); 120 if (ret == 0) continue; 121 } 122 if (strcmp(cmd, "?") == 0) 123 { 124 printf("Supported commands:\n"); 125 printf("\t?\tquit\tlist\tcreate\tremove\n"); 126 continue; 127 } 128 else if (strcmp(cmd, "quit") == 0) 129 { 130 done = 1; 131 continue; 132 } 133 else if (strcmp(cmd, "list") == 0) 134 { 135 sfs_list(sfs_handle); 136 continue; 137 } 138 else if (strncmp(cmd, "create", 6) == 0) 139 { 140 if (cmd[6] == ' ') 141 { 142 fn = cmd + 7; 143 while (*fn == ' ') fn++; 144 if (*fn != '\0') // (char) 0 145 { 146 sfs_create(sfs_handle, fn); 147 continue; 148 } 149 } 150 } 151 else if (strncmp(cmd, "remove", 6) == 0) 152 { 153 if (cmd[6] == ' ') 154 { 155 fn = cmd + 7; 156 while (*fn == ' ') fn++; 157 if (*fn != '\0') // (char) 0 158 { 159 sfs_remove(sfs_handle, fn); 160 continue; 161 } 162 } 163 } 164 printf("Unknown/Incorrect command: %s\n", cmd); 165 printf("Supported commands:\n"); 166 printf("\t?\tquit\tlist\tcreate <file>\tremove <file>\n"); 167 } 168 } 169 170 int main(int argc, char *argv[]) 171 { 172 char *sfs_file = SIMULA_DEFAULT_FILE; 173 int sfs_handle; 174 175 if (argc > 2) 176 { 177 fprintf(stderr, "Incorrect invocation. Possibilities are:\n"); 178 fprintf(stderr, 179 "\t%s /* Picks up %s as the default partition_file */\n", 180 argv[0], SIMULA_DEFAULT_FILE); 181 fprintf(stderr, "\t%s [ partition_file ]\n", argv[0]); 182 return 1; 183 } 184 if (argc == 2) 185 { 186 sfs_file = argv[1]; 187 } 188 sfs_handle = open(sfs_file, O_RDWR); 189 if (sfs_handle == -1) 190 { 191 fprintf(stderr, "Unable to browse SFS over %s\n", sfs_file); 192 return 2; 193 } 194 read(sfs_handle, &sb, sizeof(sfs_super_block_t)); 195 if (sb.type != SIMULA_FS_TYPE) 196 { 197 fprintf(stderr, "Invalid SFS detected. Giving up.\n"); 198 close(sfs_handle); 199 return 3; 200 } 201 browse_sfs(sfs_handle); 202 close(sfs_handle); 203 return 0; 204 }
通过以下命令创建 browse_sfs.c
cat << EOF | sed -e "s/\\\t/\t/g" | tee browse_sfs.c #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <time.h> #include "sfs_ds.h" sfs_super_block_t sb; void sfs_list(int sfs_handle) { \tint i; \tsfs_file_entry_t fe; \tlseek(sfs_handle, sb.entry_table_block_start * sb.block_size, SEEK_SET); \tfor (i = 0; i < sb.entry_count; i++) \t{ \t\tread(sfs_handle, &fe, sizeof(sfs_file_entry_t)); \t\tif (!fe.name[0]) continue; \t\tprintf("%-15s %10d bytes %c%c%c %s", \t\t\tfe.name, fe.size, \t\t\tfe.perms & 04 ? 'r' : '-', \t\t\tfe.perms & 02 ? 'w' : '-', \t\t\tfe.perms & 01 ? 'x' : '-', \t\t\tctime((time_t *)&fe.timestamp) \t\t\t); \t} } void sfs_create(int sfs_handle, char *fn) { \tint i; \tsfs_file_entry_t fe; \tlseek(sfs_handle, sb.entry_table_block_start * sb.block_size, SEEK_SET); \tfor (i = 0; i < sb.entry_count; i++) \t{ \t\tread(sfs_handle, &fe, sizeof(sfs_file_entry_t)); \t\tif (!fe.name[0]) break; \t\tif (strcmp(fe.name, fn) == 0) \t\t{ \t\t\tprintf("File %s already exists\n", fn); \t\t\treturn; \t\t} \t} \tif (i == sb.entry_count) \t{ \t\tprintf("No entries left for : %s\n", fn); \t\treturn; \t} \tlseek(sfs_handle, -(off_t)(sb.entry_size), SEEK_CUR); \tstrncpy(fe.name, fn, 15); \tfe.name[15] = 0; \tfe.size = 0; \tfe.timestamp = time(NULL); \tfe.perms = 07; \tfor (i = 0; i < SIMULA_FS_DATA_BLOCK_CNT; i++) \t{ \t\tfe.blocks[i] = 0; \t} \twrite(sfs_handle, &fe, sizeof(sfs_file_entry_t)); } void sfs_remove(int sfs_handle, char *fn) { \tint i; \tsfs_file_entry_t fe; \tlseek(sfs_handle, sb.entry_table_block_start * sb.block_size, SEEK_SET); \tfor (i = 0; i < sb.entry_count; i++) \t{ \t\tread(sfs_handle, &fe, sizeof(sfs_file_entry_t)); \t\tif (!fe.name[0]) continue; \t\tif (strcmp(fe.name, fn) == 0) break; \t} \tif (i == sb.entry_count) \t{ \t\tprintf("File %s doesn't exist\n", fn); \t\treturn; \t} \tlseek(sfs_handle, -(off_t)(sb.entry_size), SEEK_CUR); \tmemset(&fe, 0, sizeof(sfs_file_entry_t)); \twrite(sfs_handle, &fe, sizeof(sfs_file_entry_t)); } void browse_sfs(int sfs_handle) { \tint done; \tchar cmd[256], *fn; \tint ret; \tdone = 0; \tprintf("Welcome to SFS Browsing Shell v1.0\n\n"); \tprintf("Block size\t : %d bytes\n", sb.block_size); \tprintf("Partition size : %d blocks\n", sb.partition_size); \tprintf("File entry size: %d bytes\n", sb.entry_size); \tprintf("Entry tbl size : %d blocks\n", sb.entry_table_size); \tprintf("Entry count\t: %d\n", sb.entry_count); \tprintf("\n"); \twhile (!done) \t{ \t\tprintf(" $> "); \t\tret = scanf("%[^\n]", cmd); \t\tif (ret < 0) \t\t{ \t\t\tdone = 1; \t\t\tprintf("\n"); \t\t\tcontinue; \t\t} \t\telse \t\t{ \t\t\tgetchar(); \t\t\tif (ret == 0) continue; \t\t} \t\tif (strcmp(cmd, "?") == 0) \t\t{ \t\t\tprintf("Supported commands:\n"); \t\t\tprintf("\t?\tquit\tlist\tcreate\tremove\n"); \t\t\tcontinue; \t\t} \t\telse if (strcmp(cmd, "quit") == 0) \t\t{ \t\t\tdone = 1; \t\t\tcontinue; \t\t} \t\telse if (strcmp(cmd, "list") == 0) \t\t{ \t\t\tsfs_list(sfs_handle); \t\t\tcontinue; \t\t} \t\telse if (strncmp(cmd, "create", 6) == 0) \t\t{ \t\t\tif (cmd[6] == ' ') \t\t\t{ \t\t\t\tfn = cmd + 7; \t\t\t\twhile (*fn == ' ') fn++; \t\t\t\tif (*fn != '\0') // (char) 0 \t\t\t\t{ \t\t\t\t\tsfs_create(sfs_handle, fn); \t\t\t\t\tcontinue; \t\t\t\t} \t\t\t} \t\t} \t\telse if (strncmp(cmd, "remove", 6) == 0) \t\t{ \t\t\tif (cmd[6] == ' ') \t\t\t{ \t\t\t\tfn = cmd + 7; \t\t\t\twhile (*fn == ' ') fn++; \t\t\t\tif (*fn != '\0') // (char) 0 \t\t\t\t{ \t\t\t\t\tsfs_remove(sfs_handle, fn); \t\t\t\t\tcontinue; \t\t\t\t} \t\t\t} \t\t} \t\tprintf("Unknown/Incorrect command: %s\n", cmd); \t\tprintf("Supported commands:\n"); \t\tprintf("\t?\tquit\tlist\tcreate <file>\tremove <file>\n"); \t} } int main(int argc, char *argv[]) { \tchar *sfs_file = SIMULA_DEFAULT_FILE; \tint sfs_handle; \tif (argc > 2) \t{ \t\tfprintf(stderr, "Incorrect invocation. Possibilities are:\n"); \t\tfprintf(stderr, \t\t\t"\t%s /* Picks up %s as the default partition_file */\n", \t\t\targv[0], SIMULA_DEFAULT_FILE); \t\tfprintf(stderr, "\t%s [ partition_file ]\n", argv[0]); \t\treturn 1; \t} \tif (argc == 2) \t{ \t\tsfs_file = argv[1]; \t} \tsfs_handle = open(sfs_file, O_RDWR); \tif (sfs_handle == -1) \t{ \t\tfprintf(stderr, "Unable to browse SFS over %s\n", sfs_file); \t\treturn 2; \t} \tread(sfs_handle, &sb, sizeof(sfs_super_block_t)); \tif (sb.type != SIMULA_FS_TYPE) \t{ \t\tfprintf(stderr, "Invalid SFS detected. Giving up.\n"); \t\tclose(sfs_handle); \t\treturn 3; \t} \tbrowse_sfs(sfs_handle); \tclose(sfs_handle); \treturn 0; } EOF
执行
gcc browse_sfs.c -o browse_sfs sudo apt install rlwrap -y rlwrap ./browse_sfs
will get the result
$ rlwrap ./browse_sfs Welcome to SFS Browsing Shell v1.0 Block size : 512 bytes Partition size : 1024 blocks File entry size: 64 bytes Entry tbl size : 102 blocks Entry count : 816 $> list $> create aaaa $> create bbbb $> list aaaa 0 bytes rwx Thu Jun 24 14:48:20 2973 bbbb 0 bytes rwx Thu Jun 24 14:48:26 2973 $> ? Supported commands: ? quit list create remove