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