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
basefs_open(const char * file)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
basefs_readline(FILE * f,const char * mountpoint,int * err)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
free_base_fs_entry(void * e)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
basefs_parse(const char * file,const char * mountpoint)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
init(const char * file,const char * mountpoint)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
start_new_file(char * path,ext2_ino_t ino EXT2FS_ATTR ((unused)),struct ext2_inode * inode,void * data)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
add_block(ext2_filsys fs EXT2FS_ATTR ((unused)),blk64_t blocknr,int metadata,void * data)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
inline_data(void * inline_data EXT2FS_ATTR ((unused)),void * data EXT2FS_ATTR ((unused)))170 static int inline_data(void *inline_data EXT2FS_ATTR((unused)),
171 void *data EXT2FS_ATTR((unused)))
172 {
173 return 0;
174 }
175
end_new_file(void * data)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
cleanup(void * data)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