1 #include "base_fs.h" 2 #include <stdio.h> 3 4 #define BASE_FS_VERSION "Base EXT4 version 1.0" 5 6 struct base_fs { 7 FILE *file; 8 const char *mountpoint; 9 struct basefs_entry entry; 10 }; 11 12 static FILE *basefs_open(const char *file) 13 { 14 char *line = NULL; 15 size_t len; 16 FILE *f = fopen(file, "r"); 17 if (!f) 18 return NULL; 19 20 if (getline(&line, &len, f) == -1 || !line) 21 goto err_getline; 22 23 if (strncmp(line, BASE_FS_VERSION, strlen(BASE_FS_VERSION))) 24 goto err_header; 25 26 free(line); 27 return f; 28 29 err_header: 30 free(line); 31 err_getline: 32 fclose(f); 33 return NULL; 34 } 35 36 static struct basefs_entry *basefs_readline(FILE *f, const char *mountpoint, 37 int *err) 38 { 39 char *line = NULL, *saveptr1, *saveptr2, *block_range, *block; 40 int offset; 41 size_t len; 42 struct basefs_entry *entry = NULL; 43 blk64_t range_start, range_end; 44 45 if (getline(&line, &len, f) == -1) { 46 if (feof(f)) 47 goto end; 48 goto err_getline; 49 } 50 51 entry = calloc(1, sizeof(*entry)); 52 if (!entry) 53 goto err_alloc; 54 55 /* 56 * With BASEFS version 1.0, a typical line looks like this: 57 * /bin/mke2fs 5000-5004,8000,9000-9990 58 */ 59 if (sscanf(line, "%ms%n", &entry->path, &offset) != 1) 60 goto err_sscanf; 61 len = strlen(mountpoint); 62 memmove(entry->path, entry->path + len, strlen(entry->path) - len + 1); 63 64 while (line[offset] == ' ') 65 ++offset; 66 67 block_range = strtok_r(line + offset, ",\n", &saveptr1); 68 while (block_range) { 69 block = strtok_r(block_range, "-", &saveptr2); 70 if (!block) 71 break; 72 range_start = atoll(block); 73 block = strtok_r(NULL, "-", &saveptr2); 74 range_end = block ? atoll(block) : range_start; 75 add_blocks_to_range(&entry->head, &entry->tail, range_start, 76 range_end); 77 block_range = strtok_r(NULL, ",\n", &saveptr1); 78 } 79 end: 80 *err = 0; 81 free(line); 82 return entry; 83 84 err_sscanf: 85 free(entry); 86 err_alloc: 87 free(line); 88 err_getline: 89 *err = 1; 90 return NULL; 91 } 92 93 static void free_base_fs_entry(void *e) 94 { 95 struct basefs_entry *entry = e; 96 if (entry) { 97 free(entry->path); 98 free(entry); 99 } 100 } 101 102 struct ext2fs_hashmap *basefs_parse(const char *file, const char *mountpoint) 103 { 104 int err; 105 struct ext2fs_hashmap *entries = NULL; 106 struct basefs_entry *entry; 107 FILE *f = basefs_open(file); 108 if (!f) 109 return NULL; 110 entries = ext2fs_hashmap_create(ext2fs_djb2_hash, free_base_fs_entry, 1024); 111 if (!entries) 112 goto end; 113 114 while ((entry = basefs_readline(f, mountpoint, &err))) 115 ext2fs_hashmap_add(entries, entry, entry->path, 116 strlen(entry->path)); 117 118 if (err) { 119 fclose(f); 120 ext2fs_hashmap_free(entries); 121 return NULL; 122 } 123 end: 124 fclose(f); 125 return entries; 126 } 127 128 static void *init(const char *file, const char *mountpoint) 129 { 130 struct base_fs *params = malloc(sizeof(*params)); 131 132 if (!params) 133 return NULL; 134 params->mountpoint = mountpoint; 135 params->file = fopen(file, "w+"); 136 if (!params->file) { 137 free(params); 138 return NULL; 139 } 140 if (fwrite(BASE_FS_VERSION"\n", 1, strlen(BASE_FS_VERSION"\n"), 141 params->file) != strlen(BASE_FS_VERSION"\n")) { 142 fclose(params->file); 143 free(params); 144 return NULL; 145 } 146 return params; 147 } 148 149 static int start_new_file(char *path, ext2_ino_t ino EXT2FS_ATTR((unused)), 150 struct ext2_inode *inode, void *data) 151 { 152 struct base_fs *params = data; 153 154 params->entry.head = params->entry.tail = NULL; 155 params->entry.path = LINUX_S_ISREG(inode->i_mode) ? path : NULL; 156 return 0; 157 } 158 159 static int add_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk64_t blocknr, 160 int metadata, void *data) 161 { 162 struct base_fs *params = data; 163 164 if (params->entry.path && !metadata) 165 add_blocks_to_range(¶ms->entry.head, ¶ms->entry.tail, 166 blocknr, blocknr); 167 return 0; 168 } 169 170 static int inline_data(void *inline_data EXT2FS_ATTR((unused)), 171 void *data EXT2FS_ATTR((unused))) 172 { 173 return 0; 174 } 175 176 static int end_new_file(void *data) 177 { 178 struct base_fs *params = data; 179 180 if (!params->entry.path) 181 return 0; 182 if (fprintf(params->file, "%s%s ", params->mountpoint, 183 params->entry.path) < 0 184 || write_block_ranges(params->file, params->entry.head, ",") 185 || fwrite("\n", 1, 1, params->file) != 1) 186 return -1; 187 188 delete_block_ranges(params->entry.head); 189 return 0; 190 } 191 192 static int cleanup(void *data) 193 { 194 struct base_fs *params = data; 195 196 fclose(params->file); 197 free(params); 198 return 0; 199 } 200 201 struct fsmap_format base_fs_format = { 202 .init = init, 203 .start_new_file = start_new_file, 204 .add_block = add_block, 205 .inline_data = inline_data, 206 .end_new_file = end_new_file, 207 .cleanup = cleanup, 208 }; 209