1 /*
2 * Copyright (C) 2015 - 2020, IBEROXARXA SERVICIOS INTEGRALES, S.L.
3 * Copyright (C) 2015 - 2020, Jaume Olivé Petrus (jolive@whitecatboard.org)
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of the <organization> nor the
16 * names of its contributors may be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 * * The WHITECAT logotype cannot be changed, you can remove it, but you
19 * cannot change it in any way. The WHITECAT logotype is:
20 *
21 * /\ /\
22 * / \_____/ \
23 * /_____________\
24 * W H I T E C A T
25 *
26 * * Redistributions in binary form must retain all copyright notices printed
27 * to any local or remote output device. This include any reference to
28 * Lua RTOS, whitecatboard.org, Lua, and other copyright notices that may
29 * appear in the future.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
35 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 *
42 * Lua RTOS, a tool for make a LFS file system image
43 *
44 */
45
46 #include "lfs.h"
47
48 #include <ctype.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <errno.h>
54 #include <limits.h>
55 #include <dirent.h>
56 #include <sys/types.h>
57
58 static struct lfs_config cfg;
59 static lfs_t lfs;
60 static uint8_t *data;
61
lfs_read(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)62 static int lfs_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
63 {
64 memcpy(buffer, data + (block * c->block_size) + off, size);
65 return 0;
66 }
67
lfs_prog(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)68 static int lfs_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
69 {
70 memcpy(data + (block * c->block_size) + off, buffer, size);
71 return 0;
72 }
73
lfs_erase(const struct lfs_config * c,lfs_block_t block)74 static int lfs_erase(const struct lfs_config *c, lfs_block_t block)
75 {
76 memset(data + (block * c->block_size), 0, c->block_size);
77 return 0;
78 }
79
lfs_sync(const struct lfs_config * c)80 static int lfs_sync(const struct lfs_config *c)
81 {
82 return 0;
83 }
84
create_dir(char * src)85 static void create_dir(char *src)
86 {
87 char *path;
88
89 path = strchr(src, '/');
90 if (path) {
91 int ret;
92 fprintf(stdout, "%s\r\n", path);
93
94 if ((ret = lfs_mkdir(&lfs, path)) < 0) {
95 fprintf(stderr, "can't create directory %s: error=%d\r\n", path, ret);
96 exit(1);
97 }
98 }
99 }
100
create_file(char * src)101 static void create_file(char *src)
102 {
103 char *path;
104
105 path = strchr(src, '/');
106 if (path) {
107 fprintf(stdout, "%s\r\n", path);
108
109 // Open source file
110 FILE *srcf = fopen(src, "rb");
111 if (!srcf) {
112 fprintf(stderr, "can't open source file %s: errno=%d (%s)\r\n", src, errno, strerror(errno));
113 exit(1);
114 }
115
116 // Open destination file
117 lfs_file_t dstf;
118 int ret;
119 if ((ret = lfs_file_open(&lfs, &dstf, path, LFS_O_WRONLY | LFS_O_CREAT)) < 0) {
120 fprintf(stderr, "can't open destination file %s: error=%d\r\n", path, ret);
121 exit(1);
122 }
123
124 char c = fgetc(srcf);
125 while (!feof(srcf)) {
126 ret = lfs_file_write(&lfs, &dstf, &c, 1);
127 if (ret < 0) {
128 fprintf(stderr, "can't write to destination file %s: error=%d\r\n", path, ret);
129 exit(1);
130 }
131 c = fgetc(srcf);
132 }
133
134 // Close destination file
135 ret = lfs_file_close(&lfs, &dstf);
136 if (ret < 0) {
137 fprintf(stderr, "can't close destination file %s: error=%d\r\n", path, ret);
138 exit(1);
139 }
140
141 // Close source file
142 fclose(srcf);
143 }
144 }
145 static void skip_direct(char *curr_path, struct dirent *ent, char *src);
146
compact(char * src)147 static void compact(char *src)
148 {
149 DIR *dir;
150
151 dir = opendir(src);
152 if (dir) {
153 struct dirent *ent;
154 char curr_path[PATH_MAX];
155 while ((ent = readdir(dir))) {
156 // Skip . and .. directories
157 skip_direct(curr_path, ent, src);
158 }
159
160 closedir(dir);
161 }
162 }
skip_direct(char * curr_path,struct dirent * ent,char * src)163 static void skip_direct(char *curr_path, struct dirent *ent, char *src)
164 {
165 if ((strcmp(ent->d_name, ".") != 0) && (strcmp(ent->d_name, "..") != 0)) {
166 // Update the current path
167 strcpy(curr_path, src);
168 strcat(curr_path, "/");
169 strcat(curr_path, ent->d_name);
170
171 if (ent->d_type == DT_DIR) {
172 create_dir(curr_path);
173 compact(curr_path);
174 } else if (ent->d_type == DT_REG) {
175 create_file(curr_path);
176 }
177 }
178 }
usage(void)179 void usage(void)
180 {
181 (void)fprintf(stdout,
182 "usage: mklfs -c <pack-dir> -b <block-size> -r <read-size> -p <prog-size> -s <filesystem-size> -n "
183 "<context> -l <lookahead_size> -e <cache_size> -k <block_cycles> -i <image-file-path>\r\n");
184 }
185
is_number(const char * s)186 static int is_number(const char *s)
187 {
188 const char *c = s;
189
190 while (*c) {
191 if ((*c < '0') || (*c > '9')) {
192 return 0;
193 }
194 c++;
195 }
196
197 return 1;
198 }
199
is_hex(const char * s)200 static int is_hex(const char *s)
201 {
202 const char *c = s;
203
204 if (*c++ != '0') {
205 return 0;
206 }
207
208 if (*c++ != 'x') {
209 return 0;
210 }
211
212 while (*c) {
213 if (((*c < '0') || (*c > '9')) && ((*c < 'A') || (*c > 'F')) && ((*c < 'a') || (*c > 'f'))) {
214 return 0;
215 }
216 c++;
217 }
218
219 return 1;
220 }
221
to_int(const char * s)222 static int to_int(const char *s)
223 {
224 if (is_number(s)) {
225 return atoi(s);
226 } else if (is_hex(s)) {
227 return (int)strtol(s, NULL, 0x10);
228 }
229
230 return -1;
231 }
232
main(int argc,char ** argv)233 int main(int argc, char **argv)
234 {
235 char *src = NULL; // Source directory
236 char *dst = NULL; // Destination image
237 int c; // Current option
238 int block_size = 0; // Block size
239 int read_size = 0; // Read size
240 int prog_size = 0; // Prog size
241 int fs_size = 0; // File system size
242 int err;
243 int part_no = 0; // context
244 int blk_cycles = 0; // block_cycles
245 int la_size = 0; // lookahead_size
246 int ca_size = 0; // cache_size
247
248 while ((c = getopt(argc, argv, "c:i:b:p:r:s:n:l:e:k:")) != -1) {
249 switch (c) {
250 case 'c':
251 src = optarg;
252 break;
253
254 case 'i':
255 dst = optarg;
256 break;
257
258 case 'b':
259 block_size = to_int(optarg);
260 break;
261
262 case 'p':
263 prog_size = to_int(optarg);
264 break;
265
266 case 'r':
267 read_size = to_int(optarg);
268 break;
269
270 case 's':
271 fs_size = to_int(optarg);
272 break;
273
274 case 'n':
275 part_no = to_int(optarg);
276 break;
277
278 case 'l':
279 la_size = to_int(optarg);
280 break;
281
282 case 'e':
283 ca_size = to_int(optarg);
284 break;
285
286 case 'k':
287 blk_cycles = to_int(optarg);
288 break;
289 default:
290 break;
291 }
292 }
293
294 if ((src == NULL) || (dst == NULL) || (block_size <= 0) || (prog_size <= 0) || (read_size <= 0) || (fs_size <= 0)) {
295 usage();
296 exit(1);
297 }
298
299 // Mount the file system
300 cfg.read = lfs_read;
301 cfg.prog = lfs_prog;
302 cfg.erase = lfs_erase;
303 cfg.sync = lfs_sync;
304
305 cfg.block_size = block_size;
306 cfg.read_size = read_size;
307 cfg.prog_size = prog_size;
308 cfg.block_count = fs_size / cfg.block_size;
309 cfg.lookahead_size = la_size;
310 cfg.context = (void *)part_no;
311 cfg.block_cycles = blk_cycles;
312 cfg.cache_size = ca_size;
313
314 data = calloc(1, fs_size);
315 if (!data) {
316 fprintf(stderr, "no memory for mount\r\n");
317 return -1;
318 }
319
320 err = lfs_format(&lfs, &cfg);
321 if (err < 0) {
322 fprintf(stderr, "format error: error=%d\r\n", err);
323 return -1;
324 }
325
326 err = lfs_mount(&lfs, &cfg);
327 if (err < 0) {
328 fprintf(stderr, "mount error: error=%d\r\n", err);
329 return -1;
330 }
331
332 char *last_dir = strrchr(src, '/');
333 if (last_dir) {
334 last_dir++;
335 compact(last_dir);
336 } else {
337 compact(src);
338 }
339
340 FILE *img = fopen(dst, "wb+");
341
342 if (!img) {
343 fprintf(stderr, "can't create image file: errno=%d (%s)\r\n", errno, strerror(errno));
344 return -1;
345 }
346
347 int ret;
348 ret = fwrite(data, 1, fs_size, img);
349 if (ret < 0) {
350 fprintf(stderr, "fwrite(data, 1, fs_size, img): errno=%d (%s)\r\n", errno, strerror(errno));
351 }
352
353 ret = fclose(img);
354 if (ret < 0) {
355 fprintf(stderr, "fclose(img): errno=%d (%s)\r\n", errno, strerror(errno));
356 return -1;
357 }
358
359 return 0;
360 }
361