Newer
Older
/****************************************************************************
* fs_fat32util.c
*
* Copyright (C) 2007, 2008 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* Some good ideas were leveraged from the FAT implementation:
* 'Copyright (C) 2007, ChaN, all right reserved.'
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <semaphore.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/fs.h>
#include "fs_internal.h"
#include "fs_fat32.h"
/****************************************************************************
* Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Private Variables
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Desciption: Convert a user filename into a properly formatted FAT
* (short) filname as it would appear in a directory entry. Here are the
* rules for the 11 byte name in the directory:
* The first byte:
* - 0xe5 = The directory is free
* - 0x00 = This directory and all following directories are free
* - 0x05 = Really 0xe5
* - 0x20 = May NOT be ' '
* Any bytes
* 0x00-0x1f = (except for 0x00 and 0x05 in the first byte)
* 0x22 = '"'
* 0x2a-0x2c = '*', '+', ','
* 0x2e-0x2f = '.', '/'
* 0x3a-0x3f = ':', ';', '<', '=', '>', '?'
* 0x5b-0x5d = '[', '\\', ;]'
* 0x7c = '|'
*
* Upper case characters are not allowed in directory names (without some
* poorly documented operatgions on the NTRes directory byte). Lower case
* codes may represent different characters in other character sets ("DOS
* code pages". The logic below does not, at present, support any other
* character sets.
*
****************************************************************************/
static inline int fat_path2dirname(const char **path, struct fat_dirinfo_s *dirinfo,
char *terminator)
#ifdef CONFIG_FAT_LCNAMES
unsigned int ntlcenable = FATNTRES_LCNAME | FATNTRES_LCEXT;
unsigned int ntlcfound = 0;
#endif
const char *node = *path;
int endndx;
ubyte ch;
int ndx = 0;
memset(dirinfo->fd_name, ' ', 8+3);
/* Loop until the name is successfully parsed or an error occurs */
endndx = 8;
for (;;)
{
/* Get the next byte from the path */
/* Check if this the last byte in this node of the name */
if ((ch == '\0' || ch == '/') && ndx != 0 )
{
/* Return the accumulated NT flags and the terminating character */
#ifdef CONFIG_FAT_LCNAMES
dirinfo->fd_ntflags = ntlcfound & ntlcenable;
#endif
*terminator = ch;
*path = node;
return OK;
}
/* Accept only the printable character set. Note the first byte
* of the name could be 0x05 meaning that is it 0xe5, but this is
* not a printable character in this character in either case.
*/
else if (!isgraph(ch))
{
goto errout;
}
else if (ch == '.')
{
/* Starting the extension */
/* Reject printable characters forbidden by FAT */
else if (ch == '"' || (ch >= '*' && ch <= ',') ||
ch == '.' || ch == '/' ||
(ch >= ':' && ch <= '?') ||
(ch >= '[' && ch <= ']') ||
(ch == '|'))
{
goto errout;
}
#ifdef CONFIG_FAT_LCNAMES
else if (isupper(ch))
{
/* Some or all of the characters in the name or extension
* are upper case. Force all of the characters to be interpreted
* as upper case.
*/
if ( endndx == 8)
/* Clear lower case name bit in mask*/
ntlcenable &= FATNTRES_LCNAME;
/* Clear lower case extension in mask */
ntlcenable &= FATNTRES_LCNAME;
else if (islower(ch))
{
/* Convert the character to upper case */
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
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/* Some or all of the characters in the name or extension
* are lower case. They can be interpreted as lower case if
* only if all of the characters in the name or extension are
* lower case.
*/
#ifdef CONFIG_FAT_LCNAMES
if ( endndx == 8)
{
/* Set lower case name bit */
ntlcfound |= FATNTRES_LCNAME;
}
else
{
/* Set lower case extension bit */
ntlcfound |= FATNTRES_LCNAME;
}
#endif
}
/* Check if the file name exceeds the size permitted (without
* long file name support
*/
if (ndx >= endndx)
{
goto errout;
}
/* Save next character in the accumulated name */
dirinfo->fd_name[ndx++] = ch;
}
errout:
return -EINVAL;
}
/****************************************************************************
* Name: fat_checkfsinfo
*
* Desciption: Read the FAT32 FSINFO sector
*
****************************************************************************/
static int fat_checkfsinfo(struct fat_mountpt_s *fs)
{
/* Verify that this is, indeed, an FSINFO sector */
if (FSI_GETLEADSIG(fs->fs_buffer) == 0x41615252 &&
FSI_GETSTRUCTSIG(fs->fs_buffer) == 0x61417272 &&
FSI_GETTRAILSIG(fs->fs_buffer) == 0xaa550000)
{
fs->fs_fsinextfree = FSI_GETFREECOUNT(fs->fs_buffer);
fs->fs_fsifreecount = FSI_GETNXTFREE(fs->fs_buffer);
return OK;
}
return -ENODEV;
}
/****************************************************************************
* Name: fat_checkbootrecord
*
* Desciption: Read a sector and verify that it is a a FAT boot record.
*
****************************************************************************/
static int fat_checkbootrecord(struct fat_mountpt_s *fs)
{
uint32 ndatasectors;
uint32 fatsize;
uint16 rootdirsectors = 0;
boolean notfat32 = FALSE;
/* Verify the MBR signature at offset 510 in the sector (true even
* if the sector size is greater than 512. All FAT file systems have
* this signature. On a FAT32 volume, the RootEntCount , FatSz16, and
* FatSz32 values should always be zero. The FAT sector size should
* match the reported hardware sector size.
*/
if (MBR_GETSIGNATURE(fs->fs_buffer) != 0xaa55 ||
MBR_GETBYTESPERSEC(fs->fs_buffer) != fs->fs_hwsectorsize)
{
return -ENODEV;
}
/* Verify the FAT32 file system type. The determination of the file
* system type is based on the number of clusters on the volume: FAT12
* volume has < 4085 cluseter, a FAT16 volume has fewer than 65,525
* clusters, and any larger is FAT32.
*
* Get the number of 32-bit directory entries in root directory (zero
* for FAT32.
*/
fs->fs_rootentcnt = MBR_GETROOTENTCNT(fs->fs_buffer);
if (fs->fs_rootentcnt != 0)
{
notfat32 = TRUE; /* Must be zero for FAT32 */
rootdirsectors = (32 * fs->fs_rootentcnt + fs->fs_hwsectorsize - 1) / fs->fs_hwsectorsize;
}
/* Determine the number of sectors in a FAT. */
fs->fs_fatsize = MBR_GETFATSZ16(fs->fs_buffer); /* Should be zero */
if (fs->fs_fatsize)
{
notfat32 = TRUE; /* Must be zero for FAT32 */
}
else
{
fs->fs_fatsize = MBR_GETFATSZ32(fs->fs_buffer);
}
if (!fs->fs_fatsize || fs->fs_fatsize >= fs->fs_hwnsectors)
{
return -ENODEV;
}
/* Get the total number of sectors on the volume. */
fs->fs_fattotsec = MBR_GETTOTSEC16(fs->fs_buffer); /* Should be zero */
if (fs->fs_fattotsec)
{
notfat32 = TRUE; /* Must be zero for FAT32 */
}
else
{
fs->fs_fattotsec = MBR_GETTOTSEC32(fs->fs_buffer);
}
if (!fs->fs_fattotsec || fs->fs_fattotsec > fs->fs_hwnsectors)
{
return -ENODEV;
}
/* Get the total number of reserved sectors */
fs->fs_fatresvdseccount = MBR_GETRESVDSECCOUNT(fs->fs_buffer);
if (fs->fs_fatresvdseccount > fs->fs_hwnsectors)
{
return -ENODEV;
}
/* Get the number of FATs. This is probably two but could have other values */
fs->fs_fatnumfats = MBR_GETNUMFATS(fs->fs_buffer);
fatsize = fs->fs_fatnumfats * fs->fs_fatsize;
/* Get the total number of data sectors */
ndatasectors = fs->fs_fattotsec - fs->fs_fatresvdseccount - fatsize - rootdirsectors;
if (ndatasectors > fs->fs_hwnsectors)
{
return -ENODEV;
}
/* Get the sectors per cluster */
fs->fs_fatsecperclus = MBR_GETSECPERCLUS(fs->fs_buffer);
/* Calculate the number of clusters */
fs->fs_nclusters = ndatasectors / fs->fs_fatsecperclus;
/* Finally, the test: */
if (fs->fs_nclusters < 4085)
{
fs->fs_fsinfo = 0;
fs->fs_type = FSTYPE_FAT12;
}
else if (fs->fs_nclusters < 65525)
{
fs->fs_fsinfo = 0;
fs->fs_type = FSTYPE_FAT16;
}
else if (!notfat32)
{
fs->fs_fsinfo = fs->fs_fatbase + MBR_GETFSINFO(fs->fs_buffer);
fs->fs_type = FSTYPE_FAT32;
}
else
{
return -ENODEV;
}
/* We have what appears to be a valid FAT filesystem! Save a few more things
* from the boot record that we will need later.
*/
fs->fs_fatbase += fs->fs_fatresvdseccount;
if (fs->fs_type == FSTYPE_FAT32)
{
fs->fs_rootbase = MBR_GETROOTCLUS(fs->fs_buffer);
}
else
{
fs->fs_rootbase = fs->fs_fatbase + fatsize;
}
fs->fs_database = fs->fs_fatbase + fatsize + fs->fs_rootentcnt / DIRSEC_NDIRS(fs);
fs->fs_fsifreecount = 0xffffffff;
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: fat_getuint16
****************************************************************************/
uint16 fat_getuint16(ubyte *ptr)
{
/* The bytes always have to be swapped if the target is big-endian */
return ((uint16)ptr[0] << 8) | ptr[1];
#else
/* Byte-by-byte transfer is still necessary if the address is un-aligned */
return ((uint16)ptr[1] << 8) | ptr[0];
#endif
}
/****************************************************************************
* Name: fat_getuint32
****************************************************************************/
uint32 fat_getuint32(ubyte *ptr)
{
/* The bytes always have to be swapped if the target is big-endian */
return ((uint32)fat_getuint16(&ptr[0]) << 16) | fat_getuint16(&ptr[2]);
#else
/* Byte-by-byte transfer is still necessary if the address is un-aligned */
return ((uint32)fat_getuint16(&ptr[2]) << 16) | fat_getuint16(&ptr[0]);
#endif
}
/****************************************************************************
* Name: fat_putuint16
****************************************************************************/
void fat_putuint16(ubyte *ptr, uint16 value16)
{
ubyte *val = (ubyte*)&value16;
/* The bytes always have to be swapped if the target is big-endian */
ptr[0] = val[1];
ptr[1] = val[0];
#else
/* Byte-by-byte transfer is still necessary if the address is un-aligned */
ptr[0] = val[0];
ptr[1] = val[1];
#endif
}
/****************************************************************************
* Name: fat_putuint32
****************************************************************************/
void fat_putuint32(ubyte *ptr, uint32 value32)
{
uint16 *val = (uint16*)&value32;
/* The bytes always have to be swapped if the target is big-endian */
fat_putuint16(&ptr[0], val[2]);
fat_putuint16(&ptr[2], val[0]);
#else
/* Byte-by-byte transfer is still necessary if the address is un-aligned */
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
fat_putuint16(&ptr[0], val[0]);
fat_putuint16(&ptr[2], val[2]);
#endif
}
/****************************************************************************
* Name: fat_semtake
****************************************************************************/
void fat_semtake(struct fat_mountpt_s *fs)
{
/* Take the semaphore (perhaps waiting) */
while (sem_wait(&fs->fs_sem) != 0)
{
/* The only case that an error should occur here is if
* the wait was awakened by a signal.
*/
ASSERT(*get_errno_ptr() == EINTR);
}
}
/****************************************************************************
* Name: fat_semgive
****************************************************************************/
void fat_semgive(struct fat_mountpt_s *fs)
{
sem_post(&fs->fs_sem);
}
/****************************************************************************
* Name: fat_systime2fattime
*
* Desciption: Get the system time convertto a time and and date suitble
* for writing into the FAT FS.
*
* TIME in LS 16-bits:
* Bits 0:4 = 2 second count (0-29 representing 0-58 seconds)
* Bits 5-10 = minutes (0-59)
* Bits 11-15 = hours (0-23)
* DATE in MS 16-bits
* Bits 0:4 = Day of month (0-31)
* Bits 5:8 = Month of year (1-12)
* Bits 9:15 = Year from 1980 (0-127 representing 1980-2107)
*
****************************************************************************/
uint32 fat_systime2fattime(void)
{
#warning "Time not implemented"
return 0;
}
/****************************************************************************
* Name: fat_fattime2systime
*
* Desciption: Convert FAT data and time to a system time_t
*
* 16-bit FAT time:
* Bits 0:4 = 2 second count (0-29 representing 0-58 seconds)
* Bits 5-10 = minutes (0-59)
* Bits 11-15 = hours (0-23)
* 16-bit FAT date:
* Bits 0:4 = Day of month (0-31)
* Bits 5:8 = Month of year (1-12)
* Bits 9:15 = Year from 1980 (0-127 representing 1980-2107)
*
****************************************************************************/
{
#warning "Time not implemented"
return 0;
}
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
/****************************************************************************
* Name: fat_mount
*
* Desciption: This function is called only when the mountpoint is first
* established. It initializes the mountpoint structure and verifies
* that a valid FAT32 filesystem is provided by the block driver.
*
* The caller should hold the mountpoint semaphore
*
****************************************************************************/
int fat_mount(struct fat_mountpt_s *fs, boolean writeable)
{
FAR struct inode *inode;
struct geometry geo;
int ret;
/* Assume that the mount is successful */
fs->fs_mounted = TRUE;
/* Check if there is media available */
inode = fs->fs_blkdriver;
if (!inode || !inode->u.i_bops || !inode->u.i_bops->geometry ||
inode->u.i_bops->geometry(inode, &geo) != OK || !geo.geo_available)
{
ret = -ENODEV;
goto errout;
}
/* Make sure that that the media is write-able (if write access is needed) */
if (writeable && !geo.geo_writeenabled)
{
ret = -EACCES;
goto errout;
}
/* Save the hardware geometry */
fs->fs_hwsectorsize = geo.geo_sectorsize;
fs->fs_hwnsectors = geo.geo_nsectors;
/* Allocate a buffer to hold one hardware sector */
fs->fs_buffer = (ubyte*)malloc(fs->fs_hwsectorsize);
if (!fs->fs_buffer)
{
ret = -ENOMEM;
goto errout;
}
/* Search FAT boot record on the drive. First check at sector zero. This
* could be either the boot record or a partition that refers to the boot
* record.
*
* First read sector zero. This will be the first access to the drive and a
* likely failure point.
*/
fs->fs_fatbase = 0;
ret = fat_hwread(fs, fs->fs_buffer, 0, 1);
if (ret < 0)
{
goto errout_with_buffer;
}
if (fat_checkbootrecord(fs) != OK)
{
/* The contents of sector 0 is not a boot record. It could be a
* partition, however. Assume it is a partition and get the offset
* into the partition table. This table is at offset MBR_TABLE and is
* indexed by 16x the partition number. Here we support only
* parition 0.
*/
ubyte *partition = &fs->fs_buffer[MBR_TABLE + 0];
/* Check if the partition exists and, if so, get the bootsector for that
* partition and see if we can find the boot record there.
*/
if (partition[4])
{
/* There appears to be a partition, get the sector number of the
* partition (LBA)
*/
fs->fs_fatbase = MBR_GETPARTSECTOR(&partition[8]);
/* Read the new candidate boot sector */
ret = fat_hwread(fs, fs->fs_buffer, fs->fs_fatbase, 1);
if (ret < 0)
{
goto errout_with_buffer;
}
/* Check if this is a boot record */
if (fat_checkbootrecord(fs) != OK)
{
goto errout_with_buffer;
}
}
}
/* We have what appears to be a valid FAT filesystem! Now read the
* FSINFO sector (FAT32 only)
*/
if (fs->fs_type == FSTYPE_FAT32)
{
ret = fat_checkfsinfo(fs);
if (ret != OK)
{
goto errout_with_buffer;
}
}
/* We did it! */
fdbg("FAT%d:\n", fs->fs_type == 0 ? 12 : fs->fs_type == 1 ? 16 : 32);
fdbg("\tHW sector size: %d\n", fs->fs_hwsectorsize);
fdbg("\t sectors: %d\n", fs->fs_hwnsectors);
fdbg("\tFAT reserved: %d\n", fs->fs_fatresvdseccount);
fdbg("\t sectors: %d\n", fs->fs_fattotsec);
fdbg("\t start sector: %d\n", fs->fs_fatbase);
fdbg("\t root sector: %d\n", fs->fs_rootbase);
fdbg("\t root entries: %d\n", fs->fs_rootentcnt);
fdbg("\t data sector: %d\n", fs->fs_database);
fdbg("\t FSINFO sector: %d\n", fs->fs_fsinfo);
fdbg("\t Num FATs: %d\n", fs->fs_fatnumfats);
fdbg("\t FAT size: %d\n", fs->fs_fatsize);
fdbg("\t sectors/cluster: %d\n", fs->fs_fatsecperclus);
fdbg("\t max clusters: %d\n", fs->fs_nclusters);
fdbg("\tFSI free count %d\n", fs->fs_fsifreecount);
fdbg("\t next free %d\n", fs->fs_fsinextfree);
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
return OK;
errout_with_buffer:
free(fs->fs_buffer);
fs->fs_buffer = 0;
errout:
fs->fs_mounted = FALSE;
return ret;
}
/****************************************************************************
* Name: fat_checkmount
*
* Desciption: Check if the mountpoint is still valid.
*
* The caller should hold the mountpoint semaphore
*
****************************************************************************/
int fat_checkmount(struct fat_mountpt_s *fs)
{
/* If the fs_mounted flag is FALSE, then we have already handled the loss
* of the mount.
*/
if (fs && fs->fs_mounted)
{
struct fat_file_s *file;
/* We still think the mount is healthy. Check an see if this is
* still the case
*/
if (fs->fs_blkdriver)
{
struct inode *inode = fs->fs_blkdriver;
if (inode && inode->u.i_bops && inode->u.i_bops->geometry)
{
struct geometry geo;
int errcode = inode->u.i_bops->geometry(inode, &geo);
if (errcode == OK && geo.geo_available && !geo.geo_mediachanged)
{
return OK;
}
}
}
/* If we get here, the mount is NOT healthy */
fs->fs_mounted = FALSE;
/* Make sure that this is flagged in every opened file */
for (file = fs->fs_head; file; file = file->ff_next)
{
file->ff_open = FALSE;
}
}
return -ENODEV;
}
/****************************************************************************
* Name: fat_hwread
*
* Desciption: Read the specified sector into the sector buffer
*
****************************************************************************/
int fat_hwread(struct fat_mountpt_s *fs, ubyte *buffer, size_t sector,
unsigned int nsectors)
{
int ret = -ENODEV;
if (fs && fs->fs_blkdriver )
{
struct inode *inode = fs->fs_blkdriver;
if (inode && inode->u.i_bops && inode->u.i_bops->read)
{
ssize_t nSectorsRead = inode->u.i_bops->read(inode, buffer,
sector, nsectors);
if (nSectorsRead == nsectors)
{
ret = OK;
}
else if (nSectorsRead < 0)
{
ret = nSectorsRead;
}
}
}
return ret;
}
/****************************************************************************
* Name: fat_hwwrite
*
* Desciption: Write the sector buffer to the specified sector
*
****************************************************************************/
int fat_hwwrite(struct fat_mountpt_s *fs, ubyte *buffer, size_t sector,
unsigned int nsectors)
int ret = -ENODEV;
if (fs && fs->fs_blkdriver )
struct inode *inode = fs->fs_blkdriver;
if (inode && inode->u.i_bops && inode->u.i_bops->write)
{
ssize_t nSectorsWritten =
inode->u.i_bops->write(inode, buffer, sector, nsectors);
if (nSectorsWritten == nsectors)
{
ret = OK;
}
else if (nSectorsWritten < 0)
{
ret = nSectorsWritten;
}
}
}
/****************************************************************************
* Name: fat_cluster2sector
*
* Desciption: Convert a cluster number to a start sector number
*
****************************************************************************/
ssize_t fat_cluster2sector(struct fat_mountpt_s *fs, uint32 cluster )
cluster -= 2;
if (cluster >= fs->fs_nclusters - 2)
{
return -EINVAL;
}
return cluster * fs->fs_fatsecperclus + fs->fs_database;
}
/****************************************************************************
* Desciption: Get the cluster start sector into the FAT.
*
* Return: <0: error, 0:cluster unassigned, >=0: start sector of cluster
*
****************************************************************************/
ssize_t fat_getcluster(struct fat_mountpt_s *fs, uint32 clusterno)
/* Okay.. Read the next cluster from the FAT. The way we will do
* this depends on the type of FAT filesystm we are dealing with.
*/
size_t fatsector;
unsigned int fatoffset;
unsigned int startsector;
unsigned int fatindex;
/* FAT12 is more complex because it has 12-bits (1.5 bytes)
* per FAT entry. Get the offset to the first byte:
*/
fatoffset = (clusterno * 3) / 2;
fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
/* Get the first, LS byte of the cluster from the FAT */
fatindex = fatoffset & SEC_NDXMASK(fs);
startsector = fs->fs_buffer[fatindex];
/* With FAT12, the second byte of the cluster number may lie in
* a different sector than the first byte.
*/
/* Get the second, MS byte of the cluster for 16-bits. The
* does not depend on the endian-ness of the target, but only
* on the fact that the byte stream is little-endian.
*/
startsector |= (unsigned int)fs->fs_buffer[fatindex] << 8;
/* Now, pick out the correct 12 bit cluster start sector value */
/* Odd.. take the MS 12-bits */
startsector >>= 4;
/* Even.. take the LS 12-bits */
startsector &= 0x0fff;
unsigned int fatoffset = 2 * clusterno;
size_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
unsigned int fatoffset = 4 * clusterno;
size_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
return FAT_GETFAT16(fs->fs_buffer, fatindex) & 0x0fffffff;
/* There is no cluster information, or an error occured */
return (ssize_t)-EINVAL;
/****************************************************************************
* Name: fat_putcluster
*
* Desciption: Write a new cluster start sector into the FAT
*
****************************************************************************/
int fat_putcluster(struct fat_mountpt_s *fs, uint32 clusterno, size_t startsector)
/* Verify that the cluster number is within range. Zero erases the cluster. */
if (clusterno == 0 || (clusterno >= 2 && clusterno < fs->fs_nclusters))
{
/* Okay.. Write the next cluster into the FAT. The way we will do
* this depends on the type of FAT filesystm we are dealing with.
*/