-
Notifications
You must be signed in to change notification settings - Fork 48
/
libfs.c
354 lines (309 loc) · 10.4 KB
/
libfs.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
/* Copyright (C) 2013 by John Cronin <jncronin@tysos.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include "block.h"
#include "vfs.h"
#include "util.h"
// Space for 32 x 512 byte cache areas
#define BLOCK_CACHE_SIZE 0x4000
#ifdef ENABLE_SD
int sd_card_init(struct block_device **dev);
#endif
#ifdef ENABLE_MBR
int read_mbr(struct block_device *, struct block_device ***, int *);
#endif
#ifdef ENABLE_USB
int dwc_usb_init(struct usb_hcd **dev, uint32_t base);
#endif
#ifdef ENABLE_RASPBOOTIN
int raspbootin_init(struct fs **fs);
#endif
#ifdef ENABLE_FAT
int fat_init(struct block_device *, struct fs **);
#endif
#ifdef ENABLE_EXT2
int ext2_init(struct block_device *, struct fs **);
#endif
#ifdef ENABLE_NOFS
int nofs_init(struct block_device *, struct fs **);
#endif
#ifdef ENABLE_BLOCK_CACHE
int cache_init(struct block_device *parent, struct block_device **dev, uintptr_t cache_start, size_t cache_length);
#endif
void libfs_init()
{
#ifdef ENABLE_USB
struct usb_hcd *usb_hcd;
dwc_usb_init(&usb_hcd, DWC_USB_BASE);
#endif
#ifdef ENABLE_SD
struct block_device *sd_dev = NULL;
if(sd_card_init(&sd_dev) == 0)
{
struct block_device *c_dev = sd_dev;
#ifdef ENABLE_BLOCK_CACHE
uintptr_t cache_start = alloc_buf(BLOCK_CACHE_SIZE);
if(cache_start != 0)
cache_init(sd_dev, &c_dev, cache_start, BLOCK_CACHE_SIZE);
#endif
#ifdef ENABLE_MBR
read_mbr(c_dev, (void*)0, (void*)0);
#endif
}
#endif
#ifdef ENABLE_RASPBOOTIN
struct fs *raspbootin_fs;
if(raspbootin_init(&raspbootin_fs) == 0)
vfs_register(raspbootin_fs);
#endif
}
int register_fs(struct block_device *dev, int part_id)
{
switch(part_id)
{
case 0:
// Try reading it as an mbr, then try all known filesystems
#ifdef ENABLE_MBR
if(read_mbr(dev, NULL, NULL) == 0)
break;
#endif
#ifdef ENABLE_FAT
if(fat_init(dev, &dev->fs) == 0)
break;
#endif
#ifdef ENABLE_EXT2
if(ext2_init(dev, &dev->fs) == 0)
break;
#endif
#ifdef ENABLE_NOFS
// Don't automatically assume nofs as there is no way of ensuring
// the partition is of this type (no magic in the superblock)
//if(nofs_init(dev, &dev->fs) == 0)
// break;
#endif
break;
case 1:
case 4:
case 6:
case 0xb:
case 0xc:
case 0xe:
case 0x11:
case 0x14:
case 0x1b:
case 0x1c:
case 0x1e:
#ifdef ENABLE_FAT
fat_init(dev, &dev->fs);
#endif
break;
case 0x83:
#ifdef ENABLE_EXT2
ext2_init(dev, &dev->fs);
#endif
break;
case 0xda:
#ifdef ENABLE_NOFS
nofs_init(dev, &dev->fs);
#endif
break;
}
if(dev->fs)
{
vfs_register(dev->fs);
return 0;
}
else
return -1;
}
int fs_interpret_mode(const char *mode)
{
// Interpret mode arguments
if(!strcmp(mode, "r"))
return VFS_MODE_R;
if(!strcmp(mode, "r+"))
return VFS_MODE_RW;
if(!strcmp(mode, "w"))
return VFS_MODE_W | VFS_MODE_CREATE;
if(!strcmp(mode, "w+"))
return VFS_MODE_RW | VFS_MODE_CREATE;
if(!strcmp(mode, "a"))
return VFS_MODE_W | VFS_MODE_APPEND | VFS_MODE_CREATE;
if(!strcmp(mode, "a+"))
return VFS_MODE_RW | VFS_MODE_APPEND | VFS_MODE_CREATE;
return 0;
}
/* The fread/fwrite() functions in filesystems code shares a lot of common functionality
* We provide that here
* There are essentially two types of filesystem as regards to indexing blocks
* within a file.
* Assume a file contains n blocks and we want block i.
* Filesystems like ext2, nofs can tell us the block number from i
* Ones like FAT need to know the block number i - 1 and work it out from there
*
* Thus, if the block number can be calculated from i, can_index_blocks is set
* to 1.
*
* fs_fread fills in as many of the parameters of get_next_block_num as it can
*/
size_t fs_fread(uint32_t (*get_next_bdev_block_num)(uint32_t f_block_idx, FILE *s, void *opaque, int add_blocks),
struct fs *fs, void *ptr, size_t byte_size,
FILE *stream, void *opaque)
{
uint32_t fs_block_size = fs->block_size;
// Determine first and last block indices within file
uint32_t first_f_block_idx = stream->pos / fs_block_size;
uint32_t first_f_block_offset = stream->pos % fs_block_size;
uint32_t last_pos = stream->pos + byte_size;
uint32_t last_f_block_idx = last_pos / fs_block_size;
uint32_t last_f_block_offset = last_pos % fs_block_size;
// Now iterate through the blocks
uint32_t cur_block = first_f_block_idx;
uint8_t *save_buf = (uint8_t *)ptr;
int total_bytes_read = 0;
while(cur_block <= last_f_block_idx)
{
uint32_t start_block_offset = 0;
uint32_t last_block_offset = fs_block_size;
// If we're the first block, adjust start_block_idx appropriately
if(cur_block == first_f_block_idx)
start_block_offset = first_f_block_offset;
// If we're the last block, adjust last_block_idx appropriately
if(cur_block == last_f_block_idx)
last_block_offset = last_f_block_offset;
uint32_t block_segment_length = last_block_offset - start_block_offset;
// Get the filesystem block number
uint32_t cur_bdev_block = get_next_bdev_block_num(cur_block, stream, opaque, 0);
if(cur_bdev_block == 0xffffffff)
return total_bytes_read;
// If we can load an entire block, load it directly, else we have
// to load to a buffer somewhere and copy appropriately
if((start_block_offset == 0) && (block_segment_length == fs_block_size))
{
int bytes_read = block_read(fs->parent, save_buf, fs_block_size, cur_bdev_block);
total_bytes_read += bytes_read;
stream->pos += bytes_read;
save_buf += bytes_read;
if((uint32_t)bytes_read != fs_block_size)
return total_bytes_read;
}
else
{
// We have to load to a temporary buffer
uint8_t *temp_buf = (uint8_t *)malloc(fs_block_size);
int bytes_read = block_read(fs->parent, temp_buf, fs_block_size, cur_bdev_block);
if(last_block_offset > (uint32_t)bytes_read)
last_block_offset = bytes_read;
if(last_block_offset < start_block_offset)
block_segment_length = 0;
else
block_segment_length = last_block_offset - start_block_offset;
// Copy from the temporary buffer to the save buffer
qmemcpy(save_buf, &temp_buf[start_block_offset], block_segment_length);
// Increment the pointers
total_bytes_read += block_segment_length;
stream->pos += block_segment_length;
save_buf += block_segment_length;
free(temp_buf);
if((uint32_t)bytes_read != fs_block_size)
return total_bytes_read;
}
cur_block++;
}
return total_bytes_read;
}
size_t fs_fwrite(uint32_t (*get_next_bdev_block_num)(uint32_t f_block_idx, FILE *s, void *opaque, int add_blocks),
struct fs *fs, void *ptr, size_t byte_size,
FILE *stream, void *opaque)
{
uint32_t fs_block_size = fs->block_size;
// Files opened in mode "a+" always set the stream position to the end of the file before writing
if((stream->mode & VFS_MODE_APPEND) && (stream->mode & VFS_MODE_R))
stream->pos = stream->len;
// Determine first and last block indices within file
uint32_t first_f_block_idx = stream->pos / fs_block_size;
uint32_t first_f_block_offset = stream->pos % fs_block_size;
uint32_t last_pos = stream->pos + byte_size;
uint32_t last_f_block_idx = last_pos / fs_block_size;
uint32_t last_f_block_offset = last_pos % fs_block_size;
// Now iterate through the blocks
uint32_t cur_block = first_f_block_idx;
uint8_t *save_buf = (uint8_t *)ptr;
int total_bytes_written = 0;
while(cur_block <= last_f_block_idx)
{
uint32_t start_block_offset = 0;
uint32_t last_block_offset = fs_block_size;
// If we're the first block, adjust start_block_idx appropriately
if(cur_block == first_f_block_idx)
start_block_offset = first_f_block_offset;
// If we're the last block, adjust last_block_idx appropriately
if(cur_block == last_f_block_idx)
last_block_offset = last_f_block_offset;
uint32_t block_segment_length = last_block_offset - start_block_offset;
// Get the filesystem block number
uint32_t cur_bdev_block = get_next_bdev_block_num(cur_block, stream, opaque, 1);
if(cur_bdev_block == 0xffffffff)
return total_bytes_written;
// If we can save an entire block, save it directly, else we have
// to load to a buffer somewhere, edit, and save
if((start_block_offset == 0) && (block_segment_length == fs_block_size))
{
size_t bytes_written = block_write(fs->parent, save_buf, fs_block_size, cur_bdev_block);
total_bytes_written += bytes_written;
stream->pos += bytes_written;
if(stream->pos > stream->len)
stream->len = stream->pos;
save_buf += bytes_written;
if(bytes_written != fs_block_size)
return total_bytes_written;
}
else
{
// We have to load to a temporary buffer
uint8_t *temp_buf = (uint8_t *)malloc(fs_block_size);
size_t bytes_read = block_read(fs->parent, temp_buf, fs_block_size, cur_bdev_block);
if(bytes_read != fs_block_size)
return total_bytes_written;
// Edit the buffer
qmemcpy(&temp_buf[start_block_offset], save_buf, block_segment_length);
// Save the buffer
size_t bytes_written = block_write(fs->parent, temp_buf, fs_block_size, cur_bdev_block);
if(last_block_offset > bytes_written)
last_block_offset = bytes_written;
if(last_block_offset < start_block_offset)
block_segment_length = 0;
else
block_segment_length = last_block_offset - start_block_offset;
// Increment the pointers
total_bytes_written += block_segment_length;
stream->pos += block_segment_length;
if(stream->pos > stream->len)
stream->len = stream->pos;
save_buf += block_segment_length;
free(temp_buf);
if(bytes_written != fs_block_size)
return total_bytes_written;
}
cur_block++;
}
return total_bytes_written;
}