wiiu-nanddumper-payload/ios_fs/source/fat/fat_filelib.c

1609 lines
47 KiB
C
Raw Normal View History

2017-03-25 22:03:19 +01:00
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// FAT16/32 File IO Library
// V2.6
// Ultra-Embedded.com
// Copyright 2003 - 2012
//
// Email: admin@ultra-embedded.com
//
// License: GPL
// If you would like a version with a more permissive license for use in
// closed source commercial applications please contact me for details.
//-----------------------------------------------------------------------------
//
// This file is part of FAT File IO Library.
//
// FAT File IO Library is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// FAT File IO Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FAT File IO Library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#include "../imports.h"
#include "fat_defs.h"
#include "fat_access.h"
#include "fat_table.h"
#include "fat_write.h"
#include "fat_misc.h"
#include "fat_string.h"
#include "fat_filelib.h"
#include "fat_cache.h"
//-----------------------------------------------------------------------------
// Locals
//-----------------------------------------------------------------------------
static FL_FILE _files[FATFS_MAX_OPEN_FILES];
static int _filelib_init = 0;
static int _filelib_valid = 0;
static struct fatfs _fs;
static struct fat_list _open_file_list;
static struct fat_list _free_file_list;
//-----------------------------------------------------------------------------
// Macros
//-----------------------------------------------------------------------------
// Macro for checking if file lib is initialised
#define CHECK_FL_INIT() { if (_filelib_init==0) fl_init(); }
#define FL_LOCK(a) do { if ((a)->fl_lock) (a)->fl_lock(); } while (0)
#define FL_UNLOCK(a) do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0)
//-----------------------------------------------------------------------------
// Local Functions
//-----------------------------------------------------------------------------
//static void _fl_init();
//-----------------------------------------------------------------------------
// _allocate_file: Find a slot in the open files buffer for a new file
//-----------------------------------------------------------------------------
static FL_FILE* _allocate_file(void)
{
// Allocate free file
struct fat_node *node = fat_list_pop_head(&_free_file_list);
// Add to open list
if (node)
fat_list_insert_last(&_open_file_list, node);
return fat_list_entry(node, FL_FILE, list_node);
}
//-----------------------------------------------------------------------------
// _check_file_open: Returns true if the file is already open
//-----------------------------------------------------------------------------
static int _check_file_open(FL_FILE* file)
{
struct fat_node *node;
// Compare open files
fat_list_for_each(&_open_file_list, node)
{
FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node);
// If not the current file
if (openFile != file)
{
// Compare path and name
if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
return 1;
}
}
return 0;
}
//-----------------------------------------------------------------------------
// _free_file: Free open file handle
//-----------------------------------------------------------------------------
static void _free_file(FL_FILE* file)
{
// Remove from open list
fat_list_remove(&_open_file_list, &file->list_node);
// Add to free list
fat_list_insert_last(&_free_file_list, &file->list_node);
}
//-----------------------------------------------------------------------------
// Low Level
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// _open_directory: Cycle through path string to find the start cluster
// address of the highest subdir.
//-----------------------------------------------------------------------------
static int _open_directory(char *path, uint32 *pathCluster)
{
int levels;
int sublevel;
char currentfolder[FATFS_MAX_LONG_FILENAME];
struct fat_dir_entry sfEntry;
uint32 startcluster;
// Set starting cluster to root cluster
startcluster = fatfs_get_root_cluster(&_fs);
// Find number of levels
levels = fatfs_total_path_levels(path);
// Cycle through each level and get the start sector
for (sublevel=0;sublevel<(levels+1);sublevel++)
{
if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
return 0;
// Find clusteraddress for folder (currentfolder)
if (fatfs_get_file_entry(&_fs, startcluster, currentfolder,&sfEntry))
{
// Check entry is folder
if (fatfs_entry_is_dir(&sfEntry))
startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
else
return 0;
}
else
return 0;
}
*pathCluster = startcluster;
return 1;
}
//-----------------------------------------------------------------------------
// _create_directory: Cycle through path string and create the end directory
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
static int _create_directory(char *path)
{
FL_FILE* file;
struct fat_dir_entry sfEntry;
char shortFilename[FAT_SFN_SIZE_FULL];
int tailNum = 0;
int i;
// Allocate a new file handle
file = _allocate_file();
if (!file)
return 0;
// Clear filename
memset(file->path, '\0', sizeof(file->path));
memset(file->filename, '\0', sizeof(file->filename));
// Split full path into filename and directory path
if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
{
_free_file(file);
return 0;
}
// Check if file already open
if (_check_file_open(file))
{
_free_file(file);
return 0;
}
// If file is in the root dir
if (file->path[0] == 0)
file->parentcluster = fatfs_get_root_cluster(&_fs);
else
{
// Find parent directory start cluster
if (!_open_directory(file->path, &file->parentcluster))
{
_free_file(file);
return 0;
}
}
// Check if same filename exists in directory
if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
{
_free_file(file);
return 0;
}
file->startcluster = 0;
// Create the file space for the folder (at least one clusters worth!)
if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
{
_free_file(file);
return 0;
}
// Erase new directory cluster
memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
for (i=0;i<_fs.sectors_per_cluster;i++)
{
if (!fatfs_write_sector(&_fs, file->startcluster, i, file->file_data_sector))
{
_free_file(file);
return 0;
}
}
#if FATFS_INC_LFN_SUPPORT
// Generate a short filename & tail
tailNum = 0;
do
{
// Create a standard short filename (without tail)
fatfs_lfn_create_sfn(shortFilename, file->filename);
// If second hit or more, generate a ~n tail
if (tailNum != 0)
fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
// Try with no tail if first entry
else
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
// Check if entry exists already or not
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
break;
tailNum++;
}
while (tailNum < 9999);
// We reached the max number of duplicate short file names (unlikely!)
if (tailNum == 9999)
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return 0;
}
#else
// Create a standard short filename (without tail)
if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return 0;
}
// Copy to SFN space
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
// Check if entry exists already
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return 0;
}
#endif
// Add file to disk
if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return 0;
}
// General
file->filelength = 0;
file->bytenum = 0;
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
file->filelength_changed = 0;
// Quick lookup for next link in the chain
file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
fatfs_fat_purge(&_fs);
_free_file(file);
return 1;
}
#endif
//-----------------------------------------------------------------------------
// _open_file: Open a file for reading
//-----------------------------------------------------------------------------
static FL_FILE* _open_file(const char *path)
{
FL_FILE* file;
struct fat_dir_entry sfEntry;
// Allocate a new file handle
file = _allocate_file();
if (!file)
return NULL;
// Clear filename
memset(file->path, '\0', sizeof(file->path));
memset(file->filename, '\0', sizeof(file->filename));
// Split full path into filename and directory path
if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
{
_free_file(file);
return NULL;
}
// Check if file already open
if (_check_file_open(file))
{
_free_file(file);
return NULL;
}
// If file is in the root dir
if (file->path[0]==0)
file->parentcluster = fatfs_get_root_cluster(&_fs);
else
{
// Find parent directory start cluster
if (!_open_directory(file->path, &file->parentcluster))
{
_free_file(file);
return NULL;
}
}
// Using dir cluster address search for filename
if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry))
// Make sure entry is file not dir!
if (fatfs_entry_is_file(&sfEntry))
{
// Initialise file details
memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
file->filelength = FAT_HTONL(sfEntry.FileSize);
file->bytenum = 0;
file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
file->filelength_changed = 0;
// Quick lookup for next link in the chain
file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
fatfs_cache_init(&_fs, file);
fatfs_fat_purge(&_fs);
return file;
}
_free_file(file);
return NULL;
}
//-----------------------------------------------------------------------------
// _create_file: Create a new file
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
static FL_FILE* _create_file(const char *filename)
{
FL_FILE* file;
struct fat_dir_entry sfEntry;
char shortFilename[FAT_SFN_SIZE_FULL];
int tailNum = 0;
// No write access?
if (!_fs.disk_io.write_media)
return NULL;
// Allocate a new file handle
file = _allocate_file();
if (!file)
return NULL;
// Clear filename
memset(file->path, '\0', sizeof(file->path));
memset(file->filename, '\0', sizeof(file->filename));
// Split full path into filename and directory path
if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
{
_free_file(file);
return NULL;
}
// Check if file already open
if (_check_file_open(file))
{
_free_file(file);
return NULL;
}
// If file is in the root dir
if (file->path[0] == 0)
file->parentcluster = fatfs_get_root_cluster(&_fs);
else
{
// Find parent directory start cluster
if (!_open_directory(file->path, &file->parentcluster))
{
_free_file(file);
return NULL;
}
}
// Check if same filename exists in directory
if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
{
_free_file(file);
return NULL;
}
file->startcluster = 0;
// Create the file space for the file (at least one clusters worth!)
if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
{
_free_file(file);
return NULL;
}
#if FATFS_INC_LFN_SUPPORT
// Generate a short filename & tail
tailNum = 0;
do
{
// Create a standard short filename (without tail)
fatfs_lfn_create_sfn(shortFilename, file->filename);
// If second hit or more, generate a ~n tail
if (tailNum != 0)
fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
// Try with no tail if first entry
else
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
// Check if entry exists already or not
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
break;
tailNum++;
}
while (tailNum < 9999);
// We reached the max number of duplicate short file names (unlikely!)
if (tailNum == 9999)
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return NULL;
}
#else
// Create a standard short filename (without tail)
if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return NULL;
}
// Copy to SFN space
memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
// Check if entry exists already
if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return NULL;
}
#endif
// Add file to disk
if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0))
{
// Delete allocated space
fatfs_free_cluster_chain(&_fs, file->startcluster);
_free_file(file);
return NULL;
}
// General
file->filelength = 0;
file->bytenum = 0;
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
file->filelength_changed = 0;
// Quick lookup for next link in the chain
file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
fatfs_cache_init(&_fs, file);
fatfs_fat_purge(&_fs);
return file;
}
#endif
//-----------------------------------------------------------------------------
// _read_sectors: Read sector(s) from disk to file
//-----------------------------------------------------------------------------
static uint32 _read_sectors(FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count)
{
uint32 Sector = 0;
uint32 ClusterIdx = 0;
uint32 Cluster = 0;
uint32 i;
uint32 lba;
// Find cluster index within file & sector with cluster
ClusterIdx = offset / _fs.sectors_per_cluster;
Sector = offset - (ClusterIdx * _fs.sectors_per_cluster);
// Limit number of sectors read to the number remaining in this cluster
if ((Sector + count) > _fs.sectors_per_cluster)
count = _fs.sectors_per_cluster - Sector;
// Quick lookup for next link in the chain
if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
Cluster = file->last_fat_lookup.CurrentCluster;
// Else walk the chain
else
{
// Starting from last recorded cluster?
if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
{
i = file->last_fat_lookup.ClusterIdx;
Cluster = file->last_fat_lookup.CurrentCluster;
}
// Start searching from the beginning..
else
{
// Set start of cluster chain to initial value
i = 0;
Cluster = file->startcluster;
}
// Follow chain to find cluster to read
for ( ;i<ClusterIdx; i++)
{
uint32 nextCluster;
// Does the entry exist in the cache?
if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
{
// Scan file linked list to find next entry
nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
// Push entry into cache
fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
}
Cluster = nextCluster;
}
// Record current cluster lookup details (if valid)
if (Cluster != FAT32_LAST_CLUSTER)
{
file->last_fat_lookup.CurrentCluster = Cluster;
file->last_fat_lookup.ClusterIdx = ClusterIdx;
}
}
// If end of cluster chain then return false
if (Cluster == FAT32_LAST_CLUSTER)
return 0;
// Calculate sector address
lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector;
// Read sector of file
if (fatfs_sector_read(&_fs, lba, buffer, count))
return count;
else
return 0;
}
//-----------------------------------------------------------------------------
// External API
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// fl_init: Initialise library
//-----------------------------------------------------------------------------
void fl_init(void)
{
int i;
memset(_files, 0, sizeof(_files));
memset(&_filelib_init, 0, sizeof(_filelib_init));
memset(&_filelib_valid, 0, sizeof(_filelib_valid));
memset(&_fs, 0, sizeof(_fs));
memset(&_open_file_list, 0, sizeof(_open_file_list));
memset(&_free_file_list, 0, sizeof(_free_file_list));
fat_list_init(&_free_file_list);
fat_list_init(&_open_file_list);
// Add all file objects to free list
for (i=0;i<FATFS_MAX_OPEN_FILES;i++)
fat_list_insert_last(&_free_file_list, &_files[i].list_node);
_filelib_init = 1;
}
//-----------------------------------------------------------------------------
// fl_attach_locks:
//-----------------------------------------------------------------------------
void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
{
_fs.fl_lock = lock;
_fs.fl_unlock = unlock;
}
//-----------------------------------------------------------------------------
// fl_attach_media:
//-----------------------------------------------------------------------------
int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
{
int res;
// If first call to library, initialise
CHECK_FL_INIT();
_fs.disk_io.read_media = rd;
_fs.disk_io.write_media = wr;
// Initialise FAT parameters
if ((res = fatfs_init(&_fs)) != FAT_INIT_OK)
{
FAT_PRINTF(("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
return res;
}
_filelib_valid = 1;
return FAT_INIT_OK;
}
//-----------------------------------------------------------------------------
// fl_shutdown: Call before shutting down system
//-----------------------------------------------------------------------------
void fl_shutdown(void)
{
// If first call to library, initialise
CHECK_FL_INIT();
FL_LOCK(&_fs);
fatfs_fat_purge(&_fs);
FL_UNLOCK(&_fs);
}
//-----------------------------------------------------------------------------
// fopen: Open or Create a file for reading or writing
//-----------------------------------------------------------------------------
void* fl_fopen(const char *path, const char *mode)
{
int i;
FL_FILE* file;
uint8 flags = 0;
// If first call to library, initialise
CHECK_FL_INIT();
if (!_filelib_valid)
return NULL;
if (!path || !mode)
return NULL;
// Supported Modes:
// "r" Open a file for reading.
// The file must exist.
// "w" Create an empty file for writing.
// If a file with the same name already exists its content is erased and the file is treated as a new empty file.
// "a" Append to a file.
// Writing operations append data at the end of the file.
// The file is created if it does not exist.
// "r+" Open a file for update both reading and writing.
// The file must exist.
// "w+" Create an empty file for both reading and writing.
// If a file with the same name already exists its content is erased and the file is treated as a new empty file.
// "a+" Open a file for reading and appending.
// All writing operations are performed at the end of the file, protecting the previous content to be overwritten.
// You can reposition (fseek, rewind) the internal pointer to anywhere in the file for reading, but writing operations
// will move it back to the end of file.
// The file is created if it does not exist.
for (i=0;i<(int)strlen(mode);i++)
{
switch (mode[i])
{
case 'r':
case 'R':
flags |= FILE_READ;
break;
case 'w':
case 'W':
flags |= FILE_WRITE;
flags |= FILE_ERASE;
flags |= FILE_CREATE;
break;
case 'a':
case 'A':
flags |= FILE_WRITE;
flags |= FILE_APPEND;
flags |= FILE_CREATE;
break;
case '+':
if (flags & FILE_READ)
flags |= FILE_WRITE;
else if (flags & FILE_WRITE)
{
flags |= FILE_READ;
flags |= FILE_ERASE;
flags |= FILE_CREATE;
}
else if (flags & FILE_APPEND)
{
flags |= FILE_READ;
flags |= FILE_WRITE;
flags |= FILE_APPEND;
flags |= FILE_CREATE;
}
break;
case 'b':
case 'B':
flags |= FILE_BINARY;
break;
}
}
file = NULL;
#if FATFS_INC_WRITE_SUPPORT == 0
// No write support!
flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
#endif
// No write access - remove write/modify flags
if (!_fs.disk_io.write_media)
flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
FL_LOCK(&_fs);
// Read
if (flags & FILE_READ)
file = _open_file(path);
// Create New
#if FATFS_INC_WRITE_SUPPORT
if (!file && (flags & FILE_CREATE))
file = _create_file(path);
#endif
// Write Existing (and not open due to read or create)
if (!(flags & FILE_READ))
if ((flags & FILE_CREATE) && !file)
if (flags & (FILE_WRITE | FILE_APPEND))
file = _open_file(path);
if (file)
file->flags = flags;
FL_UNLOCK(&_fs);
return file;
}
//-----------------------------------------------------------------------------
// _write_sectors: Write sector(s) to disk
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
static uint32 _write_sectors(FL_FILE* file, uint32 offset, uint8 *buf, uint32 count)
{
uint32 SectorNumber = 0;
uint32 ClusterIdx = 0;
uint32 Cluster = 0;
uint32 LastCluster = FAT32_LAST_CLUSTER;
uint32 i;
uint32 lba;
uint32 TotalWriteCount = count;
// Find values for Cluster index & sector within cluster
ClusterIdx = offset / _fs.sectors_per_cluster;
SectorNumber = offset - (ClusterIdx * _fs.sectors_per_cluster);
// Limit number of sectors written to the number remaining in this cluster
if ((SectorNumber + count) > _fs.sectors_per_cluster)
count = _fs.sectors_per_cluster - SectorNumber;
// Quick lookup for next link in the chain
if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
Cluster = file->last_fat_lookup.CurrentCluster;
// Else walk the chain
else
{
// Starting from last recorded cluster?
if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
{
i = file->last_fat_lookup.ClusterIdx;
Cluster = file->last_fat_lookup.CurrentCluster;
}
// Start searching from the beginning..
else
{
// Set start of cluster chain to initial value
i = 0;
Cluster = file->startcluster;
}
// Follow chain to find cluster to read
for ( ;i<ClusterIdx; i++)
{
uint32 nextCluster;
// Does the entry exist in the cache?
if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
{
// Scan file linked list to find next entry
nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
// Push entry into cache
fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
}
LastCluster = Cluster;
Cluster = nextCluster;
// Dont keep following a dead end
if (Cluster == FAT32_LAST_CLUSTER)
break;
}
// If we have reached the end of the chain, allocate more!
if (Cluster == FAT32_LAST_CLUSTER)
{
// Add some more cluster(s) to the last good cluster chain
if (!fatfs_add_free_space(&_fs, &LastCluster, (TotalWriteCount + _fs.sectors_per_cluster -1) / _fs.sectors_per_cluster))
return 0;
Cluster = LastCluster;
}
// Record current cluster lookup details
file->last_fat_lookup.CurrentCluster = Cluster;
file->last_fat_lookup.ClusterIdx = ClusterIdx;
}
// Calculate write address
lba = fatfs_lba_of_cluster(&_fs, Cluster) + SectorNumber;
if (fatfs_sector_write(&_fs, lba, buf, count))
return count;
else
return 0;
}
#endif
//-----------------------------------------------------------------------------
// fl_fflush: Flush un-written data to the file
//-----------------------------------------------------------------------------
int fl_fflush(void *f)
{
#if FATFS_INC_WRITE_SUPPORT
FL_FILE *file = (FL_FILE *)f;
// If first call to library, initialise
CHECK_FL_INIT();
if (file)
{
FL_LOCK(&_fs);
// If some write data still in buffer
if (file->file_data_dirty)
{
// Write back current sector before loading next
if (_write_sectors(file, file->file_data_address, file->file_data_sector, 1))
file->file_data_dirty = 0;
}
FL_UNLOCK(&_fs);
}
#endif
return 0;
}
//-----------------------------------------------------------------------------
// fl_fclose: Close an open file
//-----------------------------------------------------------------------------
void fl_fclose(void *f)
{
FL_FILE *file = (FL_FILE *)f;
// If first call to library, initialise
CHECK_FL_INIT();
if (file)
{
FL_LOCK(&_fs);
// Flush un-written data to file
fl_fflush(f);
// File size changed?
if (file->filelength_changed)
{
#if FATFS_INC_WRITE_SUPPORT
// Update filesize in directory
fatfs_update_file_length(&_fs, file->parentcluster, (char*)file->shortfilename, file->filelength);
#endif
file->filelength_changed = 0;
}
file->bytenum = 0;
file->filelength = 0;
file->startcluster = 0;
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
file->filelength_changed = 0;
// Free file handle
_free_file(file);
fatfs_fat_purge(&_fs);
FL_UNLOCK(&_fs);
}
}
//-----------------------------------------------------------------------------
// fl_fgetc: Get a character in the stream
//-----------------------------------------------------------------------------
int fl_fgetc(void *f)
{
int res;
uint8 data = 0;
res = fl_fread(&data, 1, 1, f);
if (res == 1)
return (int)data;
else
return res;
}
//-----------------------------------------------------------------------------
// fl_fgets: Get a string from a stream
//-----------------------------------------------------------------------------
char *fl_fgets(char *s, int n, void *f)
{
int idx = 0;
// Space for null terminator?
if (n > 0)
{
// While space (+space for null terminator)
while (idx < (n-1))
{
int ch = fl_fgetc(f);
// EOF / Error?
if (ch < 0)
break;
// Store character read from stream
s[idx++] = (char)ch;
// End of line?
if (ch == '\n')
break;
}
if (idx > 0)
s[idx] = '\0';
}
return (idx > 0) ? s : 0;
}
//-----------------------------------------------------------------------------
// fl_fread: Read a block of data from the file
//-----------------------------------------------------------------------------
int fl_fread(void * buffer, int size, int length, void *f )
{
uint32 sector;
uint32 offset;
int copyCount;
int count = size * length;
int bytesRead = 0;
FL_FILE *file = (FL_FILE *)f;
// If first call to library, initialise
CHECK_FL_INIT();
if (buffer==NULL || file==NULL)
return -1;
// No read permissions
if (!(file->flags & FILE_READ))
return -1;
// Nothing to be done
if (!count)
return 0;
// Check if read starts past end of file
if (file->bytenum >= file->filelength)
return -1;
// Limit to file size
if ( (file->bytenum + count) > file->filelength )
count = file->filelength - file->bytenum;
// Calculate start sector
sector = file->bytenum / FAT_SECTOR_SIZE;
// Offset to start copying data from first sector
offset = file->bytenum % FAT_SECTOR_SIZE;
while (bytesRead < count)
{
// Read whole sector, read from media directly into target buffer
if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE))
{
// Read as many sectors as possible into target buffer
uint32 sectorsRead = _read_sectors(file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE);
if (sectorsRead)
{
// We have upto one sector to copy
copyCount = FAT_SECTOR_SIZE * sectorsRead;
// Move onto next sector and reset copy offset
sector+= sectorsRead;
offset = 0;
}
else
break;
}
else
{
// Do we need to re-read the sector?
if (file->file_data_address != sector)
{
// Flush un-written data to file
if (file->file_data_dirty)
fl_fflush(file);
// Get LBA of sector offset within file
if (!_read_sectors(file, sector, file->file_data_sector, 1))
// Read failed - out of range (probably)
break;
file->file_data_address = sector;
file->file_data_dirty = 0;
}
// We have upto one sector to copy
copyCount = FAT_SECTOR_SIZE - offset;
// Only require some of this sector?
if (copyCount > (count - bytesRead))
copyCount = (count - bytesRead);
// Copy to application buffer
memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount);
// Move onto next sector and reset copy offset
sector++;
offset = 0;
}
// Increase total read count
bytesRead += copyCount;
// Increment file pointer
file->bytenum += copyCount;
}
return bytesRead;
}
//-----------------------------------------------------------------------------
// fl_fseek: Seek to a specific place in the file
//-----------------------------------------------------------------------------
int fl_fseek( void *f, long offset, int origin )
{
FL_FILE *file = (FL_FILE *)f;
int res = -1;
// If first call to library, initialise
CHECK_FL_INIT();
if (!file)
return -1;
if (origin == SEEK_END && offset != 0)
return -1;
FL_LOCK(&_fs);
// Invalidate file buffer
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
if (origin == SEEK_SET)
{
file->bytenum = (uint32)offset;
if (file->bytenum > file->filelength)
file->bytenum = file->filelength;
res = 0;
}
else if (origin == SEEK_CUR)
{
// Positive shift
if (offset >= 0)
{
file->bytenum += offset;
if (file->bytenum > file->filelength)
file->bytenum = file->filelength;
}
// Negative shift
else
{
// Make shift positive
offset = -offset;
// Limit to negative shift to start of file
if ((uint32)offset > file->bytenum)
file->bytenum = 0;
else
file->bytenum-= offset;
}
res = 0;
}
else if (origin == SEEK_END)
{
file->bytenum = file->filelength;
res = 0;
}
else
res = -1;
FL_UNLOCK(&_fs);
return res;
}
//-----------------------------------------------------------------------------
// fl_fgetpos: Get the current file position
//-----------------------------------------------------------------------------
int fl_fgetpos(void *f , uint32 * position)
{
FL_FILE *file = (FL_FILE *)f;
if (!file)
return -1;
FL_LOCK(&_fs);
// Get position
*position = file->bytenum;
FL_UNLOCK(&_fs);
return 0;
}
//-----------------------------------------------------------------------------
// fl_ftell: Get the current file position
//-----------------------------------------------------------------------------
long fl_ftell(void *f)
{
uint32 pos = 0;
fl_fgetpos(f, &pos);
return (long)pos;
}
//-----------------------------------------------------------------------------
// fl_feof: Is the file pointer at the end of the stream?
//-----------------------------------------------------------------------------
int fl_feof(void *f)
{
FL_FILE *file = (FL_FILE *)f;
int res;
if (!file)
return -1;
FL_LOCK(&_fs);
if (file->bytenum == file->filelength)
res = EOF;
else
res = 0;
FL_UNLOCK(&_fs);
return res;
}
//-----------------------------------------------------------------------------
// fl_fputc: Write a character to the stream
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fl_fputc(int c, void *f)
{
uint8 data = (uint8)c;
int res;
res = fl_fwrite(&data, 1, 1, f);
if (res == 1)
return c;
else
return res;
}
#endif
//-----------------------------------------------------------------------------
// fl_fwrite: Write a block of data to the stream
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fl_fwrite(const void * data, int size, int count, void *f )
{
FL_FILE *file = (FL_FILE *)f;
uint32 sector;
uint32 offset;
uint32 length = (size*count);
uint8 *buffer = (uint8 *)data;
uint32 bytesWritten = 0;
uint32 copyCount;
// If first call to library, initialise
CHECK_FL_INIT();
if (!file)
return -1;
FL_LOCK(&_fs);
// No write permissions
if (!(file->flags & FILE_WRITE))
{
FL_UNLOCK(&_fs);
return -1;
}
// Append writes to end of file
if (file->flags & FILE_APPEND)
file->bytenum = file->filelength;
// Else write to current position
// Calculate start sector
sector = file->bytenum / FAT_SECTOR_SIZE;
// Offset to start copying data from first sector
offset = file->bytenum % FAT_SECTOR_SIZE;
while (bytesWritten < length)
{
// Whole sector or more to be written?
if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE))
{
uint32 sectorsWrote;
// Buffered sector, flush back to disk
if (file->file_data_address != 0xFFFFFFFF)
{
// Flush un-written data to file
if (file->file_data_dirty)
fl_fflush(file);
file->file_data_address = 0xFFFFFFFF;
file->file_data_dirty = 0;
}
// Write as many sectors as possible
sectorsWrote = _write_sectors(file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE);
copyCount = FAT_SECTOR_SIZE * sectorsWrote;
// Increase total read count
bytesWritten += copyCount;
// Increment file pointer
file->bytenum += copyCount;
// Move onto next sector and reset copy offset
sector+= sectorsWrote;
offset = 0;
if (!sectorsWrote)
break;
}
else
{
// We have upto one sector to copy
copyCount = FAT_SECTOR_SIZE - offset;
// Only require some of this sector?
if (copyCount > (length - bytesWritten))
copyCount = (length - bytesWritten);
// Do we need to read a new sector?
if (file->file_data_address != sector)
{
// Flush un-written data to file
if (file->file_data_dirty)
fl_fflush(file);
// If we plan to overwrite the whole sector, we don't need to read it first!
if (copyCount != FAT_SECTOR_SIZE)
{
// NOTE: This does not have succeed; if last sector of file
// reached, no valid data will be read in, but write will
// allocate some more space for new data.
// Get LBA of sector offset within file
if (!_read_sectors(file, sector, file->file_data_sector, 1))
memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
}
file->file_data_address = sector;
file->file_data_dirty = 0;
}
// Copy from application buffer into sector buffer
memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount);
// Mark buffer as dirty
file->file_data_dirty = 1;
// Increase total read count
bytesWritten += copyCount;
// Increment file pointer
file->bytenum += copyCount;
// Move onto next sector and reset copy offset
sector++;
offset = 0;
}
}
// Write increased extent of the file?
if (file->bytenum > file->filelength)
{
// Increase file size to new point
file->filelength = file->bytenum;
// We are changing the file length and this
// will need to be writen back at some point
file->filelength_changed = 1;
}
#if FATFS_INC_TIME_DATE_SUPPORT
// If time & date support is enabled, always force directory entry to be
// written in-order to update file modify / access time & date.
file->filelength_changed = 1;
#endif
FL_UNLOCK(&_fs);
return (size*count);
}
#endif
//-----------------------------------------------------------------------------
// fl_fputs: Write a character string to the stream
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fl_fputs(const char * str, void *f)
{
int len = (int)strlen(str);
int res = fl_fwrite(str, 1, len, f);
if (res == len)
return len;
else
return res;
}
#endif
//-----------------------------------------------------------------------------
// fl_remove: Remove a file from the filesystem
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fl_remove( const char * filename )
{
FL_FILE* file;
int res = -1;
FL_LOCK(&_fs);
// Use read_file as this will check if the file is already open!
file = fl_fopen((char*)filename, "r");
if (file)
{
// Delete allocated space
if (fatfs_free_cluster_chain(&_fs, file->startcluster))
{
// Remove directory entries
if (fatfs_mark_file_deleted(&_fs, file->parentcluster, (char*)file->shortfilename))
{
// Close the file handle (this should not write anything to the file
// as we have not changed the file since opening it!)
fl_fclose(file);
res = 0;
}
}
}
FL_UNLOCK(&_fs);
return res;
}
#endif
//-----------------------------------------------------------------------------
// fl_createdirectory: Create a directory based on a path
//-----------------------------------------------------------------------------
#if FATFS_INC_WRITE_SUPPORT
int fl_createdirectory(const char *path)
{
int res;
// If first call to library, initialise
CHECK_FL_INIT();
FL_LOCK(&_fs);
res =_create_directory((char*)path);
FL_UNLOCK(&_fs);
return res;
}
#endif
//-----------------------------------------------------------------------------
// fl_listdirectory: List a directory based on a path
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
void fl_listdirectory(const char *path)
{
FL_DIR dirstat;
// If first call to library, initialise
CHECK_FL_INIT();
FL_LOCK(&_fs);
FAT_PRINTF(("\r\nDirectory %s\r\n", path));
if (fl_opendir(path, &dirstat))
{
struct fs_dir_ent dirent;
while (fl_readdir(&dirstat, &dirent) == 0)
{
#if FATFS_INC_TIME_DATE_SUPPORT
int d,m,y,h,mn,s;
fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s);
fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y);
FAT_PRINTF(("%02d/%02d/%04d %02d:%02d ", d,mn,y,h,m));
#endif
if (dirent.is_dir)
{
FAT_PRINTF(("%s <DIR>\r\n", dirent.filename));
}
else
{
FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size));
}
}
fl_closedir(&dirstat);
}
FL_UNLOCK(&_fs);
}
#endif
//-----------------------------------------------------------------------------
// fl_opendir: Opens a directory for listing
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
FL_DIR* fl_opendir(const char* path, FL_DIR *dir)
{
int levels;
int res = 1;
uint32 cluster = FAT32_INVALID_CLUSTER;
// If first call to library, initialise
CHECK_FL_INIT();
FL_LOCK(&_fs);
levels = fatfs_total_path_levels((char*)path) + 1;
// If path is in the root dir
if (levels == 0)
cluster = fatfs_get_root_cluster(&_fs);
// Find parent directory start cluster
else
res = _open_directory((char*)path, &cluster);
if (res)
fatfs_list_directory_start(&_fs, dir, cluster);
FL_UNLOCK(&_fs);
return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
}
#endif
//-----------------------------------------------------------------------------
// fl_readdir: Get next item in directory
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
int fl_readdir(FL_DIR *dirls, fl_dirent *entry)
{
int res = 0;
// If first call to library, initialise
CHECK_FL_INIT();
FL_LOCK(&_fs);
res = fatfs_list_directory_next(&_fs, dirls, entry);
FL_UNLOCK(&_fs);
return res ? 0 : -1;
}
#endif
//-----------------------------------------------------------------------------
// fl_closedir: Close directory after listing
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
int fl_closedir(FL_DIR* dir)
{
// Not used
return 0;
}
#endif
//-----------------------------------------------------------------------------
// fl_is_dir: Is this a directory?
//-----------------------------------------------------------------------------
#if FATFS_DIR_LIST_SUPPORT
int fl_is_dir(const char *path)
{
int res = 0;
FL_DIR dir;
if (fl_opendir(path, &dir))
{
res = 1;
fl_closedir(&dir);
}
return res;
}
#endif
//-----------------------------------------------------------------------------
// fl_format: Format a partition with either FAT16 or FAT32 based on size
//-----------------------------------------------------------------------------
#if FATFS_INC_FORMAT_SUPPORT
int fl_format(uint32 volume_sectors, const char *name)
{
return fatfs_format(&_fs, volume_sectors, name);
}
#endif /*FATFS_INC_FORMAT_SUPPORT*/
//-----------------------------------------------------------------------------
// fl_get_fs:
//-----------------------------------------------------------------------------
#ifdef FATFS_INC_TEST_HOOKS
struct fatfs* fl_get_fs(void)
{
return &_fs;
}
#endif