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