4 Functions used by the newlib disc stubs to interface with
7 Copyright (c) 2006 Michael "Chishm" Chisholm
9 Redistribution and use in source and binary forms, with or without modification,
10 are permitted provided that the following conditions are met:
12 1. Redistributions of source code must retain the above copyright notice,
13 this list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright notice,
15 this list of conditions and the following disclaimer in the documentation and/or
16 other materials provided with the distribution.
17 3. The name of the author may not be used to endorse or promote products derived
18 from this software without specific prior written permission.
20 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
21 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22 AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 #include "file_allocation_table.h"
47 int _FAT_open_r (struct _reent
*r
, void *fileStruct
, const char *path
, int flags
, int mode
) {
48 PARTITION
* partition
= NULL
;
53 FILE_STRUCT
* file
= (FILE_STRUCT
*) fileStruct
;
54 partition
= _FAT_partition_getPartitionFromPath (path
);
56 if (partition
== NULL
) {
61 // Move the path pointer to the start of the actual path
62 if (strchr (path
, ':') != NULL
) {
63 path
= strchr (path
, ':') + 1;
65 if (strchr (path
, ':') != NULL
) {
70 // Determine which mode the file is openned for
71 if ((flags
& 0x03) == O_RDONLY
) {
72 // Open the file for read-only access
76 } else if ((flags
& 0x03) == O_WRONLY
) {
77 // Open file for write only access
81 } else if ((flags
& 0x03) == O_RDWR
) {
82 // Open file for read/write access
91 // Make sure we aren't trying to write to a read-only disc
92 if (file
->write
&& partition
->readOnly
) {
97 // Search for the file on the disc
98 _FAT_lock(&partition
->lock
);
99 fileExists
= _FAT_directory_entryFromPath (partition
, &dirEntry
, path
, NULL
);
101 // The file shouldn't exist if we are trying to create it
102 if ((flags
& O_CREAT
) && (flags
& O_EXCL
) && fileExists
) {
103 _FAT_unlock(&partition
->lock
);
108 // It should not be a directory if we're openning a file,
109 if (fileExists
&& _FAT_directory_isDirectory(&dirEntry
)) {
110 _FAT_unlock(&partition
->lock
);
115 // We haven't modified the file yet
116 file
->modified
= false;
118 // If the file doesn't exist, create it if we're allowed to
120 if (flags
& O_CREAT
) {
121 if (partition
->readOnly
) {
122 // We can't write to a read-only partition
123 _FAT_unlock(&partition
->lock
);
128 // Get the directory it has to go in
129 pathEnd
= strrchr (path
, DIR_SEPARATOR
);
130 if (pathEnd
== NULL
) {
131 // No path was specified
132 dirCluster
= partition
->cwdCluster
;
135 // Path was specified -- get the right dirCluster
136 // Recycling dirEntry, since it needs to be recreated anyway
137 if (!_FAT_directory_entryFromPath (partition
, &dirEntry
, path
, pathEnd
) ||
138 !_FAT_directory_isDirectory(&dirEntry
)) {
139 _FAT_unlock(&partition
->lock
);
143 dirCluster
= _FAT_directory_entryGetCluster (partition
, dirEntry
.entryData
);
144 // Move the pathEnd past the last DIR_SEPARATOR
147 // Create the entry data
148 strncpy (dirEntry
.filename
, pathEnd
, MAX_FILENAME_LENGTH
- 1);
149 memset (dirEntry
.entryData
, 0, DIR_ENTRY_DATA_SIZE
);
151 // Set the creation time and date
152 dirEntry
.entryData
[DIR_ENTRY_cTime_ms
] = 0;
153 u16_to_u8array (dirEntry
.entryData
, DIR_ENTRY_cTime
, _FAT_filetime_getTimeFromRTC());
154 u16_to_u8array (dirEntry
.entryData
, DIR_ENTRY_cDate
, _FAT_filetime_getDateFromRTC());
156 if (!_FAT_directory_addEntry (partition
, &dirEntry
, dirCluster
)) {
157 _FAT_unlock(&partition
->lock
);
162 // File entry is modified
163 file
->modified
= true;
165 // file doesn't exist, and we aren't creating it
166 _FAT_unlock(&partition
->lock
);
172 file
->filesize
= u8array_to_u32 (dirEntry
.entryData
, DIR_ENTRY_fileSize
);
174 /* Allow LARGEFILEs with undefined results
175 // Make sure that the file size can fit in the available space
176 if (!(flags & O_LARGEFILE) && (file->filesize >= (1<<31))) {
182 // Make sure we aren't trying to write to a read-only file
183 if (file
->write
&& !_FAT_directory_isWritable(&dirEntry
)) {
184 _FAT_unlock(&partition
->lock
);
189 // Associate this file with a particular partition
190 file
->partition
= partition
;
192 file
->startCluster
= _FAT_directory_entryGetCluster (partition
, dirEntry
.entryData
);
194 // Truncate the file if requested
195 if ((flags
& O_TRUNC
) && file
->write
&& (file
->startCluster
!= 0)) {
196 _FAT_fat_clearLinks (partition
, file
->startCluster
);
197 file
->startCluster
= CLUSTER_FREE
;
199 // File is modified since we just cut it all off
200 file
->modified
= true;
203 // Remember the position of this file's directory entry
204 file
->dirEntryStart
= dirEntry
.dataStart
; // Points to the start of the LFN entries of a file, or the alias for no LFN
205 file
->dirEntryEnd
= dirEntry
.dataEnd
;
207 // Reset read/write pointer
208 file
->currentPosition
= 0;
209 file
->rwPosition
.cluster
= file
->startCluster
;
210 file
->rwPosition
.sector
= 0;
211 file
->rwPosition
.byte
= 0;
213 if (flags
& O_APPEND
) {
216 // Set append pointer to the end of the file
217 file
->appendPosition
.cluster
= _FAT_fat_lastCluster (partition
, file
->startCluster
);
218 file
->appendPosition
.sector
= (file
->filesize
% partition
->bytesPerCluster
) / BYTES_PER_READ
;
219 file
->appendPosition
.byte
= file
->filesize
% BYTES_PER_READ
;
221 // Check if the end of the file is on the end of a cluster
222 if ( (file
->filesize
> 0) && ((file
->filesize
% partition
->bytesPerCluster
)==0) ){
223 // Set flag to allocate a new cluster
224 file
->appendPosition
.sector
= partition
->sectorsPerCluster
;
225 file
->appendPosition
.byte
= 0;
228 file
->append
= false;
229 // Use something sane for the append pointer, so the whole file struct contains known values
230 file
->appendPosition
= file
->rwPosition
;
235 // Insert this file into the double-linked list of open files
236 partition
->openFileCount
+= 1;
237 if (partition
->firstOpenFile
) {
238 file
->nextOpenFile
= partition
->firstOpenFile
;
239 partition
->firstOpenFile
->prevOpenFile
= file
;
241 file
->nextOpenFile
= NULL
;
243 file
->prevOpenFile
= NULL
;
244 partition
->firstOpenFile
= file
;
246 _FAT_unlock(&partition
->lock
);
250 fp->_file = (int) file;
251 setvbuf(fp,NULL,_IONBF,0);*/
256 Synchronizes the file data to disc.
257 Does no locking of its own -- lock the partition before calling.
258 Returns 0 on success, an error code on failure.
260 int _FAT_syncToDisc (FILE_STRUCT
* file
) {
261 uint8_t dirEntryData
[DIR_ENTRY_DATA_SIZE
];
263 if (!file
|| !file
->inUse
) {
267 if (file
->write
&& file
->modified
) {
268 // Load the old entry
269 _FAT_cache_readPartialSector (file
->partition
->cache
, dirEntryData
,
270 _FAT_fat_clusterToSector(file
->partition
, file
->dirEntryEnd
.cluster
) + file
->dirEntryEnd
.sector
,
271 file
->dirEntryEnd
.offset
* DIR_ENTRY_DATA_SIZE
, DIR_ENTRY_DATA_SIZE
);
273 // Write new data to the directory entry
275 u32_to_u8array (dirEntryData
, DIR_ENTRY_fileSize
, file
->filesize
);
278 u16_to_u8array (dirEntryData
, DIR_ENTRY_cluster
, file
->startCluster
);
279 u16_to_u8array (dirEntryData
, DIR_ENTRY_clusterHigh
, file
->startCluster
>> 16);
281 // Modification time and date
282 u16_to_u8array (dirEntryData
, DIR_ENTRY_mTime
, _FAT_filetime_getTimeFromRTC());
283 u16_to_u8array (dirEntryData
, DIR_ENTRY_mDate
, _FAT_filetime_getDateFromRTC());
286 u16_to_u8array (dirEntryData
, DIR_ENTRY_aDate
, _FAT_filetime_getDateFromRTC());
288 // Set archive attribute
289 dirEntryData
[DIR_ENTRY_attributes
] |= ATTRIB_ARCH
;
291 // Write the new entry
292 _FAT_cache_writePartialSector (file
->partition
->cache
, dirEntryData
,
293 _FAT_fat_clusterToSector(file
->partition
, file
->dirEntryEnd
.cluster
) + file
->dirEntryEnd
.sector
,
294 file
->dirEntryEnd
.offset
* DIR_ENTRY_DATA_SIZE
, DIR_ENTRY_DATA_SIZE
);
296 // Flush any sectors in the disc cache
297 if (!_FAT_cache_flush(file
->partition
->cache
)) {
302 file
->modified
= false;
308 int _FAT_close_r (struct _reent
*r
, int fd
) {
309 FILE_STRUCT
* file
= (FILE_STRUCT
*) fd
;
317 _FAT_lock(&file
->partition
->lock
);
319 ret
= _FAT_syncToDisc (file
);
327 // Remove this file from the double-linked list of open files
328 file
->partition
->openFileCount
-= 1;
329 if (file
->nextOpenFile
) {
330 file
->nextOpenFile
->prevOpenFile
= file
->prevOpenFile
;
332 if (file
->prevOpenFile
) {
333 file
->prevOpenFile
->nextOpenFile
= file
->nextOpenFile
;
335 file
->partition
->firstOpenFile
= file
->nextOpenFile
;
338 _FAT_unlock(&file
->partition
->lock
);
343 ssize_t
_FAT_read_r (struct _reent
*r
, int fd
, char *ptr
, size_t len
) {
344 FILE_STRUCT
* file
= (FILE_STRUCT
*) fd
;
345 PARTITION
* partition
;
347 FILE_POSITION position
;
348 uint32_t tempNextCluster
;
349 unsigned int tempVar
;
351 bool flagNoError
= true;
353 // Short circuit cases where len is 0 (or less)
358 // Make sure we can actually read from the file
359 if ((file
== NULL
) || !file
->inUse
|| !file
->read
) {
364 partition
= file
->partition
;
365 _FAT_lock(&partition
->lock
);
367 // Don't try to read if the read pointer is past the end of file
368 if (file
->currentPosition
>= file
->filesize
|| file
->startCluster
== CLUSTER_FREE
) {
369 r
->_errno
= EOVERFLOW
;
370 _FAT_unlock(&partition
->lock
);
374 // Don't read past end of file
375 if (len
+ file
->currentPosition
> file
->filesize
) {
376 r
->_errno
= EOVERFLOW
;
377 len
= file
->filesize
- file
->currentPosition
;
381 position
= file
->rwPosition
;
382 cache
= file
->partition
->cache
;
385 tempVar
= BYTES_PER_READ
- position
.byte
;
386 if (tempVar
> remain
) {
390 if ((tempVar
< BYTES_PER_READ
) && flagNoError
)
392 _FAT_cache_readPartialSector ( cache
, ptr
, _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
,
393 position
.byte
, tempVar
);
398 position
.byte
+= tempVar
;
399 if (position
.byte
>= BYTES_PER_READ
) {
406 // tempVar is number of sectors to read
407 if (remain
> (partition
->sectorsPerCluster
- position
.sector
) * BYTES_PER_READ
) {
408 tempVar
= partition
->sectorsPerCluster
- position
.sector
;
410 tempVar
= remain
/ BYTES_PER_READ
;
413 if ((tempVar
> 0) && flagNoError
) {
414 if (! _FAT_cache_getSectors (cache
, _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
,
420 ptr
+= tempVar
* BYTES_PER_READ
;
421 remain
-= tempVar
* BYTES_PER_READ
;
422 position
.sector
+= tempVar
;
426 // Move onto next cluster
427 // It should get to here without reading anything if a cluster is due to be allocated
428 if ((position
.sector
>= partition
->sectorsPerCluster
) && flagNoError
) {
429 tempNextCluster
= _FAT_fat_nextCluster(partition
, position
.cluster
);
430 if ((remain
== 0) && (tempNextCluster
== CLUSTER_EOF
)) {
431 position
.sector
= partition
->sectorsPerCluster
;
432 } else if (!_FAT_fat_isValidCluster(partition
, tempNextCluster
)) {
437 position
.cluster
= tempNextCluster
;
441 // Read in whole clusters, contiguous blocks at a time
442 while ((remain
>= partition
->bytesPerCluster
) && flagNoError
) {
444 uint32_t nextChunkStart
= position
.cluster
;
445 size_t chunkSize
= 0;
448 chunkEnd
= nextChunkStart
;
449 nextChunkStart
= _FAT_fat_nextCluster (partition
, chunkEnd
);
450 chunkSize
+= partition
->bytesPerCluster
;
451 } while ((nextChunkStart
== chunkEnd
+ 1) &&
453 (chunkSize
+ partition
->bytesPerCluster
<= LIMIT_SECTORS
* BYTES_PER_READ
) &&
455 (chunkSize
+ partition
->bytesPerCluster
<= remain
));
457 if (!_FAT_cache_getSectors (cache
, _FAT_fat_clusterToSector (partition
, position
.cluster
),
458 chunkSize
/ BYTES_PER_READ
, ptr
))
467 // Advance to next cluster
468 if ((remain
== 0) && (nextChunkStart
== CLUSTER_EOF
)) {
469 position
.sector
= partition
->sectorsPerCluster
;
470 position
.cluster
= chunkEnd
;
471 } else if (!_FAT_fat_isValidCluster(partition
, nextChunkStart
)) {
476 position
.cluster
= nextChunkStart
;
480 // Read remaining sectors
481 tempVar
= remain
/ BYTES_PER_READ
; // Number of sectors left
482 if ((tempVar
> 0) && flagNoError
) {
483 if (!_FAT_cache_getSectors (cache
, _FAT_fat_clusterToSector (partition
, position
.cluster
),
489 ptr
+= tempVar
* BYTES_PER_READ
;
490 remain
-= tempVar
* BYTES_PER_READ
;
491 position
.sector
+= tempVar
;
495 // Last remaining sector
496 // Check if anything is left
497 if ((remain
> 0) && flagNoError
) {
498 _FAT_cache_readPartialSector ( cache
, ptr
,
499 _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
, 0, remain
);
500 position
.byte
+= remain
;
504 // Length read is the wanted length minus the stuff not read
507 // Update file information
508 file
->rwPosition
= position
;
509 file
->currentPosition
+= len
;
511 _FAT_unlock(&partition
->lock
);
516 Extend a file so that the size is the same as the rwPosition
518 static bool _FAT_file_extend_r (struct _reent
*r
, FILE_STRUCT
* file
) {
519 PARTITION
* partition
= file
->partition
;
520 CACHE
* cache
= file
->partition
->cache
;
521 FILE_POSITION position
;
522 uint8_t zeroBuffer
[BYTES_PER_READ
] = {0};
524 uint32_t tempNextCluster
;
526 position
.byte
= file
->filesize
% BYTES_PER_READ
;
527 position
.sector
= (file
->filesize
% partition
->bytesPerCluster
) / BYTES_PER_READ
;
528 // It is assumed that there is always a startCluster
529 // This will be true when _FAT_file_extend_r is called from _FAT_write_r
530 position
.cluster
= _FAT_fat_lastCluster (partition
, file
->startCluster
);
532 remain
= file
->currentPosition
- file
->filesize
;
534 if ((remain
> 0) && (file
->filesize
> 0) && (position
.sector
== 0) && (position
.byte
== 0)) {
535 // Get a new cluster on the edge of a cluster boundary
536 tempNextCluster
= _FAT_fat_linkFreeCluster(partition
, position
.cluster
);
537 if (!_FAT_fat_isValidCluster(partition
, tempNextCluster
)) {
538 // Couldn't get a cluster, so abort
542 position
.cluster
= tempNextCluster
;
546 if (remain
+ position
.byte
< BYTES_PER_READ
) {
547 // Only need to clear to the end of the sector
548 _FAT_cache_writePartialSector (cache
, zeroBuffer
,
549 _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
, position
.byte
, remain
);
550 position
.byte
+= remain
;
552 if (position
.byte
> 0) {
553 _FAT_cache_writePartialSector (cache
, zeroBuffer
,
554 _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
, position
.byte
,
555 BYTES_PER_READ
- position
.byte
);
556 remain
-= (BYTES_PER_READ
- position
.byte
);
561 while (remain
>= BYTES_PER_READ
) {
562 if (position
.sector
>= partition
->sectorsPerCluster
) {
564 // Ran out of clusters so get a new one
565 tempNextCluster
= _FAT_fat_linkFreeCluster(partition
, position
.cluster
);
566 if (!_FAT_fat_isValidCluster(partition
, tempNextCluster
)) {
567 // Couldn't get a cluster, so abort
571 position
.cluster
= tempNextCluster
;
574 _FAT_disc_writeSectors (partition
->disc
,
575 _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
, 1, zeroBuffer
);
577 remain
-= BYTES_PER_READ
;
581 if (position
.sector
>= partition
->sectorsPerCluster
) {
583 tempNextCluster
= _FAT_fat_nextCluster(partition
, position
.cluster
);
584 if ((tempNextCluster
== CLUSTER_EOF
) || (tempNextCluster
== CLUSTER_FREE
)) {
585 // Ran out of clusters so get a new one
586 tempNextCluster
= _FAT_fat_linkFreeCluster(partition
, position
.cluster
);
588 if (!_FAT_fat_isValidCluster(partition
, tempNextCluster
)) {
589 // Couldn't get a cluster, so abort
593 position
.cluster
= tempNextCluster
;
597 _FAT_cache_writePartialSector (cache
, zeroBuffer
,
598 _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
, 0, remain
);
599 position
.byte
= remain
;
603 file
->rwPosition
= position
;
604 file
->filesize
= file
->currentPosition
;
609 ssize_t
_FAT_write_r (struct _reent
*r
, int fd
, const char *ptr
, size_t len
) {
610 FILE_STRUCT
* file
= (FILE_STRUCT
*) fd
;
611 PARTITION
* partition
;
613 FILE_POSITION position
;
614 uint32_t tempNextCluster
;
615 unsigned int tempVar
;
617 bool flagNoError
= true;
618 bool flagAppending
= false;
620 // Make sure we can actually write to the file
621 if ((file
== NULL
) || !file
->inUse
|| !file
->write
) {
626 partition
= file
->partition
;
627 cache
= file
->partition
->cache
;
628 _FAT_lock(&partition
->lock
);
630 // Only write up to the maximum file size, taking into account wrap-around of ints
631 if (remain
+ file
->filesize
> FILE_MAX_SIZE
|| len
+ file
->filesize
< file
->filesize
) {
632 len
= FILE_MAX_SIZE
- file
->filesize
;
636 // Short circuit cases where len is 0 (or less)
638 _FAT_unlock(&partition
->lock
);
642 // Get a new cluster for the start of the file if required
643 if (file
->startCluster
== CLUSTER_FREE
) {
644 tempNextCluster
= _FAT_fat_linkFreeCluster (partition
, CLUSTER_FREE
);
645 if (!_FAT_fat_isValidCluster(partition
, tempNextCluster
)) {
646 // Couldn't get a cluster, so abort immediately
647 _FAT_unlock(&partition
->lock
);
651 file
->startCluster
= tempNextCluster
;
653 // Appending starts at the begining for a 0 byte file
654 file
->appendPosition
.cluster
= file
->startCluster
;
655 file
->appendPosition
.sector
= 0;
656 file
->appendPosition
.byte
= 0;
658 file
->rwPosition
.cluster
= file
->startCluster
;
659 file
->rwPosition
.sector
= 0;
660 file
->rwPosition
.byte
= 0;
664 position
= file
->appendPosition
;
665 flagAppending
= true;
667 // If the write pointer is past the end of the file, extend the file to that size
668 if (file
->currentPosition
> file
->filesize
) {
669 if (!_FAT_file_extend_r (r
, file
)) {
670 _FAT_unlock(&partition
->lock
);
675 // Write at current read pointer
676 position
= file
->rwPosition
;
678 // If it is writing past the current end of file, set appending flag
679 if (len
+ file
->currentPosition
> file
->filesize
) {
680 flagAppending
= true;
684 // Move onto next cluster if needed
685 if (position
.sector
>= partition
->sectorsPerCluster
) {
687 tempNextCluster
= _FAT_fat_nextCluster(partition
, position
.cluster
);
688 if ((tempNextCluster
== CLUSTER_EOF
) || (tempNextCluster
== CLUSTER_FREE
)) {
689 // Ran out of clusters so get a new one
690 tempNextCluster
= _FAT_fat_linkFreeCluster(partition
, position
.cluster
);
692 if (!_FAT_fat_isValidCluster(partition
, tempNextCluster
)) {
693 // Couldn't get a cluster, so abort
697 position
.cluster
= tempNextCluster
;
702 tempVar
= BYTES_PER_READ
- position
.byte
;
703 if (tempVar
> remain
) {
707 if ((tempVar
< BYTES_PER_READ
) && flagNoError
) {
708 // Write partial sector to disk
709 _FAT_cache_writePartialSector (cache
, ptr
,
710 _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
, position
.byte
, tempVar
);
714 position
.byte
+= tempVar
;
717 // Move onto next sector
718 if (position
.byte
>= BYTES_PER_READ
) {
725 // tempVar is number of sectors to write
726 if (remain
> (partition
->sectorsPerCluster
- position
.sector
) * BYTES_PER_READ
) {
727 tempVar
= partition
->sectorsPerCluster
- position
.sector
;
729 tempVar
= remain
/ BYTES_PER_READ
;
732 if ((tempVar
> 0) && flagNoError
) {
733 if (!_FAT_disc_writeSectors (partition
->disc
,
734 _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
, tempVar
, ptr
))
739 ptr
+= tempVar
* BYTES_PER_READ
;
740 remain
-= tempVar
* BYTES_PER_READ
;
741 position
.sector
+= tempVar
;
745 if ((position
.sector
>= partition
->sectorsPerCluster
) && flagNoError
&& (remain
> 0)) {
747 tempNextCluster
= _FAT_fat_nextCluster(partition
, position
.cluster
);
748 if ((tempNextCluster
== CLUSTER_EOF
) || (tempNextCluster
== CLUSTER_FREE
)) {
749 // Ran out of clusters so get a new one
750 tempNextCluster
= _FAT_fat_linkFreeCluster(partition
, position
.cluster
);
752 if (!_FAT_fat_isValidCluster(partition
, tempNextCluster
)) {
753 // Couldn't get a cluster, so abort
757 position
.cluster
= tempNextCluster
;
761 // Write whole clusters
762 while ((remain
>= partition
->bytesPerCluster
) && flagNoError
) {
764 uint32_t nextChunkStart
= position
.cluster
;
765 size_t chunkSize
= 0;
768 chunkEnd
= nextChunkStart
;
769 nextChunkStart
= _FAT_fat_nextCluster (partition
, chunkEnd
);
770 if ((nextChunkStart
== CLUSTER_EOF
) || (nextChunkStart
== CLUSTER_FREE
)) {
771 // Ran out of clusters so get a new one
772 nextChunkStart
= _FAT_fat_linkFreeCluster(partition
, chunkEnd
);
774 if (!_FAT_fat_isValidCluster(partition
, nextChunkStart
)) {
775 // Couldn't get a cluster, so abort
779 chunkSize
+= partition
->bytesPerCluster
;
781 } while (flagNoError
&& (nextChunkStart
== chunkEnd
+ 1) &&
783 (chunkSize
+ partition
->bytesPerCluster
<= LIMIT_SECTORS
* BYTES_PER_READ
) &&
785 (chunkSize
+ partition
->bytesPerCluster
<= remain
));
787 if ( !_FAT_disc_writeSectors (partition
->disc
, _FAT_fat_clusterToSector(partition
, position
.cluster
),
788 chunkSize
/ BYTES_PER_READ
, ptr
))
797 if (_FAT_fat_isValidCluster(partition
, nextChunkStart
)) {
798 position
.cluster
= nextChunkStart
;
800 // Allocate a new cluster when next writing the file
801 position
.cluster
= chunkEnd
;
802 position
.sector
= partition
->sectorsPerCluster
;
806 // Write remaining sectors
807 tempVar
= remain
/ BYTES_PER_READ
; // Number of sectors left
808 if ((tempVar
> 0) && flagNoError
) {
809 if (!_FAT_disc_writeSectors (partition
->disc
, _FAT_fat_clusterToSector (partition
, position
.cluster
),
815 ptr
+= tempVar
* BYTES_PER_READ
;
816 remain
-= tempVar
* BYTES_PER_READ
;
817 position
.sector
+= tempVar
;
821 // Last remaining sector
822 if ((remain
> 0) && flagNoError
) {
824 _FAT_cache_eraseWritePartialSector ( cache
, ptr
,
825 _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
, 0, remain
);
827 _FAT_cache_writePartialSector ( cache
, ptr
,
828 _FAT_fat_clusterToSector (partition
, position
.cluster
) + position
.sector
, 0, remain
);
830 position
.byte
+= remain
;
835 // Amount written is the originally requested amount minus stuff remaining
838 // Update file information
839 file
->modified
= true;
841 // Appending doesn't affect the read pointer
842 file
->appendPosition
= position
;
843 file
->filesize
+= len
;
845 // Writing also shifts the read pointer
846 file
->rwPosition
= position
;
847 file
->currentPosition
+= len
;
848 if (file
->filesize
< file
->currentPosition
) {
849 file
->filesize
= file
->currentPosition
;
853 _FAT_unlock(&partition
->lock
);
859 off_t
_FAT_seek_r (struct _reent
*r
, int fd
, off_t pos
, int dir
) {
860 FILE_STRUCT
* file
= (FILE_STRUCT
*) fd
;
861 PARTITION
* partition
;
862 uint32_t cluster
, nextCluster
;
867 if ((file
== NULL
) || (file
->inUse
== false)) {
873 partition
= file
->partition
;
874 _FAT_lock(&partition
->lock
);
881 newPosition
= (off_t
)file
->currentPosition
+ pos
;
884 newPosition
= (off_t
)file
->filesize
+ pos
;
887 _FAT_unlock(&partition
->lock
);
892 if ((pos
> 0) && (newPosition
< 0)) {
893 _FAT_unlock(&partition
->lock
);
894 r
->_errno
= EOVERFLOW
;
898 // newPosition can only be larger than the FILE_MAX_SIZE on platforms where
899 // off_t is larger than 32 bits.
900 if (newPosition
< 0 || ((sizeof(newPosition
) > 4) && newPosition
> (off_t
)FILE_MAX_SIZE
)) {
901 _FAT_unlock(&partition
->lock
);
906 position
= (uint32_t)newPosition
;
908 // Only change the read/write position if it is within the bounds of the current filesize,
909 // or at the very edge of the file
910 if (position
<= file
->filesize
&& file
->startCluster
!= CLUSTER_FREE
) {
911 // Calculate the sector and byte of the current position,
913 file
->rwPosition
.sector
= (position
% partition
->bytesPerCluster
) / BYTES_PER_READ
;
914 file
->rwPosition
.byte
= position
% BYTES_PER_READ
;
916 // Calculate where the correct cluster is
917 if ((position
>= file
->currentPosition
) && (file
->rwPosition
.sector
!= partition
->sectorsPerCluster
)) {
918 clusCount
= (position
/ partition
->bytesPerCluster
) - (file
->currentPosition
/ partition
->bytesPerCluster
);
919 cluster
= file
->rwPosition
.cluster
;
921 clusCount
= position
/ partition
->bytesPerCluster
;
922 cluster
= file
->startCluster
;
925 nextCluster
= _FAT_fat_nextCluster (partition
, cluster
);
926 while ((clusCount
> 0) && (nextCluster
!= CLUSTER_FREE
) && (nextCluster
!= CLUSTER_EOF
)) {
928 cluster
= nextCluster
;
929 nextCluster
= _FAT_fat_nextCluster (partition
, cluster
);
932 // Check if ran out of clusters and it needs to allocate a new one
934 if ((clusCount
== 1) && (file
->filesize
== position
) && (file
->rwPosition
.sector
== 0)) {
935 // Set flag to allocate a new cluster
936 file
->rwPosition
.sector
= partition
->sectorsPerCluster
;
937 file
->rwPosition
.byte
= 0;
939 _FAT_unlock(&partition
->lock
);
945 file
->rwPosition
.cluster
= cluster
;
949 file
->currentPosition
= position
;
951 _FAT_unlock(&partition
->lock
);
957 int _FAT_fstat_r (struct _reent
*r
, int fd
, struct stat
*st
) {
958 FILE_STRUCT
* file
= (FILE_STRUCT
*) fd
;
959 PARTITION
* partition
;
962 if ((file
== NULL
) || (file
->inUse
== false)) {
968 partition
= file
->partition
;
969 _FAT_lock(&partition
->lock
);
971 // Get the file's entry data
972 fileEntry
.dataStart
= file
->dirEntryStart
;
973 fileEntry
.dataEnd
= file
->dirEntryEnd
;
975 if (!_FAT_directory_entryFromPosition (partition
, &fileEntry
)) {
976 _FAT_unlock(&partition
->lock
);
981 // Fill in the stat struct
982 _FAT_directory_entryStat (partition
, &fileEntry
, st
);
984 // Fix stats that have changed since the file was openned
985 st
->st_ino
= (ino_t
)(file
->startCluster
); // The file serial number is the start cluster
986 st
->st_size
= file
->filesize
; // File size
988 _FAT_unlock(&partition
->lock
);
992 int _FAT_ftruncate_r (struct _reent
*r
, int fd
, off_t len
) {
993 FILE_STRUCT
* file
= (FILE_STRUCT
*) fd
;
994 PARTITION
* partition
;
996 uint32_t newSize
= (uint32_t)len
;
999 // Trying to truncate to a negative size
1004 if ((sizeof(len
) > 4) && len
> (off_t
)FILE_MAX_SIZE
) {
1005 // Trying to extend the file beyond what FAT supports
1010 if (!file
|| !file
->inUse
) {
1022 partition
= file
->partition
;
1023 _FAT_lock(&partition
->lock
);
1025 if (newSize
> file
->filesize
) {
1026 // Expanding the file
1027 FILE_POSITION savedPosition
;
1028 uint32_t savedOffset
;
1029 // Get a new cluster for the start of the file if required
1030 if (file
->startCluster
== CLUSTER_FREE
) {
1031 uint32_t tempNextCluster
= _FAT_fat_linkFreeCluster (partition
, CLUSTER_FREE
);
1032 if (!_FAT_fat_isValidCluster(partition
, tempNextCluster
)) {
1033 // Couldn't get a cluster, so abort immediately
1034 _FAT_unlock(&partition
->lock
);
1038 file
->startCluster
= tempNextCluster
;
1040 file
->rwPosition
.cluster
= file
->startCluster
;
1041 file
->rwPosition
.sector
= 0;
1042 file
->rwPosition
.byte
= 0;
1044 // Save the read/write pointer
1045 savedPosition
= file
->rwPosition
;
1046 savedOffset
= file
->currentPosition
;
1047 // Set the position to the new size
1048 file
->currentPosition
= newSize
;
1049 // Extend the file to the new position
1050 if (!_FAT_file_extend_r (r
, file
)) {
1053 // Set the append position to the new rwPointer
1055 file
->appendPosition
= file
->rwPosition
;
1057 // Restore the old rwPointer;
1058 file
->rwPosition
= savedPosition
;
1059 file
->currentPosition
= savedOffset
;
1060 } else if (newSize
< file
->filesize
){
1061 // Shrinking the file
1063 // Cutting the file down to nothing, clear all clusters used
1064 _FAT_fat_clearLinks (partition
, file
->startCluster
);
1065 file
->startCluster
= CLUSTER_FREE
;
1067 file
->appendPosition
.cluster
= CLUSTER_FREE
;
1068 file
->appendPosition
.sector
= 0;
1069 file
->appendPosition
.byte
= 0;
1071 // Trimming the file down to the required size
1072 unsigned int chainLength
;
1073 uint32_t lastCluster
;
1075 // Drop the unneeded end of the cluster chain.
1076 // If the end falls on a cluster boundary, drop that cluster too,
1077 // then set a flag to allocate a cluster as needed
1078 chainLength
= ((newSize
-1) / partition
->bytesPerCluster
) + 1;
1079 lastCluster
= _FAT_fat_trimChain (partition
, file
->startCluster
, chainLength
);
1082 file
->appendPosition
.byte
= newSize
% BYTES_PER_READ
;
1083 // Does the end of the file fall on the edge of a cluster?
1084 if (newSize
% partition
->bytesPerCluster
== 0) {
1085 // Set a flag to allocate a new cluster
1086 file
->appendPosition
.sector
= partition
->sectorsPerCluster
;
1088 file
->appendPosition
.sector
= (newSize
% partition
->bytesPerCluster
) / BYTES_PER_READ
;
1090 file
->appendPosition
.cluster
= lastCluster
;
1094 // Truncating to same length, so don't do anything
1097 file
->filesize
= newSize
;
1098 file
->modified
= true;
1100 _FAT_unlock(&partition
->lock
);
1104 int _FAT_fsync_r (struct _reent
*r
, int fd
) {
1105 FILE_STRUCT
* file
= (FILE_STRUCT
*) fd
;
1113 _FAT_lock(&file
->partition
->lock
);
1115 ret
= _FAT_syncToDisc (file
);
1121 _FAT_unlock(&file
->partition
->lock
);