remove unnecessary function
[libfat.git] / source / fatfile.c
blobdcd10b1a4a59dc0f76da35815fddc6f0ca4bd620
1 /*
2 fatfile.c
4 Functions used by the newlib disc stubs to interface with
5 this library
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.
32 #include "fatfile.h"
34 #include <fcntl.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <ctype.h>
38 #include <unistd.h>
39 #include <stdio.h>
41 #include "cache.h"
42 #include "file_allocation_table.h"
43 #include "bit_ops.h"
44 #include "filetime.h"
45 #include "lock.h"
47 int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) {
48 PARTITION* partition = NULL;
49 bool fileExists;
50 DIR_ENTRY dirEntry;
51 const char* pathEnd;
52 uint32_t dirCluster;
53 FILE_STRUCT* file = (FILE_STRUCT*) fileStruct;
54 partition = _FAT_partition_getPartitionFromPath (path);
56 if (partition == NULL) {
57 r->_errno = ENODEV;
58 return -1;
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) {
66 r->_errno = EINVAL;
67 return -1;
70 // Determine which mode the file is openned for
71 if ((flags & 0x03) == O_RDONLY) {
72 // Open the file for read-only access
73 file->read = true;
74 file->write = false;
75 file->append = false;
76 } else if ((flags & 0x03) == O_WRONLY) {
77 // Open file for write only access
78 file->read = false;
79 file->write = true;
80 file->append = false;
81 } else if ((flags & 0x03) == O_RDWR) {
82 // Open file for read/write access
83 file->read = true;
84 file->write = true;
85 file->append = false;
86 } else {
87 r->_errno = EACCES;
88 return -1;
91 // Make sure we aren't trying to write to a read-only disc
92 if (file->write && partition->readOnly) {
93 r->_errno = EROFS;
94 return -1;
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);
104 r->_errno = EEXIST;
105 return -1;
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);
111 r->_errno = EISDIR;
112 return -1;
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
119 if (!fileExists) {
120 if (flags & O_CREAT) {
121 if (partition->readOnly) {
122 // We can't write to a read-only partition
123 _FAT_unlock(&partition->lock);
124 r->_errno = EROFS;
125 return -1;
127 // Create the file
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;
133 pathEnd = path;
134 } else {
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);
140 r->_errno = ENOTDIR;
141 return -1;
143 dirCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
144 // Move the pathEnd past the last DIR_SEPARATOR
145 pathEnd += 1;
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);
158 r->_errno = ENOSPC;
159 return -1;
162 // File entry is modified
163 file->modified = true;
164 } else {
165 // file doesn't exist, and we aren't creating it
166 _FAT_unlock(&partition->lock);
167 r->_errno = ENOENT;
168 return -1;
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))) {
177 r->_errno = EFBIG;
178 return -1;
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);
185 r->_errno = EROFS;
186 return -1;
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;
198 file->filesize = 0;
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) {
214 file->append = true;
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;
227 } else {
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;
233 file->inUse = true;
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;
240 } else {
241 file->nextOpenFile = NULL;
243 file->prevOpenFile = NULL;
244 partition->firstOpenFile = file;
246 _FAT_unlock(&partition->lock);
248 /*FILE *fp;
249 fp=__sfp(r);
250 fp->_file = (int) file;
251 setvbuf(fp,NULL,_IONBF,0);*/
252 return (int) file;
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) {
264 return EBADF;
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
274 // File size
275 u32_to_u8array (dirEntryData, DIR_ENTRY_fileSize, file->filesize);
277 // Start cluster
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());
285 // Access date
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)) {
298 return EIO;
302 file->modified = false;
304 return 0;
308 int _FAT_close_r (struct _reent *r, int fd) {
309 FILE_STRUCT* file = (FILE_STRUCT*) fd;
310 int ret = 0;
312 if (!file->inUse) {
313 r->_errno = EBADF;
314 return -1;
317 _FAT_lock(&file->partition->lock);
319 ret = _FAT_syncToDisc (file);
320 if (ret != 0) {
321 r->_errno = ret;
322 ret = -1;
325 file->inUse = false;
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;
334 } else {
335 file->partition->firstOpenFile = file->nextOpenFile;
338 _FAT_unlock(&file->partition->lock);
340 return ret;
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;
346 CACHE* cache;
347 FILE_POSITION position;
348 uint32_t tempNextCluster;
349 unsigned int tempVar;
350 size_t remain;
351 bool flagNoError = true;
353 // Short circuit cases where len is 0 (or less)
354 if (len <= 0) {
355 return 0;
358 // Make sure we can actually read from the file
359 if ((file == NULL) || !file->inUse || !file->read) {
360 r->_errno = EBADF;
361 return -1;
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);
371 return 0;
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;
380 remain = len;
381 position = file->rwPosition;
382 cache = file->partition->cache;
384 // Align to sector
385 tempVar = BYTES_PER_READ - position.byte;
386 if (tempVar > remain) {
387 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);
395 remain -= tempVar;
396 ptr += tempVar;
398 position.byte += tempVar;
399 if (position.byte >= BYTES_PER_READ) {
400 position.byte = 0;
401 position.sector++;
405 // align to cluster
406 // tempVar is number of sectors to read
407 if (remain > (partition->sectorsPerCluster - position.sector) * BYTES_PER_READ) {
408 tempVar = partition->sectorsPerCluster - position.sector;
409 } else {
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,
415 tempVar, ptr))
417 flagNoError = false;
418 r->_errno = EIO;
419 } else {
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)) {
433 r->_errno = EIO;
434 flagNoError = false;
435 } else {
436 position.sector = 0;
437 position.cluster = tempNextCluster;
441 // Read in whole clusters, contiguous blocks at a time
442 while ((remain >= partition->bytesPerCluster) && flagNoError) {
443 uint32_t chunkEnd;
444 uint32_t nextChunkStart = position.cluster;
445 size_t chunkSize = 0;
447 do {
448 chunkEnd = nextChunkStart;
449 nextChunkStart = _FAT_fat_nextCluster (partition, chunkEnd);
450 chunkSize += partition->bytesPerCluster;
451 } while ((nextChunkStart == chunkEnd + 1) &&
452 #ifdef LIMIT_SECTORS
453 (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * BYTES_PER_READ) &&
454 #endif
455 (chunkSize + partition->bytesPerCluster <= remain));
457 if (!_FAT_cache_getSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster),
458 chunkSize / BYTES_PER_READ, ptr))
460 flagNoError = false;
461 r->_errno = EIO;
462 break;
464 ptr += chunkSize;
465 remain -= chunkSize;
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)) {
472 r->_errno = EIO;
473 flagNoError = false;
474 } else {
475 position.sector = 0;
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),
484 tempVar, ptr))
486 flagNoError = false;
487 r->_errno = EIO;
488 } else {
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;
501 remain = 0;
504 // Length read is the wanted length minus the stuff not read
505 len = len - remain;
507 // Update file information
508 file->rwPosition = position;
509 file->currentPosition += len;
511 _FAT_unlock(&partition->lock);
512 return len;
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};
523 uint32_t remain;
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
539 r->_errno = ENOSPC;
540 return false;
542 position.cluster = tempNextCluster;
543 position.sector = 0;
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;
551 } else {
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);
557 position.byte = 0;
558 position.sector ++;
561 while (remain >= BYTES_PER_READ) {
562 if (position.sector >= partition->sectorsPerCluster) {
563 position.sector = 0;
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
568 r->_errno = ENOSPC;
569 return false;
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;
578 position.sector ++;
581 if (position.sector >= partition->sectorsPerCluster) {
582 position.sector = 0;
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
590 r->_errno = ENOSPC;
591 return false;
593 position.cluster = tempNextCluster;
596 if (remain > 0) {
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;
605 return true;
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;
612 CACHE* cache;
613 FILE_POSITION position;
614 uint32_t tempNextCluster;
615 unsigned int tempVar;
616 size_t remain;
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) {
622 r->_errno = EBADF;
623 return -1;
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;
634 remain = len;
636 // Short circuit cases where len is 0 (or less)
637 if (len <= 0) {
638 _FAT_unlock(&partition->lock);
639 return 0;
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);
648 r->_errno = ENOSPC;
649 return -1;
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;
663 if (file->append) {
664 position = file->appendPosition;
665 flagAppending = true;
666 } else {
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);
671 return -1;
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) {
686 position.sector = 0;
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
694 r->_errno = ENOSPC;
695 flagNoError = false;
696 } else {
697 position.cluster = tempNextCluster;
701 // Align to sector
702 tempVar = BYTES_PER_READ - position.byte;
703 if (tempVar > remain) {
704 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);
712 remain -= tempVar;
713 ptr += tempVar;
714 position.byte += tempVar;
717 // Move onto next sector
718 if (position.byte >= BYTES_PER_READ) {
719 position.byte = 0;
720 position.sector ++;
724 // Align to cluster
725 // tempVar is number of sectors to write
726 if (remain > (partition->sectorsPerCluster - position.sector) * BYTES_PER_READ) {
727 tempVar = partition->sectorsPerCluster - position.sector;
728 } else {
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))
736 flagNoError = false;
737 r->_errno = EIO;
738 } else {
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)) {
746 position.sector = 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
754 r->_errno = ENOSPC;
755 flagNoError = false;
756 } else {
757 position.cluster = tempNextCluster;
761 // Write whole clusters
762 while ((remain >= partition->bytesPerCluster) && flagNoError) {
763 uint32_t chunkEnd;
764 uint32_t nextChunkStart = position.cluster;
765 size_t chunkSize = 0;
767 do {
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
776 r->_errno = ENOSPC;
777 flagNoError = false;
778 } else {
779 chunkSize += partition->bytesPerCluster;
781 } while (flagNoError && (nextChunkStart == chunkEnd + 1) &&
782 #ifdef LIMIT_SECTORS
783 (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * BYTES_PER_READ) &&
784 #endif
785 (chunkSize + partition->bytesPerCluster <= remain));
787 if ( !_FAT_disc_writeSectors (partition->disc, _FAT_fat_clusterToSector(partition, position.cluster),
788 chunkSize / BYTES_PER_READ, ptr))
790 flagNoError = false;
791 r->_errno = EIO;
792 break;
794 ptr += chunkSize;
795 remain -= chunkSize;
797 if (_FAT_fat_isValidCluster(partition, nextChunkStart)) {
798 position.cluster = nextChunkStart;
799 } else {
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),
810 tempVar, ptr))
812 flagNoError = false;
813 r->_errno = EIO;
814 } else {
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) {
823 if (flagAppending) {
824 _FAT_cache_eraseWritePartialSector ( cache, ptr,
825 _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
826 } else {
827 _FAT_cache_writePartialSector ( cache, ptr,
828 _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
830 position.byte += remain;
831 remain = 0;
835 // Amount written is the originally requested amount minus stuff remaining
836 len = len - remain;
838 // Update file information
839 file->modified = true;
840 if (file->append) {
841 // Appending doesn't affect the read pointer
842 file->appendPosition = position;
843 file->filesize += len;
844 } else {
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);
855 return len;
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;
863 int clusCount;
864 off_t newPosition;
865 uint32_t position;
867 if ((file == NULL) || (file->inUse == false)) {
868 // invalid file
869 r->_errno = EBADF;
870 return -1;
873 partition = file->partition;
874 _FAT_lock(&partition->lock);
876 switch (dir) {
877 case SEEK_SET:
878 newPosition = pos;
879 break;
880 case SEEK_CUR:
881 newPosition = (off_t)file->currentPosition + pos;
882 break;
883 case SEEK_END:
884 newPosition = (off_t)file->filesize + pos;
885 break;
886 default:
887 _FAT_unlock(&partition->lock);
888 r->_errno = EINVAL;
889 return -1;
892 if ((pos > 0) && (newPosition < 0)) {
893 _FAT_unlock(&partition->lock);
894 r->_errno = EOVERFLOW;
895 return -1;
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);
902 r->_errno = EINVAL;
903 return -1;
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,
912 // and store them
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;
920 } else {
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)) {
927 clusCount--;
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
933 if (clusCount > 0) {
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;
938 } else {
939 _FAT_unlock(&partition->lock);
940 r->_errno = EINVAL;
941 return -1;
945 file->rwPosition.cluster = cluster;
948 // Save position
949 file->currentPosition = position;
951 _FAT_unlock(&partition->lock);
952 return position;
957 int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st) {
958 FILE_STRUCT* file = (FILE_STRUCT*) fd;
959 PARTITION* partition;
960 DIR_ENTRY fileEntry;
962 if ((file == NULL) || (file->inUse == false)) {
963 // invalid file
964 r->_errno = EBADF;
965 return -1;
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);
977 r->_errno = EIO;
978 return -1;
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);
989 return 0;
992 int _FAT_ftruncate_r (struct _reent *r, int fd, off_t len) {
993 FILE_STRUCT* file = (FILE_STRUCT*) fd;
994 PARTITION* partition;
995 int ret=0;
996 uint32_t newSize = (uint32_t)len;
998 if (len < 0) {
999 // Trying to truncate to a negative size
1000 r->_errno = EINVAL;
1001 return -1;
1004 if ((sizeof(len) > 4) && len > (off_t)FILE_MAX_SIZE) {
1005 // Trying to extend the file beyond what FAT supports
1006 r->_errno = EFBIG;
1007 return -1;
1010 if (!file || !file->inUse) {
1011 // invalid file
1012 r->_errno = EBADF;
1013 return -1;
1016 if (!file->write) {
1017 // Read-only file
1018 r->_errno = EINVAL;
1019 return -1;
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);
1035 r->_errno = ENOSPC;
1036 return -1;
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)) {
1051 ret = -1;
1053 // Set the append position to the new rwPointer
1054 if (file->append) {
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
1062 if (len == 0) {
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;
1070 } else {
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);
1081 if (file->append) {
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;
1087 } else {
1088 file->appendPosition.sector = (newSize % partition->bytesPerCluster) / BYTES_PER_READ;
1090 file->appendPosition.cluster = lastCluster;
1093 } else {
1094 // Truncating to same length, so don't do anything
1097 file->filesize = newSize;
1098 file->modified = true;
1100 _FAT_unlock(&partition->lock);
1101 return ret;
1104 int _FAT_fsync_r (struct _reent *r, int fd) {
1105 FILE_STRUCT* file = (FILE_STRUCT*) fd;
1106 int ret = 0;
1108 if (!file->inUse) {
1109 r->_errno = EBADF;
1110 return -1;
1113 _FAT_lock(&file->partition->lock);
1115 ret = _FAT_syncToDisc (file);
1116 if (ret != 0) {
1117 r->_errno = ret;
1118 ret = -1;
1121 _FAT_unlock(&file->partition->lock);
1123 return ret;