1 /*
2 fatfile.c
3
4 Functions used by the newlib disc stubs to interface with
5 this library
6
7 Copyright (c) 2006 Michael "Chishm" Chisholm
8
9 Redistribution and use in source and binary forms, with or without modification,
10 are permitted provided that the following conditions are met:
11
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.
19
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.
29
30 2009-10-23 oggzee: fixes for cluster aligned file size (write, truncate, seek)
31 */
32
33
34 #include "fatfile.h"
35
36 #include <fcntl.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <ctype.h>
40 #include <unistd.h>
41
42 #include "cache.h"
43 #include "file_allocation_table.h"
44 #include "bit_ops.h"
45 #include "filetime.h"
46 #include "lock.h"
47
_FAT_findEntry(const char * path,DIR_ENTRY * dirEntry)48 bool _FAT_findEntry(const char *path, DIR_ENTRY *dirEntry) {
49 PARTITION *partition = _FAT_partition_getPartitionFromPath(path);
50
51 /* Check Partition */
52 if( !partition )
53 return false;
54
55 /* Move the path pointer to the start of the actual path */
56 if (strchr (path, ':') != NULL)
57 path = strchr (path, ':') + 1;
58 if (strchr (path, ':') != NULL)
59 return false;
60
61 /* Search for the file on the disc */
62 return _FAT_directory_entryFromPath (partition, dirEntry, path, NULL);
63
64 }
65
FAT_getAttr(const char * file)66 int FAT_getAttr(const char *file) {
67 DIR_ENTRY dirEntry;
68 if (!_FAT_findEntry(file,&dirEntry)) return -1;
69
70 return dirEntry.entryData[DIR_ENTRY_attributes];
71 }
72
FAT_setAttr(const char * file,uint8_t attr)73 int FAT_setAttr(const char *file, uint8_t attr) {
74
75 /* Defines... */
76 DIR_ENTRY_POSITION entryEnd;
77 PARTITION *partition = NULL;
78 DIR_ENTRY dirEntry;
79
80 /* Get Partition */
81 partition = _FAT_partition_getPartitionFromPath( file );
82
83 /* Check Partition */
84 if( !partition )
85 return -1;
86
87 /* Move the path pointer to the start of the actual path */
88 if (strchr (file, ':') != NULL)
89 file = strchr (file, ':') + 1;
90 if (strchr (file, ':') != NULL)
91 return -1;
92
93 /* Get DIR_ENTRY */
94 if( !_FAT_directory_entryFromPath (partition, &dirEntry, file, NULL) )
95 return -1;
96
97 /* Get Entry-End */
98 entryEnd = dirEntry.dataEnd;
99
100 /* Lock Partition */
101 _FAT_lock(&partition->lock);
102
103
104 /* Write Data */
105 _FAT_cache_writePartialSector (
106 partition->cache /* Cache to write */
107 , &attr /* Value to be written */
108 , _FAT_fat_clusterToSector( partition , entryEnd.cluster ) + entryEnd.sector /* cluster */
109 , entryEnd.offset * DIR_ENTRY_DATA_SIZE + DIR_ENTRY_attributes /* offset */
110 , 1 /* Size in bytes */
111 );
112
113 /* Flush any sectors in the disc cache */
114 if ( !_FAT_cache_flush( partition->cache ) )
115 {
116 _FAT_unlock(&partition->lock); /* Unlock Partition */
117 return -1;
118 }
119
120 /* Unlock Partition */
121 _FAT_unlock(&partition->lock);
122
123 return 0;
124 }
125
126
_FAT_open_r(struct _reent * r,void * fileStruct,const char * path,int flags,int mode)127 int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) {
128 PARTITION* partition = NULL;
129 bool fileExists;
130 DIR_ENTRY dirEntry;
131 const char* pathEnd;
132 uint32_t dirCluster;
133 FILE_STRUCT* file = (FILE_STRUCT*) fileStruct;
134 partition = _FAT_partition_getPartitionFromPath (path);
135
136 if (partition == NULL)
137 {
138 r->_errno = ENODEV;
139 return -1;
140 }
141
142 /* Move the path pointer to the start of the actual path */
143 if (strchr (path, ':') != NULL)
144 path = strchr (path, ':') + 1;
145
146 if (strchr (path, ':') != NULL)
147 {
148 r->_errno = EINVAL;
149 return -1;
150 }
151
152 /* Determine which mode the file is openned for */
153 if ((flags & 0x03) == O_RDONLY)
154 {
155 /* Open the file for read-only access */
156 file->read = true;
157 file->write = false;
158 file->append = false;
159 }
160 else if ((flags & 0x03) == O_WRONLY)
161 {
162 /* Open file for write only access */
163 file->read = false;
164 file->write = true;
165 file->append = false;
166 }
167 else if ((flags & 0x03) == O_RDWR)
168 {
169 /* Open file for read/write access */
170 file->read = true;
171 file->write = true;
172 file->append = false;
173 } else {
174 r->_errno = EACCES;
175 return -1;
176 }
177
178 /* Make sure we aren't trying to write to a read-only disc */
179 if (file->write && partition->readOnly)
180 {
181 r->_errno = EROFS;
182 return -1;
183 }
184
185 /* Search for the file on the disc */
186 _FAT_lock(&partition->lock);
187 fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
188
189 /* The file shouldn't exist if we are trying to create it */
190 if ((flags & O_CREAT) && (flags & O_EXCL) && fileExists)
191 {
192 _FAT_unlock(&partition->lock);
193 r->_errno = EEXIST;
194 return -1;
195 }
196
197 /* It should not be a directory if we're opening a file, */
198 if (fileExists && _FAT_directory_isDirectory(&dirEntry))
199 {
200 _FAT_unlock(&partition->lock);
201 r->_errno = EISDIR;
202 return -1;
203 }
204
205 /* We haven't modified the file yet */
206 file->modified = false;
207
208 /* If the file doesn't exist, create it if we're allowed to */
209 if (!fileExists)
210 {
211 if (flags & O_CREAT)
212 {
213 if (partition->readOnly)
214 {
215 /* We can't write to a read-only partition */
216 _FAT_unlock(&partition->lock);
217 r->_errno = EROFS;
218 return -1;
219 }
220 /* Create the file
221 * Get the directory it has to go in */
222 pathEnd = strrchr (path, DIR_SEPARATOR);
223 if (pathEnd == NULL)
224 {
225 /* No path was specified */
226 dirCluster = partition->cwdCluster;
227 pathEnd = path;
228 }
229 else
230 {
231 /* Path was specified -- get the right dirCluster
232 * Recycling dirEntry, since it needs to be recreated anyway */
233 if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) ||
234 !_FAT_directory_isDirectory(&dirEntry))
235 {
236 _FAT_unlock(&partition->lock);
237 r->_errno = ENOTDIR;
238 return -1;
239 }
240 dirCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
241 /* Move the pathEnd past the last DIR_SEPARATOR */
242 pathEnd += 1;
243 }
244 /* Create the entry data */
245 strncpy (dirEntry.filename, pathEnd, NAME_MAX - 1);
246 memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE);
247
248 /* Set the creation time and date */
249 dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0;
250 u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC());
251 u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC());
252
253 if (!_FAT_directory_addEntry (partition, &dirEntry, dirCluster))
254 {
255 _FAT_unlock(&partition->lock);
256 r->_errno = ENOSPC;
257 return -1;
258 }
259
260 /* File entry is modified */
261 file->modified = true;
262 }
263 else
264 {
265 /* file doesn't exist, and we aren't creating it */
266 _FAT_unlock(&partition->lock);
267 r->_errno = ENOENT;
268 return -1;
269 }
270 }
271
272 file->filesize = u8array_to_u32 (dirEntry.entryData, DIR_ENTRY_fileSize);
273
274 #if 0
275 /* Allow LARGEFILEs with undefined results
276 * Make sure that the file size can fit in the available space */
277 if (!(flags & O_LARGEFILE) && (file->filesize >= (1<<31)))
278 {
279 r->_errno = EFBIG;
280 return -1;
281 }
282 #endif
283
284 /* Make sure we aren't trying to write to a read-only file */
285 if (file->write && !_FAT_directory_isWritable(&dirEntry)) {
286 _FAT_unlock(&partition->lock);
287 r->_errno = EROFS;
288 return -1;
289 }
290
291 /* Associate this file with a particular partition */
292 file->partition = partition;
293
294 file->startCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
295
296 /* Truncate the file if requested */
297 if ((flags & O_TRUNC) && file->write && (file->startCluster != 0)) {
298 _FAT_fat_clearLinks (partition, file->startCluster);
299 file->startCluster = CLUSTER_FREE;
300 file->filesize = 0;
301 /* File is modified since we just cut it all off */
302 file->modified = true;
303 }
304
305 /* Remember the position of this file's directory entry */
306 file->dirEntryStart = dirEntry.dataStart; /* Points to the start of the LFN entries of a file, or the alias for no LFN */
307 file->dirEntryEnd = dirEntry.dataEnd;
308
309 /* Reset read/write pointer */
310 file->currentPosition = 0;
311 file->rwPosition.cluster = file->startCluster;
312 file->rwPosition.sector = 0;
313 file->rwPosition.byte = 0;
314
315 if (flags & O_APPEND)
316 {
317 file->append = true;
318
319 /* Set append pointer to the end of the file */
320 file->appendPosition.cluster = _FAT_fat_lastCluster (partition, file->startCluster);
321 file->appendPosition.sector = (file->filesize % partition->bytesPerCluster) / partition->bytesPerSector;
322 file->appendPosition.byte = file->filesize % partition->bytesPerSector;
323
324 /* Check if the end of the file is on the end of a cluster */
325 if ( (file->filesize > 0) && ((file->filesize % partition->bytesPerCluster)==0) ){
326 /* Set flag to allocate a new cluster */
327 file->appendPosition.sector = partition->sectorsPerCluster;
328 file->appendPosition.byte = 0;
329 }
330 } else {
331 file->append = false;
332 /* Use something sane for the append pointer, so the whole file struct contains known values */
333 file->appendPosition = file->rwPosition;
334 }
335
336 file->inUse = true;
337
338 /* Insert this file into the double-linked list of open files */
339 partition->openFileCount += 1;
340 if (partition->firstOpenFile) {
341 file->nextOpenFile = partition->firstOpenFile;
342 partition->firstOpenFile->prevOpenFile = file;
343 } else {
344 file->nextOpenFile = NULL;
345 }
346 file->prevOpenFile = NULL;
347 partition->firstOpenFile = file;
348
349 _FAT_unlock(&partition->lock);
350
351 return (int) file;
352 }
353
354 /*
355 Synchronizes the file data to disc.
356 Does no locking of its own -- lock the partition before calling.
357 Returns 0 on success, an error code on failure.
358 */
_FAT_syncToDisc(FILE_STRUCT * file)359 int _FAT_syncToDisc (FILE_STRUCT* file) {
360 uint8_t dirEntryData[DIR_ENTRY_DATA_SIZE];
361
362 if (!file || !file->inUse) {
363 return EBADF;
364 }
365
366 if (file->write && file->modified) {
367 /* Load the old entry */
368 _FAT_cache_readPartialSector (file->partition->cache, dirEntryData,
369 _FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector,
370 file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
371
372 // Write new data to the directory entry
373 // File size
374 u32_to_u8array (dirEntryData, DIR_ENTRY_fileSize, file->filesize);
375
376 // Start cluster
377 u16_to_u8array (dirEntryData, DIR_ENTRY_cluster, file->startCluster);
378 u16_to_u8array (dirEntryData, DIR_ENTRY_clusterHigh, file->startCluster >> 16);
379
380 // Modification time and date
381 u16_to_u8array (dirEntryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC());
382 u16_to_u8array (dirEntryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC());
383
384 // Access date
385 u16_to_u8array (dirEntryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC());
386
387 // Set archive attribute
388 dirEntryData[DIR_ENTRY_attributes] |= ATTRIB_ARCH;
389
390 // Write the new entry
391 _FAT_cache_writePartialSector (file->partition->cache, dirEntryData,
392 _FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector,
393 file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
394
395 // Flush any sectors in the disc cache
396 if (!_FAT_cache_flush(file->partition->cache)) {
397 return EIO;
398 }
399 }
400
401 file->modified = false;
402
403 return 0;
404 }
405
406
_FAT_close_r(struct _reent * r,void * fd)407 int _FAT_close_r (struct _reent *r, void *fd) {
408 FILE_STRUCT* file = (FILE_STRUCT*) fd;
409 int ret = 0;
410
411 if (!file->inUse) {
412 r->_errno = EBADF;
413 return -1;
414 }
415
416 _FAT_lock(&file->partition->lock);
417
418 if (file->write) {
419 ret = _FAT_syncToDisc (file);
420 if (ret != 0) {
421 r->_errno = ret;
422 ret = -1;
423 }
424 }
425
426 file->inUse = false;
427
428 // Remove this file from the double-linked list of open files
429 file->partition->openFileCount -= 1;
430 if (file->nextOpenFile) {
431 file->nextOpenFile->prevOpenFile = file->prevOpenFile;
432 }
433 if (file->prevOpenFile) {
434 file->prevOpenFile->nextOpenFile = file->nextOpenFile;
435 } else {
436 file->partition->firstOpenFile = file->nextOpenFile;
437 }
438
439 _FAT_unlock(&file->partition->lock);
440
441 return ret;
442 }
443
_FAT_read_r(struct _reent * r,void * fd,char * ptr,size_t len)444 ssize_t _FAT_read_r (struct _reent *r, void *fd, char *ptr, size_t len) {
445 FILE_STRUCT* file = (FILE_STRUCT*) fd;
446 PARTITION* partition;
447 CACHE* cache;
448 FILE_POSITION position;
449 uint32_t tempNextCluster;
450 unsigned int tempVar;
451 size_t remain;
452 bool flagNoError = true;
453
454 // Short circuit cases where len is 0 (or less)
455 if (len <= 0)
456 return 0;
457
458 /* Make sure we can actually read from the file */
459 if ((file == NULL) || !file->inUse || !file->read) {
460 r->_errno = EBADF;
461 return -1;
462 }
463
464 partition = file->partition;
465 _FAT_lock(&partition->lock);
466
467 /* Don't try to read if the read pointer is past the end of file */
468 if (file->currentPosition >= file->filesize || file->startCluster == CLUSTER_FREE)
469 {
470 r->_errno = EOVERFLOW;
471 _FAT_unlock(&partition->lock);
472 return 0;
473 }
474
475 // Don't read past end of file
476 if (len + file->currentPosition > file->filesize) {
477 r->_errno = EOVERFLOW;
478 len = file->filesize - file->currentPosition;
479 }
480
481 remain = len;
482 position = file->rwPosition;
483 cache = file->partition->cache;
484
485 // Align to sector
486 tempVar = partition->bytesPerSector - position.byte;
487 if (tempVar > remain) {
488 tempVar = remain;
489 }
490
491 if ((tempVar < partition->bytesPerSector) && flagNoError)
492 {
493 _FAT_cache_readPartialSector ( cache, ptr, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector,
494 position.byte, tempVar);
495
496 remain -= tempVar;
497 ptr += tempVar;
498
499 position.byte += tempVar;
500 if (position.byte >= partition->bytesPerSector) {
501 position.byte = 0;
502 position.sector++;
503 }
504 }
505
506 /* align to cluster
507 * tempVar is number of sectors to read */
508 if (remain > (partition->sectorsPerCluster - position.sector) * partition->bytesPerSector) {
509 tempVar = partition->sectorsPerCluster - position.sector;
510 } else {
511 tempVar = remain / partition->bytesPerSector;
512 }
513
514 if ((tempVar > 0) && flagNoError) {
515 if (! _FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector,
516 tempVar, ptr))
517 {
518 flagNoError = false;
519 r->_errno = EIO;
520 } else {
521 ptr += tempVar * partition->bytesPerSector;
522 remain -= tempVar * partition->bytesPerSector;
523 position.sector += tempVar;
524 }
525 }
526
527 /* Move onto next cluster
528 * It should get to here without reading anything if a cluster is due to be allocated */
529 if ((position.sector >= partition->sectorsPerCluster) && flagNoError) {
530 tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
531 if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) {
532 position.sector = partition->sectorsPerCluster;
533 } else if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
534 r->_errno = EIO;
535 flagNoError = false;
536 } else {
537 position.sector = 0;
538 position.cluster = tempNextCluster;
539 }
540 }
541
542 /* Read in whole clusters, contiguous blocks at a time */
543 while ((remain >= partition->bytesPerCluster) && flagNoError) {
544 uint32_t chunkEnd;
545 uint32_t nextChunkStart = position.cluster;
546 size_t chunkSize = 0;
547
548 do {
549 chunkEnd = nextChunkStart;
550 nextChunkStart = _FAT_fat_nextCluster (partition, chunkEnd);
551 chunkSize += partition->bytesPerCluster;
552 } while ((nextChunkStart == chunkEnd + 1) &&
553 #ifdef LIMIT_SECTORS
554 (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * partition->bytesPerSector) &&
555 #endif
556 (chunkSize + partition->bytesPerCluster <= remain));
557
558 if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster),
559 chunkSize / partition->bytesPerSector, ptr))
560 {
561 flagNoError = false;
562 r->_errno = EIO;
563 break;
564 }
565 ptr += chunkSize;
566 remain -= chunkSize;
567
568 /* Advance to next cluster */
569 if ((remain == 0) && (nextChunkStart == CLUSTER_EOF)) {
570 position.sector = partition->sectorsPerCluster;
571 position.cluster = chunkEnd;
572 } else if (!_FAT_fat_isValidCluster(partition, nextChunkStart)) {
573 r->_errno = EIO;
574 flagNoError = false;
575 } else {
576 position.sector = 0;
577 position.cluster = nextChunkStart;
578 }
579 }
580
581 /* Read remaining sectors */
582 tempVar = remain / partition->bytesPerSector; /* Number of sectors left */
583 if ((tempVar > 0) && flagNoError) {
584 if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster),
585 tempVar, ptr))
586 {
587 flagNoError = false;
588 r->_errno = EIO;
589 } else {
590 ptr += tempVar * partition->bytesPerSector;
591 remain -= tempVar * partition->bytesPerSector;
592 position.sector += tempVar;
593 }
594 }
595
596 // Last remaining sector
597 // Check if anything is left
598 if ((remain > 0) && flagNoError) {
599 _FAT_cache_readPartialSector ( cache, ptr,
600 _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
601 position.byte += remain;
602 remain = 0;
603 }
604
605 // Length read is the wanted length minus the stuff not read
606 len = len - remain;
607
608 // Update file information
609 file->rwPosition = position;
610 file->currentPosition += len;
611
612 _FAT_unlock(&partition->lock);
613 return len;
614 }
615
616 // if current position is on the cluster border and more data has to be written
617 // then get next cluster or allocate next cluster
618 // this solves the over-allocation problems when file size is aligned to cluster size
619 // return true on succes, false on error
_FAT_check_position_for_next_cluster(struct _reent * r,FILE_POSITION * position,PARTITION * partition,size_t remain,bool * flagNoError)620 static bool _FAT_check_position_for_next_cluster(struct _reent *r,
621 FILE_POSITION *position, PARTITION* partition, size_t remain, bool *flagNoError)
622 {
623 uint32_t tempNextCluster;
624 // do nothing if no more data to write
625 if (remain == 0) return true;
626 if (flagNoError && *flagNoError == false) return false;
627 if (position->sector > partition->sectorsPerCluster) {
628 // invalid arguments - internal error
629 r->_errno = EINVAL;
630 goto err;
631 }
632 if (position->sector == partition->sectorsPerCluster) {
633 // need to advance to next cluster
634 tempNextCluster = _FAT_fat_nextCluster(partition, position->cluster);
635 if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) {
636 // Ran out of clusters so get a new one
637 tempNextCluster = _FAT_fat_linkFreeCluster(partition, position->cluster);
638 }
639 if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
640 // Couldn't get a cluster, so abort
641 r->_errno = ENOSPC;
642 goto err;
643 }
644 position->sector = 0;
645 position->cluster = tempNextCluster;
646 }
647 return true;
648 err:
649 if (flagNoError) *flagNoError = false;
650 return false;
651 }
652
653 /*
654 Extend a file so that the size is the same as the rwPosition
655 */
_FAT_file_extend_r(struct _reent * r,FILE_STRUCT * file)656 static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) {
657 PARTITION* partition = file->partition;
658 CACHE* cache = file->partition->cache;
659 FILE_POSITION position;
660 uint8_t zeroBuffer [partition->bytesPerSector];
661 memset(zeroBuffer, 0, partition->bytesPerSector);
662 uint32_t remain;
663 uint32_t tempNextCluster;
664 unsigned int sector;
665
666 position.byte = file->filesize % partition->bytesPerSector;
667 position.sector = (file->filesize % partition->bytesPerCluster) / partition->bytesPerSector;
668 // It is assumed that there is always a startCluster
669 // This will be true when _FAT_file_extend_r is called from _FAT_write_r
670 position.cluster = _FAT_fat_lastCluster (partition, file->startCluster);
671
672 remain = file->currentPosition - file->filesize;
673
674 if ((remain > 0) && (file->filesize > 0) && (position.sector == 0) && (position.byte == 0)) {
675 // Get a new cluster on the edge of a cluster boundary
676 tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
677 if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
678 // Couldn't get a cluster, so abort
679 r->_errno = ENOSPC;
680 return false;
681 }
682 position.cluster = tempNextCluster;
683 position.sector = 0;
684 }
685
686 if (remain + position.byte < partition->bytesPerSector) {
687 // Only need to clear to the end of the sector
688 _FAT_cache_writePartialSector (cache, zeroBuffer,
689 _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, remain);
690 position.byte += remain;
691 } else {
692 if (position.byte > 0) {
693 _FAT_cache_writePartialSector (cache, zeroBuffer,
694 _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte,
695 partition->bytesPerSector - position.byte);
696 remain -= (partition->bytesPerSector - position.byte);
697 position.byte = 0;
698 position.sector ++;
699 }
700
701 while (remain >= partition->bytesPerSector) {
702 if (position.sector >= partition->sectorsPerCluster) {
703 position.sector = 0;
704 // Ran out of clusters so get a new one
705 tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
706 if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
707 // Couldn't get a cluster, so abort
708 r->_errno = ENOSPC;
709 return false;
710 }
711 position.cluster = tempNextCluster;
712 }
713
714 sector = _FAT_fat_clusterToSector (partition, position.cluster) + position.sector;
715 _FAT_cache_writeSectors (cache, sector, 1, zeroBuffer);
716
717 remain -= partition->bytesPerSector;
718 position.sector ++;
719 }
720
721 // error already marked
722 if (!_FAT_check_position_for_next_cluster(r, &position, partition, remain, NULL))
723 return false;
724
725 if (remain > 0)
726 {
727 _FAT_cache_writePartialSector (cache, zeroBuffer,
728 _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
729 position.byte = remain;
730 }
731 }
732
733 file->rwPosition = position;
734 file->filesize = file->currentPosition;
735 return true;
736 }
737
_FAT_write_r(struct _reent * r,void * fd,const char * ptr,size_t len)738 ssize_t _FAT_write_r (struct _reent *r, void *fd, const char *ptr, size_t len) {
739 FILE_STRUCT* file = (FILE_STRUCT*) fd;
740 PARTITION* partition;
741 CACHE* cache;
742 FILE_POSITION position;
743 uint32_t tempNextCluster;
744 unsigned int tempVar;
745 size_t remain;
746 bool flagNoError = true;
747 bool flagAppending = false;
748
749 /* Make sure we can actually write to the file */
750 if ((file == NULL) || !file->inUse || !file->write) {
751 r->_errno = EBADF;
752 return -1;
753 }
754
755 partition = file->partition;
756 cache = file->partition->cache;
757 _FAT_lock(&partition->lock);
758
759 /* Only write up to the maximum file size, taking into account wrap-around of ints */
760 if (len + file->filesize > FILE_MAX_SIZE || len + file->filesize < file->filesize) {
761 len = FILE_MAX_SIZE - file->filesize;
762 }
763
764 /* Short circuit cases where len is 0 (or less) */
765 if (len <= 0) {
766 _FAT_unlock(&partition->lock);
767 return 0;
768 }
769
770 remain = len;
771
772 /* Get a new cluster for the start of the file if required */
773 if (file->startCluster == CLUSTER_FREE) {
774 tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE);
775 if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
776 /* Couldn't get a cluster, so abort immediately */
777 _FAT_unlock(&partition->lock);
778 r->_errno = ENOSPC;
779 return -1;
780 }
781 file->startCluster = tempNextCluster;
782
783 /* Appending starts at the begining for a 0 byte file */
784 file->appendPosition.cluster = file->startCluster;
785 file->appendPosition.sector = 0;
786 file->appendPosition.byte = 0;
787
788 file->rwPosition.cluster = file->startCluster;
789 file->rwPosition.sector = 0;
790 file->rwPosition.byte = 0;
791 }
792
793 if (file->append) {
794 position = file->appendPosition;
795 flagAppending = true;
796 } else {
797 // If the write pointer is past the end of the file, extend the file to that size
798 if (file->currentPosition > file->filesize) {
799 if (!_FAT_file_extend_r (r, file)) {
800 _FAT_unlock(&partition->lock);
801 return -1;
802 }
803 }
804
805 // Write at current read pointer
806 position = file->rwPosition;
807
808 // If it is writing past the current end of file, set appending flag
809 if (len + file->currentPosition > file->filesize) {
810 flagAppending = true;
811 }
812 }
813
814 // Move onto next cluster if needed
815 _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError);
816
817 // Align to sector
818 tempVar = partition->bytesPerSector - position.byte;
819 if (tempVar > remain) {
820 tempVar = remain;
821 }
822
823 if ((tempVar < partition->bytesPerSector) && flagNoError) {
824 // Write partial sector to disk
825 _FAT_cache_writePartialSector (cache, ptr,
826 _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, tempVar);
827
828 remain -= tempVar;
829 ptr += tempVar;
830 position.byte += tempVar;
831
832
833 // Move onto next sector
834 if (position.byte >= partition->bytesPerSector) {
835 position.byte = 0;
836 position.sector ++;
837 }
838 }
839
840 // Align to cluster
841 // tempVar is number of sectors to write
842 if (remain > (partition->sectorsPerCluster - position.sector) * partition->bytesPerSector) {
843 tempVar = partition->sectorsPerCluster - position.sector;
844 } else {
845 tempVar = remain / partition->bytesPerSector;
846 }
847
848 if ((tempVar > 0 && tempVar < partition->sectorsPerCluster) && flagNoError) {
849 if (!_FAT_cache_writeSectors (cache,
850 _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, tempVar, ptr))
851 {
852 flagNoError = false;
853 r->_errno = EIO;
854 } else {
855 ptr += tempVar * partition->bytesPerSector;
856 remain -= tempVar * partition->bytesPerSector;
857 position.sector += tempVar;
858 }
859 }
860
861 // Write whole clusters
862 while ((remain >= partition->bytesPerCluster) && flagNoError) {
863 // allocate next cluster
864 _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError);
865 if (!flagNoError) break;
866 // set indexes to the current position
867 uint32_t chunkEnd = position.cluster;
868 uint32_t nextChunkStart = position.cluster;
869 size_t chunkSize = partition->bytesPerCluster;
870 FILE_POSITION next_position = position;
871
872 // group consecutive clusters
873 while (flagNoError &&
874 #ifdef LIMIT_SECTORS
875 (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * partition->bytesPerSector) &&
876 #endif
877 (chunkSize + partition->bytesPerCluster < remain))
878 {
879 // pretend to use up all sectors in next_position
880 next_position.sector = partition->sectorsPerCluster;
881 // get or allocate next cluster
882 _FAT_check_position_for_next_cluster(r, &next_position, partition,
883 remain - chunkSize, &flagNoError);
884 if (!flagNoError) break; // exit loop on error
885 nextChunkStart = next_position.cluster;
886 if (nextChunkStart != chunkEnd + 1) break; // exit loop if not consecutive
887 chunkEnd = nextChunkStart;
888 chunkSize += partition->bytesPerCluster;
889 }
890
891 if ( !_FAT_cache_writeSectors (cache,
892 _FAT_fat_clusterToSector(partition, position.cluster), chunkSize / partition->bytesPerSector, ptr))
893 {
894 flagNoError = false;
895 r->_errno = EIO;
896 break;
897 }
898 ptr += chunkSize;
899 remain -= chunkSize;
900
901 if ((chunkEnd != nextChunkStart) && _FAT_fat_isValidCluster(partition, nextChunkStart)) {
902 // new cluster is already allocated (because it was not consecutive)
903 position.cluster = nextChunkStart;
904 position.sector = 0;
905 } else {
906 // Allocate a new cluster when next writing the file
907 position.cluster = chunkEnd;
908 position.sector = partition->sectorsPerCluster;
909 }
910 }
911
912 // allocate next cluster if needed
913 _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError);
914
915 // Write remaining sectors
916 tempVar = remain / partition->bytesPerSector; // Number of sectors left
917 if ((tempVar > 0) && flagNoError) {
918 if (!_FAT_cache_writeSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), tempVar, ptr))
919 {
920 flagNoError = false;
921 r->_errno = EIO;
922 } else {
923 ptr += tempVar * partition->bytesPerSector;
924 remain -= tempVar * partition->bytesPerSector;
925 position.sector += tempVar;
926 }
927 }
928
929 // Last remaining sector
930 if ((remain > 0) && flagNoError) {
931 if (flagAppending) {
932 _FAT_cache_eraseWritePartialSector ( cache, ptr,
933 _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
934 } else {
935 _FAT_cache_writePartialSector ( cache, ptr,
936 _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
937 }
938 position.byte += remain;
939 remain = 0;
940 }
941
942
943 // Amount written is the originally requested amount minus stuff remaining
944 len = len - remain;
945
946 // Update file information
947 file->modified = true;
948 if (file->append) {
949 // Appending doesn't affect the read pointer
950 file->appendPosition = position;
951 file->filesize += len;
952 } else {
953 // Writing also shifts the read pointer
954 file->rwPosition = position;
955 file->currentPosition += len;
956 if (file->filesize < file->currentPosition) {
957 file->filesize = file->currentPosition;
958 }
959 }
960 _FAT_unlock(&partition->lock);
961
962 return len;
963 }
964
965
_FAT_seek_r(struct _reent * r,void * fd,off_t pos,int dir)966 off_t _FAT_seek_r (struct _reent *r, void *fd, off_t pos, int dir) {
967 FILE_STRUCT* file = (FILE_STRUCT*) fd;
968 PARTITION* partition;
969 uint32_t cluster, nextCluster;
970 int clusCount;
971 off_t newPosition;
972 uint32_t position;
973
974 if ((file == NULL) || (file->inUse == false)) {
975 // invalid file
976 r->_errno = EBADF;
977 return -1;
978 }
979
980 partition = file->partition;
981 _FAT_lock(&partition->lock);
982
983 switch (dir) {
984 case SEEK_SET:
985 newPosition = pos;
986 break;
987 case SEEK_CUR:
988 newPosition = (off_t)file->currentPosition + pos;
989 break;
990 case SEEK_END:
991 newPosition = (off_t)file->filesize + pos;
992 break;
993 default:
994 _FAT_unlock(&partition->lock);
995 r->_errno = EINVAL;
996 return -1;
997 }
998
999 if ((pos > 0) && (newPosition < 0)) {
1000 _FAT_unlock(&partition->lock);
1001 r->_errno = EOVERFLOW;
1002 return -1;
1003 }
1004
1005 // newPosition can only be larger than the FILE_MAX_SIZE on platforms where
1006 // off_t is larger than 32 bits.
1007 if (newPosition < 0 || ((sizeof(newPosition) > 4) && newPosition > (off_t)FILE_MAX_SIZE)) {
1008 _FAT_unlock(&partition->lock);
1009 r->_errno = EINVAL;
1010 return -1;
1011 }
1012
1013 position = (uint32_t)newPosition;
1014
1015 // Only change the read/write position if it is within the bounds of the current filesize,
1016 // or at the very edge of the file
1017 if (position <= file->filesize && file->startCluster != CLUSTER_FREE) {
1018 // Calculate where the correct cluster is
1019 // how many clusters from start of file
1020 clusCount = position / partition->bytesPerCluster;
1021 cluster = file->startCluster;
1022 if (position >= file->currentPosition) {
1023 // start from current cluster
1024 int currentCount = file->currentPosition / partition->bytesPerCluster;
1025 if (file->rwPosition.sector == partition->sectorsPerCluster) {
1026 currentCount--;
1027 }
1028 clusCount -= currentCount;
1029 cluster = file->rwPosition.cluster;
1030 }
1031 // Calculate the sector and byte of the current position,
1032 // and store them
1033 file->rwPosition.sector = (position % partition->bytesPerCluster) / partition->bytesPerSector;
1034 file->rwPosition.byte = position % partition->bytesPerSector;
1035
1036 nextCluster = _FAT_fat_nextCluster (partition, cluster);
1037 while ((clusCount > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) {
1038 clusCount--;
1039 cluster = nextCluster;
1040 nextCluster = _FAT_fat_nextCluster (partition, cluster);
1041 }
1042
1043 // Check if ran out of clusters and it needs to allocate a new one
1044 if (clusCount > 0) {
1045 if ((clusCount == 1) && (file->filesize == position) && (file->rwPosition.sector == 0)) {
1046 // Set flag to allocate a new cluster
1047 file->rwPosition.sector = partition->sectorsPerCluster;
1048 file->rwPosition.byte = 0;
1049 } else {
1050 _FAT_unlock(&partition->lock);
1051 r->_errno = EINVAL;
1052 return -1;
1053 }
1054 }
1055
1056 file->rwPosition.cluster = cluster;
1057 }
1058
1059 // Save position
1060 file->currentPosition = position;
1061
1062 _FAT_unlock(&partition->lock);
1063 return position;
1064 }
1065
1066
1067
_FAT_fstat_r(struct _reent * r,void * fd,struct stat * st)1068 int _FAT_fstat_r (struct _reent *r, void *fd, struct stat *st) {
1069 FILE_STRUCT* file = (FILE_STRUCT*) fd;
1070 PARTITION* partition;
1071 DIR_ENTRY fileEntry;
1072
1073 if ((file == NULL) || (file->inUse == false)) {
1074 // invalid file
1075 r->_errno = EBADF;
1076 return -1;
1077 }
1078
1079 partition = file->partition;
1080 _FAT_lock(&partition->lock);
1081
1082 // Get the file's entry data
1083 fileEntry.dataStart = file->dirEntryStart;
1084 fileEntry.dataEnd = file->dirEntryEnd;
1085
1086 if (!_FAT_directory_entryFromPosition (partition, &fileEntry)) {
1087 _FAT_unlock(&partition->lock);
1088 r->_errno = EIO;
1089 return -1;
1090 }
1091
1092 // Fill in the stat struct
1093 _FAT_directory_entryStat (partition, &fileEntry, st);
1094
1095 // Fix stats that have changed since the file was openned
1096 st->st_ino = (ino_t)(file->startCluster); // The file serial number is the start cluster
1097 st->st_size = file->filesize; // File size
1098
1099 _FAT_unlock(&partition->lock);
1100 return 0;
1101 }
1102
_FAT_ftruncate_r(struct _reent * r,void * fd,off_t len)1103 int _FAT_ftruncate_r (struct _reent *r, void *fd, off_t len) {
1104 FILE_STRUCT* file = (FILE_STRUCT*) fd;
1105 PARTITION* partition;
1106 int ret=0;
1107 uint32_t newSize = (uint32_t)len;
1108
1109 if (len < 0) {
1110 // Trying to truncate to a negative size
1111 r->_errno = EINVAL;
1112 return -1;
1113 }
1114
1115 if ((sizeof(len) > 4) && len > (off_t)FILE_MAX_SIZE) {
1116 // Trying to extend the file beyond what FAT supports
1117 r->_errno = EFBIG;
1118 return -1;
1119 }
1120
1121 if (!file || !file->inUse) {
1122 // invalid file
1123 r->_errno = EBADF;
1124 return -1;
1125 }
1126
1127 if (!file->write) {
1128 // Read-only file
1129 r->_errno = EINVAL;
1130 return -1;
1131 }
1132
1133 partition = file->partition;
1134 _FAT_lock(&partition->lock);
1135
1136 if (newSize > file->filesize) {
1137 // Expanding the file
1138 FILE_POSITION savedPosition;
1139 uint32_t savedOffset;
1140 // Get a new cluster for the start of the file if required
1141 if (file->startCluster == CLUSTER_FREE) {
1142 uint32_t tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE);
1143 if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
1144 // Couldn't get a cluster, so abort immediately
1145 _FAT_unlock(&partition->lock);
1146 r->_errno = ENOSPC;
1147 return -1;
1148 }
1149 file->startCluster = tempNextCluster;
1150
1151 file->rwPosition.cluster = file->startCluster;
1152 file->rwPosition.sector = 0;
1153 file->rwPosition.byte = 0;
1154 }
1155 // Save the read/write pointer
1156 savedPosition = file->rwPosition;
1157 savedOffset = file->currentPosition;
1158 // Set the position to the new size
1159 file->currentPosition = newSize;
1160 // Extend the file to the new position
1161 if (!_FAT_file_extend_r (r, file)) {
1162 ret = -1;
1163 }
1164 // Set the append position to the new rwPointer
1165 if (file->append) {
1166 file->appendPosition = file->rwPosition;
1167 }
1168 // Restore the old rwPointer;
1169 file->rwPosition = savedPosition;
1170 file->currentPosition = savedOffset;
1171 } else if (newSize < file->filesize){
1172 // Shrinking the file
1173 if (len == 0) {
1174 // Cutting the file down to nothing, clear all clusters used
1175 _FAT_fat_clearLinks (partition, file->startCluster);
1176 file->startCluster = CLUSTER_FREE;
1177
1178 file->appendPosition.cluster = CLUSTER_FREE;
1179 file->appendPosition.sector = 0;
1180 file->appendPosition.byte = 0;
1181 } else {
1182 // Trimming the file down to the required size
1183 unsigned int chainLength;
1184 uint32_t lastCluster;
1185
1186 // Drop the unneeded end of the cluster chain.
1187 // If the end falls on a cluster boundary, drop that cluster too,
1188 // then set a flag to allocate a cluster as needed
1189 chainLength = ((newSize-1) / partition->bytesPerCluster) + 1;
1190 lastCluster = _FAT_fat_trimChain (partition, file->startCluster, chainLength);
1191
1192 if (file->append) {
1193 file->appendPosition.byte = newSize % partition->bytesPerSector;
1194 // Does the end of the file fall on the edge of a cluster?
1195 if (newSize % partition->bytesPerCluster == 0) {
1196 // Set a flag to allocate a new cluster
1197 file->appendPosition.sector = partition->sectorsPerCluster;
1198 } else {
1199 file->appendPosition.sector = (newSize % partition->bytesPerCluster) / partition->bytesPerSector;
1200 }
1201 file->appendPosition.cluster = lastCluster;
1202 }
1203 }
1204 } else {
1205 // Truncating to same length, so don't do anything
1206 }
1207
1208 file->filesize = newSize;
1209 file->modified = true;
1210
1211 _FAT_unlock(&partition->lock);
1212 return ret;
1213 }
1214
_FAT_fsync_r(struct _reent * r,void * fd)1215 int _FAT_fsync_r (struct _reent *r, void *fd) {
1216 FILE_STRUCT* file = (FILE_STRUCT*) fd;
1217 int ret = 0;
1218
1219 if (!file->inUse) {
1220 r->_errno = EBADF;
1221 return -1;
1222 }
1223
1224 _FAT_lock(&file->partition->lock);
1225
1226 ret = _FAT_syncToDisc (file);
1227 if (ret != 0) {
1228 r->_errno = ret;
1229 ret = -1;
1230 }
1231
1232 _FAT_unlock(&file->partition->lock);
1233
1234 return ret;
1235 }
1236