buildman: Try to avoid hard-coded string parsing
[platform/kernel/u-boot.git] / fs / fs.c
1 /*
2  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16
17 #include <config.h>
18 #include <errno.h>
19 #include <common.h>
20 #include <part.h>
21 #include <ext4fs.h>
22 #include <fat.h>
23 #include <fs.h>
24 #include <sandboxfs.h>
25 #include <asm/io.h>
26 #include <div64.h>
27 #include <linux/math64.h>
28
29 DECLARE_GLOBAL_DATA_PTR;
30
31 static block_dev_desc_t *fs_dev_desc;
32 static disk_partition_t fs_partition;
33 static int fs_type = FS_TYPE_ANY;
34
35 static inline int fs_probe_unsupported(block_dev_desc_t *fs_dev_desc,
36                                       disk_partition_t *fs_partition)
37 {
38         printf("** Unrecognized filesystem type **\n");
39         return -1;
40 }
41
42 static inline int fs_ls_unsupported(const char *dirname)
43 {
44         return -1;
45 }
46
47 static inline int fs_exists_unsupported(const char *filename)
48 {
49         return 0;
50 }
51
52 static inline int fs_size_unsupported(const char *filename, loff_t *size)
53 {
54         return -1;
55 }
56
57 static inline int fs_read_unsupported(const char *filename, void *buf,
58                                       loff_t offset, loff_t len,
59                                       loff_t *actread)
60 {
61         return -1;
62 }
63
64 static inline int fs_write_unsupported(const char *filename, void *buf,
65                                       loff_t offset, loff_t len,
66                                       loff_t *actwrite)
67 {
68         return -1;
69 }
70
71 static inline void fs_close_unsupported(void)
72 {
73 }
74
75 static inline int fs_uuid_unsupported(char *uuid_str)
76 {
77         return -1;
78 }
79
80 struct fstype_info {
81         int fstype;
82         /*
83          * Is it legal to pass NULL as .probe()'s  fs_dev_desc parameter? This
84          * should be false in most cases. For "virtual" filesystems which
85          * aren't based on a U-Boot block device (e.g. sandbox), this can be
86          * set to true. This should also be true for the dumm entry at the end
87          * of fstypes[], since that is essentially a "virtual" (non-existent)
88          * filesystem.
89          */
90         bool null_dev_desc_ok;
91         int (*probe)(block_dev_desc_t *fs_dev_desc,
92                      disk_partition_t *fs_partition);
93         int (*ls)(const char *dirname);
94         int (*exists)(const char *filename);
95         int (*size)(const char *filename, loff_t *size);
96         int (*read)(const char *filename, void *buf, loff_t offset,
97                     loff_t len, loff_t *actread);
98         int (*write)(const char *filename, void *buf, loff_t offset,
99                      loff_t len, loff_t *actwrite);
100         void (*close)(void);
101         int (*uuid)(char *uuid_str);
102 };
103
104 static struct fstype_info fstypes[] = {
105 #ifdef CONFIG_FS_FAT
106         {
107                 .fstype = FS_TYPE_FAT,
108                 .null_dev_desc_ok = false,
109                 .probe = fat_set_blk_dev,
110                 .close = fat_close,
111                 .ls = file_fat_ls,
112                 .exists = fat_exists,
113                 .size = fat_size,
114                 .read = fat_read_file,
115 #ifdef CONFIG_FAT_WRITE
116                 .write = file_fat_write,
117 #else
118                 .write = fs_write_unsupported,
119 #endif
120                 .uuid = fs_uuid_unsupported,
121         },
122 #endif
123 #ifdef CONFIG_FS_EXT4
124         {
125                 .fstype = FS_TYPE_EXT,
126                 .null_dev_desc_ok = false,
127                 .probe = ext4fs_probe,
128                 .close = ext4fs_close,
129                 .ls = ext4fs_ls,
130                 .exists = ext4fs_exists,
131                 .size = ext4fs_size,
132                 .read = ext4_read_file,
133 #ifdef CONFIG_CMD_EXT4_WRITE
134                 .write = ext4_write_file,
135 #else
136                 .write = fs_write_unsupported,
137 #endif
138                 .uuid = ext4fs_uuid,
139         },
140 #endif
141 #ifdef CONFIG_SANDBOX
142         {
143                 .fstype = FS_TYPE_SANDBOX,
144                 .null_dev_desc_ok = true,
145                 .probe = sandbox_fs_set_blk_dev,
146                 .close = sandbox_fs_close,
147                 .ls = sandbox_fs_ls,
148                 .exists = sandbox_fs_exists,
149                 .size = sandbox_fs_size,
150                 .read = fs_read_sandbox,
151                 .write = fs_write_sandbox,
152                 .uuid = fs_uuid_unsupported,
153         },
154 #endif
155         {
156                 .fstype = FS_TYPE_ANY,
157                 .null_dev_desc_ok = true,
158                 .probe = fs_probe_unsupported,
159                 .close = fs_close_unsupported,
160                 .ls = fs_ls_unsupported,
161                 .exists = fs_exists_unsupported,
162                 .size = fs_size_unsupported,
163                 .read = fs_read_unsupported,
164                 .write = fs_write_unsupported,
165                 .uuid = fs_uuid_unsupported,
166         },
167 };
168
169 static struct fstype_info *fs_get_info(int fstype)
170 {
171         struct fstype_info *info;
172         int i;
173
174         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) {
175                 if (fstype == info->fstype)
176                         return info;
177         }
178
179         /* Return the 'unsupported' sentinel */
180         return info;
181 }
182
183 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
184 {
185         struct fstype_info *info;
186         int part, i;
187 #ifdef CONFIG_NEEDS_MANUAL_RELOC
188         static int relocated;
189
190         if (!relocated) {
191                 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
192                                 i++, info++) {
193                         info->probe += gd->reloc_off;
194                         info->close += gd->reloc_off;
195                         info->ls += gd->reloc_off;
196                         info->read += gd->reloc_off;
197                         info->write += gd->reloc_off;
198                 }
199                 relocated = 1;
200         }
201 #endif
202
203         part = get_device_and_partition(ifname, dev_part_str, &fs_dev_desc,
204                                         &fs_partition, 1);
205         if (part < 0)
206                 return -1;
207
208         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
209                 if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
210                                 fstype != info->fstype)
211                         continue;
212
213                 if (!fs_dev_desc && !info->null_dev_desc_ok)
214                         continue;
215
216                 if (!info->probe(fs_dev_desc, &fs_partition)) {
217                         fs_type = info->fstype;
218                         return 0;
219                 }
220         }
221
222         return -1;
223 }
224
225 static void fs_close(void)
226 {
227         struct fstype_info *info = fs_get_info(fs_type);
228
229         info->close();
230
231         fs_type = FS_TYPE_ANY;
232 }
233
234 int fs_uuid(char *uuid_str)
235 {
236         struct fstype_info *info = fs_get_info(fs_type);
237
238         return info->uuid(uuid_str);
239 }
240
241 int fs_ls(const char *dirname)
242 {
243         int ret;
244
245         struct fstype_info *info = fs_get_info(fs_type);
246
247         ret = info->ls(dirname);
248
249         fs_type = FS_TYPE_ANY;
250         fs_close();
251
252         return ret;
253 }
254
255 int fs_exists(const char *filename)
256 {
257         int ret;
258
259         struct fstype_info *info = fs_get_info(fs_type);
260
261         ret = info->exists(filename);
262
263         fs_close();
264
265         return ret;
266 }
267
268 int fs_size(const char *filename, loff_t *size)
269 {
270         int ret;
271
272         struct fstype_info *info = fs_get_info(fs_type);
273
274         ret = info->size(filename, size);
275
276         fs_close();
277
278         return ret;
279 }
280
281 int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
282             loff_t *actread)
283 {
284         struct fstype_info *info = fs_get_info(fs_type);
285         void *buf;
286         int ret;
287
288         /*
289          * We don't actually know how many bytes are being read, since len==0
290          * means read the whole file.
291          */
292         buf = map_sysmem(addr, len);
293         ret = info->read(filename, buf, offset, len, actread);
294         unmap_sysmem(buf);
295
296         /* If we requested a specific number of bytes, check we got it */
297         if (ret == 0 && len && *actread != len) {
298                 printf("** Unable to read file %s **\n", filename);
299                 ret = -1;
300         }
301         fs_close();
302
303         return ret;
304 }
305
306 int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
307              loff_t *actwrite)
308 {
309         struct fstype_info *info = fs_get_info(fs_type);
310         void *buf;
311         int ret;
312
313         buf = map_sysmem(addr, len);
314         ret = info->write(filename, buf, offset, len, actwrite);
315         unmap_sysmem(buf);
316
317         if (ret < 0 && len != *actwrite) {
318                 printf("** Unable to write file %s **\n", filename);
319                 ret = -1;
320         }
321         fs_close();
322
323         return ret;
324 }
325
326 int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
327                 int fstype)
328 {
329         loff_t size;
330
331         if (argc != 4)
332                 return CMD_RET_USAGE;
333
334         if (fs_set_blk_dev(argv[1], argv[2], fstype))
335                 return 1;
336
337         if (fs_size(argv[3], &size) < 0)
338                 return CMD_RET_FAILURE;
339
340         setenv_hex("filesize", size);
341
342         return 0;
343 }
344
345 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
346                 int fstype)
347 {
348         unsigned long addr;
349         const char *addr_str;
350         const char *filename;
351         loff_t bytes;
352         loff_t pos;
353         loff_t len_read;
354         int ret;
355         unsigned long time;
356         char *ep;
357
358         if (argc < 2)
359                 return CMD_RET_USAGE;
360         if (argc > 7)
361                 return CMD_RET_USAGE;
362
363         if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
364                 return 1;
365
366         if (argc >= 4) {
367                 addr = simple_strtoul(argv[3], &ep, 16);
368                 if (ep == argv[3] || *ep != '\0')
369                         return CMD_RET_USAGE;
370         } else {
371                 addr_str = getenv("loadaddr");
372                 if (addr_str != NULL)
373                         addr = simple_strtoul(addr_str, NULL, 16);
374                 else
375                         addr = CONFIG_SYS_LOAD_ADDR;
376         }
377         if (argc >= 5) {
378                 filename = argv[4];
379         } else {
380                 filename = getenv("bootfile");
381                 if (!filename) {
382                         puts("** No boot file defined **\n");
383                         return 1;
384                 }
385         }
386         if (argc >= 6)
387                 bytes = simple_strtoul(argv[5], NULL, 16);
388         else
389                 bytes = 0;
390         if (argc >= 7)
391                 pos = simple_strtoul(argv[6], NULL, 16);
392         else
393                 pos = 0;
394
395         time = get_timer(0);
396         ret = fs_read(filename, addr, pos, bytes, &len_read);
397         time = get_timer(time);
398         if (ret < 0)
399                 return 1;
400
401         printf("%llu bytes read in %lu ms", len_read, time);
402         if (time > 0) {
403                 puts(" (");
404                 print_size(div_u64(len_read, time) * 1000, "/s");
405                 puts(")");
406         }
407         puts("\n");
408
409         setenv_hex("filesize", len_read);
410
411         return 0;
412 }
413
414 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
415         int fstype)
416 {
417         if (argc < 2)
418                 return CMD_RET_USAGE;
419         if (argc > 4)
420                 return CMD_RET_USAGE;
421
422         if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
423                 return 1;
424
425         if (fs_ls(argc >= 4 ? argv[3] : "/"))
426                 return 1;
427
428         return 0;
429 }
430
431 int file_exists(const char *dev_type, const char *dev_part, const char *file,
432                 int fstype)
433 {
434         if (fs_set_blk_dev(dev_type, dev_part, fstype))
435                 return 0;
436
437         return fs_exists(file);
438 }
439
440 int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
441                 int fstype)
442 {
443         unsigned long addr;
444         const char *filename;
445         loff_t bytes;
446         loff_t pos;
447         loff_t len;
448         int ret;
449         unsigned long time;
450
451         if (argc < 6 || argc > 7)
452                 return CMD_RET_USAGE;
453
454         if (fs_set_blk_dev(argv[1], argv[2], fstype))
455                 return 1;
456
457         addr = simple_strtoul(argv[3], NULL, 16);
458         filename = argv[4];
459         bytes = simple_strtoul(argv[5], NULL, 16);
460         if (argc >= 7)
461                 pos = simple_strtoul(argv[6], NULL, 16);
462         else
463                 pos = 0;
464
465         time = get_timer(0);
466         ret = fs_write(filename, addr, pos, bytes, &len);
467         time = get_timer(time);
468         if (ret < 0)
469                 return 1;
470
471         printf("%llu bytes written in %lu ms", len, time);
472         if (time > 0) {
473                 puts(" (");
474                 print_size(div_u64(len, time) * 1000, "/s");
475                 puts(")");
476         }
477         puts("\n");
478
479         return 0;
480 }
481
482 int do_fs_uuid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
483                 int fstype)
484 {
485         int ret;
486         char uuid[37];
487         memset(uuid, 0, sizeof(uuid));
488
489         if (argc < 3 || argc > 4)
490                 return CMD_RET_USAGE;
491
492         if (fs_set_blk_dev(argv[1], argv[2], fstype))
493                 return 1;
494
495         ret = fs_uuid(uuid);
496         if (ret)
497                 return CMD_RET_FAILURE;
498
499         if (argc == 4)
500                 setenv(argv[3], uuid);
501         else
502                 printf("%s\n", uuid);
503
504         return CMD_RET_SUCCESS;
505 }