libfs.c 10 KB

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