fs: fix generic save command implementation
[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 <common.h>
19 #include <part.h>
20 #include <ext4fs.h>
21 #include <fat.h>
22 #include <fs.h>
23 #include <sandboxfs.h>
24 #include <asm/io.h>
25
26 DECLARE_GLOBAL_DATA_PTR;
27
28 static block_dev_desc_t *fs_dev_desc;
29 static disk_partition_t fs_partition;
30 static int fs_type = FS_TYPE_ANY;
31
32 static inline int fs_probe_unsupported(block_dev_desc_t *fs_dev_desc,
33                                       disk_partition_t *fs_partition)
34 {
35         printf("** Unrecognized filesystem type **\n");
36         return -1;
37 }
38
39 static inline int fs_ls_unsupported(const char *dirname)
40 {
41         return -1;
42 }
43
44 static inline int fs_read_unsupported(const char *filename, void *buf,
45                                       int offset, int len)
46 {
47         return -1;
48 }
49
50 static inline int fs_write_unsupported(const char *filename, void *buf,
51                                       int offset, int len)
52 {
53         return -1;
54 }
55
56 static inline void fs_close_unsupported(void)
57 {
58 }
59
60 struct fstype_info {
61         int fstype;
62         int (*probe)(block_dev_desc_t *fs_dev_desc,
63                      disk_partition_t *fs_partition);
64         int (*ls)(const char *dirname);
65         int (*read)(const char *filename, void *buf, int offset, int len);
66         int (*write)(const char *filename, void *buf, int offset, int len);
67         void (*close)(void);
68 };
69
70 static struct fstype_info fstypes[] = {
71 #ifdef CONFIG_FS_FAT
72         {
73                 .fstype = FS_TYPE_FAT,
74                 .probe = fat_set_blk_dev,
75                 .close = fat_close,
76                 .ls = file_fat_ls,
77                 .read = fat_read_file,
78                 .write = fs_write_unsupported,
79         },
80 #endif
81 #ifdef CONFIG_FS_EXT4
82         {
83                 .fstype = FS_TYPE_EXT,
84                 .probe = ext4fs_probe,
85                 .close = ext4fs_close,
86                 .ls = ext4fs_ls,
87                 .read = ext4_read_file,
88                 .write = fs_write_unsupported,
89         },
90 #endif
91 #ifdef CONFIG_SANDBOX
92         {
93                 .fstype = FS_TYPE_SANDBOX,
94                 .probe = sandbox_fs_set_blk_dev,
95                 .close = sandbox_fs_close,
96                 .ls = sandbox_fs_ls,
97                 .read = fs_read_sandbox,
98                 .write = fs_write_sandbox,
99         },
100 #endif
101         {
102                 .fstype = FS_TYPE_ANY,
103                 .probe = fs_probe_unsupported,
104                 .close = fs_close_unsupported,
105                 .ls = fs_ls_unsupported,
106                 .read = fs_read_unsupported,
107                 .write = fs_write_unsupported,
108         },
109 };
110
111 static struct fstype_info *fs_get_info(int fstype)
112 {
113         struct fstype_info *info;
114         int i;
115
116         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) {
117                 if (fstype == info->fstype)
118                         return info;
119         }
120
121         /* Return the 'unsupported' sentinel */
122         return info;
123 }
124
125 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
126 {
127         struct fstype_info *info;
128         int part, i;
129 #ifdef CONFIG_NEEDS_MANUAL_RELOC
130         static int relocated;
131
132         if (!relocated) {
133                 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
134                                 i++, info++) {
135                         info->probe += gd->reloc_off;
136                         info->close += gd->reloc_off;
137                         info->ls += gd->reloc_off;
138                         info->read += gd->reloc_off;
139                         info->write += gd->reloc_off;
140                 }
141                 relocated = 1;
142         }
143 #endif
144
145         part = get_device_and_partition(ifname, dev_part_str, &fs_dev_desc,
146                                         &fs_partition, 1);
147         if (part < 0)
148                 return -1;
149
150         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
151                 if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
152                                 fstype != info->fstype)
153                         continue;
154
155                 if (!info->probe(fs_dev_desc, &fs_partition)) {
156                         fs_type = info->fstype;
157                         return 0;
158                 }
159         }
160
161         return -1;
162 }
163
164 static void fs_close(void)
165 {
166         struct fstype_info *info = fs_get_info(fs_type);
167
168         info->close();
169
170         fs_type = FS_TYPE_ANY;
171 }
172
173 int fs_ls(const char *dirname)
174 {
175         int ret;
176
177         struct fstype_info *info = fs_get_info(fs_type);
178
179         ret = info->ls(dirname);
180
181         fs_type = FS_TYPE_ANY;
182         fs_close();
183
184         return ret;
185 }
186
187 int fs_read(const char *filename, ulong addr, int offset, int len)
188 {
189         struct fstype_info *info = fs_get_info(fs_type);
190         void *buf;
191         int ret;
192
193         /*
194          * We don't actually know how many bytes are being read, since len==0
195          * means read the whole file.
196          */
197         buf = map_sysmem(addr, len);
198         ret = info->read(filename, buf, offset, len);
199         unmap_sysmem(buf);
200
201         /* If we requested a specific number of bytes, check we got it */
202         if (ret >= 0 && len && ret != len) {
203                 printf("** Unable to read file %s **\n", filename);
204                 ret = -1;
205         }
206         fs_close();
207
208         return ret;
209 }
210
211 int fs_write(const char *filename, ulong addr, int offset, int len)
212 {
213         struct fstype_info *info = fs_get_info(fs_type);
214         void *buf;
215         int ret;
216
217         buf = map_sysmem(addr, len);
218         ret = info->write(filename, buf, offset, len);
219         unmap_sysmem(buf);
220
221         if (ret >= 0 && ret != len) {
222                 printf("** Unable to write file %s **\n", filename);
223                 ret = -1;
224         }
225         fs_close();
226
227         return ret;
228 }
229
230 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
231                 int fstype)
232 {
233         unsigned long addr;
234         const char *addr_str;
235         const char *filename;
236         unsigned long bytes;
237         unsigned long pos;
238         int len_read;
239         unsigned long time;
240
241         if (argc < 2)
242                 return CMD_RET_USAGE;
243         if (argc > 7)
244                 return CMD_RET_USAGE;
245
246         if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
247                 return 1;
248
249         if (argc >= 4) {
250                 addr = simple_strtoul(argv[3], NULL, 16);
251         } else {
252                 addr_str = getenv("loadaddr");
253                 if (addr_str != NULL)
254                         addr = simple_strtoul(addr_str, NULL, 16);
255                 else
256                         addr = CONFIG_SYS_LOAD_ADDR;
257         }
258         if (argc >= 5) {
259                 filename = argv[4];
260         } else {
261                 filename = getenv("bootfile");
262                 if (!filename) {
263                         puts("** No boot file defined **\n");
264                         return 1;
265                 }
266         }
267         if (argc >= 6)
268                 bytes = simple_strtoul(argv[5], NULL, 16);
269         else
270                 bytes = 0;
271         if (argc >= 7)
272                 pos = simple_strtoul(argv[6], NULL, 16);
273         else
274                 pos = 0;
275
276         time = get_timer(0);
277         len_read = fs_read(filename, addr, pos, bytes);
278         time = get_timer(time);
279         if (len_read <= 0)
280                 return 1;
281
282         printf("%d bytes read in %lu ms", len_read, time);
283         if (time > 0) {
284                 puts(" (");
285                 print_size(len_read / time * 1000, "/s");
286                 puts(")");
287         }
288         puts("\n");
289
290         setenv_hex("filesize", len_read);
291
292         return 0;
293 }
294
295 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
296         int fstype)
297 {
298         if (argc < 2)
299                 return CMD_RET_USAGE;
300         if (argc > 4)
301                 return CMD_RET_USAGE;
302
303         if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
304                 return 1;
305
306         if (fs_ls(argc >= 4 ? argv[3] : "/"))
307                 return 1;
308
309         return 0;
310 }
311
312 int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
313                 int fstype)
314 {
315         unsigned long addr;
316         const char *filename;
317         unsigned long bytes;
318         unsigned long pos;
319         int len;
320         unsigned long time;
321
322         if (argc < 6 || argc > 7)
323                 return CMD_RET_USAGE;
324
325         if (fs_set_blk_dev(argv[1], argv[2], fstype))
326                 return 1;
327
328         filename = argv[3];
329         addr = simple_strtoul(argv[4], NULL, 16);
330         bytes = simple_strtoul(argv[5], NULL, 16);
331         if (argc >= 7)
332                 pos = simple_strtoul(argv[6], NULL, 16);
333         else
334                 pos = 0;
335
336         time = get_timer(0);
337         len = fs_write(filename, addr, pos, bytes);
338         time = get_timer(time);
339         if (len <= 0)
340                 return 1;
341
342         printf("%d bytes written in %lu ms", len, time);
343         if (time > 0) {
344                 puts(" (");
345                 print_size(len / time * 1000, "/s");
346                 puts(")");
347         }
348         puts("\n");
349
350         return 0;
351 }