phatvol.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /*
  2. * Copyright (C) 2005-2006 by egnite Software GmbH. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. */
  32. /*!
  33. * \file fs/phatvol.c
  34. * \brief Volume related routines of the PHAT file system.
  35. *
  36. * When mounting a partition, we expect the block device driver to call
  37. * the related mount routine of the file system driver.
  38. *
  39. * \verbatim
  40. *
  41. * $Log$
  42. * Revision 1.11 2009/02/13 14:52:05 haraldkipp
  43. * Include memdebug.h for heap management debugging support.
  44. *
  45. * Revision 1.10 2008/08/11 06:59:42 haraldkipp
  46. * BSD types replaced by stdint types (feature request #1282721).
  47. *
  48. * Revision 1.9 2006/10/08 16:42:56 haraldkipp
  49. * Not optimal, but simple and reliable exclusive access implemented.
  50. * Fixes bug #1486539. Furthermore, bug #1567790, which had been rejected,
  51. * had been reported correctly and is now fixed.
  52. *
  53. * Revision 1.8 2006/07/11 12:20:19 haraldkipp
  54. * PHAT file system failed when accessed from multiple threads. A mutual
  55. * exclusion semaphore fixes this.
  56. *
  57. * Revision 1.7 2006/07/10 08:48:47 haraldkipp
  58. * Automatically detect FAT12 and FAT16 volumes when no partition table
  59. * is provided.
  60. *
  61. * Revision 1.6 2006/06/18 16:40:34 haraldkipp
  62. * No need to set errno after malloc failed.
  63. *
  64. * Revision 1.5 2006/05/15 11:49:47 haraldkipp
  65. * Added support for media formats without partition table like USB sticks.
  66. *
  67. * Revision 1.4 2006/04/07 12:56:18 haraldkipp
  68. * Several memory holes fixed.
  69. *
  70. * Revision 1.3 2006/02/23 15:45:22 haraldkipp
  71. * PHAT file system now supports configurable number of sector buffers.
  72. * This dramatically increased write rates of no-name cards.
  73. * AVR compile errors corrected.
  74. *
  75. * Revision 1.2 2006/01/22 17:38:06 haraldkipp
  76. * If mounting fails, the occupied resources are no longer released in
  77. * PhatVolMount(). Instead the caller, PhatIOCtl() in this case, calls
  78. * PhatVolUnmount(). This reduces the code size and makes sure, the all
  79. * resources are released in all cases.
  80. *
  81. * Revision 1.1 2006/01/05 16:31:56 haraldkipp
  82. * First check-in.
  83. *
  84. *
  85. * \endverbatim
  86. */
  87. #include <dev/blockdev.h>
  88. #include <sys/event.h>
  89. #include <fs/dospart.h>
  90. #include <fs/phatio.h>
  91. #include <fs/phatutil.h>
  92. #include <fs/phatvol.h>
  93. #include <errno.h>
  94. #include <stdlib.h>
  95. #include <string.h>
  96. #include <memdebug.h>
  97. #if defined(NUTDEBUG)
  98. #include <stdio.h>
  99. #include <fs/phatdbg.h>
  100. #endif
  101. /*!
  102. * \addtogroup xgPhatVol
  103. */
  104. /*@{*/
  105. /*!
  106. * \brief Counts the number of free clusters in a volume.
  107. *
  108. * \param dev Specifies the file system device.
  109. *
  110. * \return The number of free clusters.
  111. */
  112. static uint32_t PhatCountFreeClusters(NUTDEVICE * dev)
  113. {
  114. uint32_t rc = 0;
  115. uint32_t i = 2;
  116. uint32_t link;
  117. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  118. if (vol->vol_type == 32) {
  119. /* Use fast verion for FAT32. */
  120. rc = Phat32FreeClusters(dev);
  121. } else if (vol->vol_type == 16) {
  122. while (i < vol->vol_last_clust) {
  123. if (Phat16GetClusterLink(dev, i, &link)) {
  124. break;
  125. }
  126. if (link == 0) {
  127. rc++;
  128. }
  129. i++;
  130. }
  131. } else {
  132. while (i < vol->vol_last_clust) {
  133. if (Phat12GetClusterLink(dev, i, &link)) {
  134. break;
  135. }
  136. if (link == 0) {
  137. rc++;
  138. }
  139. i++;
  140. }
  141. }
  142. return rc;
  143. }
  144. /*!
  145. * \brief Mount a PHAT volume.
  146. *
  147. * This routine is called by the block device driver while mounting a
  148. * partition. It reads and verifies the volume boot record, which is
  149. * located in the first sector of a volume.
  150. *
  151. * The routine may also initializes any caching mechanism. Thus, it must
  152. * be called before any other read or write access.
  153. *
  154. * \param dev Specifies the file system device.
  155. * \param blkmnt Handle of the block device's partition mount.
  156. * \param part_type Partition type:
  157. * - PTYPE_FAT32
  158. * - PTYPE_FAT32_LBA
  159. * - PTYPE_FAT16
  160. * - PTYPE_FAT16_BIG
  161. * - PTYPE_FAT16_LBA
  162. * - PTYPE_FAT12
  163. *
  164. * \return 0 on success or -1 in case of an error.
  165. */
  166. int PhatVolMount(NUTDEVICE * dev, NUTFILE * blkmnt, uint8_t part_type)
  167. {
  168. PHATVOL *vol;
  169. PHATVBR *vbr;
  170. BLKPAR_INFO pari;
  171. int sbn;
  172. NUTDEVICE *blkdev = blkmnt->nf_dev;
  173. /*
  174. * Allocate the volume information structure
  175. */
  176. if ((dev->dev_dcb = malloc(sizeof(PHATVOL))) == 0) {
  177. return -1;
  178. }
  179. vol = (PHATVOL *) memset(dev->dev_dcb, 0, sizeof(PHATVOL));
  180. /*
  181. * Determine the PHAT type.
  182. */
  183. switch (part_type) {
  184. case PTYPE_FAT32:
  185. case PTYPE_FAT32_LBA:
  186. vol->vol_type = 32;
  187. break;
  188. case PTYPE_FAT16:
  189. case PTYPE_FAT16_BIG:
  190. case PTYPE_FAT16_LBA:
  191. vol->vol_type = 16;
  192. break;
  193. case PTYPE_FAT12:
  194. vol->vol_type = 12;
  195. break;
  196. }
  197. /*
  198. * Query information from the block device driver.
  199. */
  200. pari.par_nfp = blkmnt;
  201. if ((*blkdev->dev_ioctl) (blkdev, NUTBLKDEV_INFO, &pari)) {
  202. free(vol);
  203. errno = ENODEV;
  204. return -1;
  205. }
  206. #if PHAT_SECTOR_BUFFERS
  207. for (sbn = 0; sbn < PHAT_SECTOR_BUFFERS; sbn++) {
  208. if ((vol->vol_buf[sbn].sect_data = malloc(pari.par_blksz)) == NULL) {
  209. PhatVolUnmount(dev);
  210. return -1;
  211. }
  212. vol->vol_buf[sbn].sect_num = (uint32_t)-1;
  213. }
  214. #else
  215. vol->vol_buf[0].sect_data = pari.par_blkbp;
  216. #endif
  217. sbn = 0;
  218. /*
  219. * We use PhatSectorRead() instead of PhatSectorLoad() for our
  220. * very first read to properly initialize the caching status.
  221. */
  222. if (PhatSectorRead(blkmnt, 0, vol->vol_buf[sbn].sect_data)) {
  223. PhatVolUnmount(dev);
  224. return -1;
  225. }
  226. vol->vol_buf[sbn].sect_num = 0;
  227. vbr = (PHATVBR *) vol->vol_buf[sbn].sect_data;
  228. /*
  229. * PHAT32 doesn't have a fixed root directory. At this point
  230. * we reliably know wether we got PHAT32 or not. After having
  231. * determined the total number of clusters later, we can check
  232. * for PHAT12 or PHAT16.
  233. */
  234. if (vol->vol_type == 0 && vbr->bios_rootsz == 0) {
  235. vol->vol_type = 32;
  236. }
  237. /* Convert to PHAT12/PHAT16 layout. */
  238. if (vol->vol_type != 32) {
  239. memcpy(&vbr->boot_drive, &vbr->bios_tabsz_big, 26);
  240. memset(&vbr->bios_tabsz_big, 0, 28);
  241. }
  242. #ifdef NUTDEBUG
  243. PhatDbgVbr(stdout, "Volume Boot Record", vbr);
  244. #endif
  245. /*
  246. * Verify the VBR signature.
  247. */
  248. if (vol->vol_buf[sbn].sect_data[510] != 0x55 || vol->vol_buf[sbn].sect_data[511] != 0xAA) {
  249. PhatVolUnmount(dev);
  250. errno = ENODEV;
  251. return -1;
  252. }
  253. /*
  254. * Make sure we got a valid media type.
  255. */
  256. if (vbr->bios_media != 0xF0 && vbr->bios_media < 0xF8) {
  257. PhatVolUnmount(dev);
  258. errno = ENODEV;
  259. return -1;
  260. }
  261. /*
  262. * Examine the informations found in the boot record.
  263. */
  264. /* Bytes per sector. */
  265. vol->vol_sectsz = vbr->bios_sectsz;
  266. if (vol->vol_sectsz < 512 || vol->vol_sectsz & 0xFF) {
  267. PhatVolUnmount(dev);
  268. errno = ENODEV;
  269. return -1;
  270. }
  271. /* Sectors per cluster. */
  272. if ((vol->vol_clustsz = vbr->bios_clustsz) == 0) {
  273. PhatVolUnmount(dev);
  274. errno = ENODEV;
  275. return -1;
  276. }
  277. /* Allocation table size and position. */
  278. if (vbr->bios_tabsz) {
  279. vol->vol_tabsz = vbr->bios_tabsz;
  280. } else {
  281. vol->vol_tabsz = vbr->bios_tabsz_big;
  282. }
  283. vol->vol_tab_sect[0] = vbr->bios_rsvd_sects;
  284. if (vbr->bios_ntabs > 1) {
  285. vol->vol_tab_sect[1] = vol->vol_tab_sect[0] + vol->vol_tabsz;
  286. }
  287. /* Root directory size and position. */
  288. vol->vol_rootsz = (vbr->bios_rootsz * sizeof(PHATDIRENT) + /* */
  289. vol->vol_sectsz - 1) / vol->vol_sectsz;
  290. vol->vol_root_sect = vbr->bios_rsvd_sects + /* */
  291. vbr->bios_ntabs * vol->vol_tabsz;
  292. if (vol->vol_type == 32) {
  293. vol->vol_root_clust = vbr->bios_root_clust;
  294. }
  295. /* First data sector and number of data clusters. */
  296. vol->vol_data_sect = vol->vol_root_sect + vol->vol_rootsz;
  297. if (vbr->bios_volsz) {
  298. vol->vol_last_clust = vbr->bios_volsz - vol->vol_data_sect;
  299. } else {
  300. vol->vol_last_clust = vbr->bios_volsz_big - vol->vol_data_sect;
  301. }
  302. vol->vol_last_clust /= vol->vol_clustsz;
  303. /* First cluster number is 2. */
  304. vol->vol_last_clust += 2;
  305. /*
  306. * Having calculated the total number of clusters allows us to
  307. * distinguish between PHAT12 and PHAT16.
  308. */
  309. if (vol->vol_type == 0) {
  310. if (vol->vol_last_clust > 4086) {
  311. vol->vol_type = 16;
  312. }
  313. else {
  314. vol->vol_type = 12;
  315. }
  316. }
  317. #ifdef NUTDEBUG
  318. printf("\n%lu cluster -> PHAT%d\n", vol->vol_last_clust, vol->vol_type);
  319. #endif
  320. dev->dev_icb = blkmnt;
  321. /* Initialize mutual exclusion semaphores. */
  322. NutEventPost(&vol->vol_fsmutex);
  323. NutEventPost(&vol->vol_iomutex);
  324. vol->vol_numfree = PhatCountFreeClusters(dev);
  325. return 0;
  326. }
  327. /*!
  328. * \brief Unmount a PHAT volume.
  329. *
  330. * This routine is called by the block device driver while unmounting a
  331. * partition.
  332. *
  333. * \param dev Specifies the file system device.
  334. *
  335. * \return 0 on success or -1 in case of an error.
  336. */
  337. int PhatVolUnmount(NUTDEVICE * dev)
  338. {
  339. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  340. if (vol) {
  341. #if PHAT_SECTOR_BUFFERS
  342. int sbn;
  343. for (sbn = 0; sbn < PHAT_SECTOR_BUFFERS; sbn++) {
  344. if (vol->vol_buf[sbn].sect_data) {
  345. free(vol->vol_buf[sbn].sect_data);
  346. }
  347. }
  348. #endif
  349. free(vol);
  350. }
  351. return 0;
  352. }
  353. /*
  354. * \brief Get first sector of a specified cluster.
  355. *
  356. * \param nfp File descriptor.
  357. * \param clust Specified cluster.
  358. */
  359. uint32_t PhatClusterSector(NUTFILE * nfp, uint32_t clust)
  360. {
  361. NUTDEVICE *dev = nfp->nf_dev;
  362. PHATVOL *vol = (PHATVOL *) dev->dev_dcb;
  363. /*
  364. * If the file descriptor specifies the root directory, then the first
  365. * sector is located after the reserved sectors and the allocation table.
  366. */
  367. if (IsFixedRootDir(nfp)) {
  368. return vol->vol_root_sect;
  369. }
  370. /*
  371. * For all other files/directories the sector is located in the data
  372. * area of the volume.
  373. */
  374. if (clust >= 2) {
  375. clust -= 2;
  376. }
  377. return vol->vol_data_sect + clust * vol->vol_clustsz;
  378. }
  379. /*@}*/