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