/** * @public * @brief Opens a File for Access * * @param pIoman FF_IOMAN object that was created by FF_CreateIOMAN(). * @param path Path to the File or object. * @param Mode Access Mode required. Modes are a little complicated, the function FF_GetModeBits() * @param Mode will convert a stdio Mode string into the equivalent Mode bits for this parameter. * @param pError Pointer to a signed byte for error checking. Can be NULL if not required. * @param pError To be checked when a NULL pointer is returned. * * @return NULL pointer on Error, in which case pError should be checked for more information. * @return pError can be: **/ FF_FILE *FF_Open(FF_IOMAN *pIoman, const FF_T_INT8 *path, FF_T_UINT8 Mode, FF_ERROR *pError) { FF_FILE *pFile; FF_FILE *pFileChain; FF_DIRENT Object; FF_T_UINT32 DirCluster, FileCluster; FF_T_UINT32 nBytesPerCluster; FF_T_INT8 filename[FF_MAX_FILENAME]; FF_T_UINT16 i; if(pError) { *pError = 0; } if(!pIoman) { if(pError) { *pError = FF_ERR_NULL_POINTER; } return (FF_FILE *)NULL; } pFile = FF_MALLOC(sizeof(FF_FILE)); if(!pFile) { if(pError) { *pError = FF_ERR_NOT_ENOUGH_MEMORY; } return (FF_FILE *)NULL; } // Get the Mode Bits. pFile->Mode = Mode; i = (FF_T_UINT16) strlen(path); while(i != 0) { if(path[i] == '\\' || path[i] == '/') { break; } i--; } strncpy(filename, (path + i + 1), FF_MAX_FILENAME); if(i == 0) { i = 1; } DirCluster = FF_FindDir(pIoman, path, i); if(DirCluster) { //RetVal = //FF_FindEntry(pIoman, DirCluster, filename, &Object, FF_TRUE); //if(RetVal >= 0) { //FileCluster = Object.ObjectCluster;//FF_FindEntryInDir(pIoman, DirCluster, filename, 0x00, &Object); //} else { // FileCluster = 0; //} FileCluster = FF_FindEntryInDir(pIoman, DirCluster, filename, 0x00, &Object); if(!FileCluster) { // If 0 was returned, it might be because the file has no allocated cluster if(strlen(filename) == strlen(Object.FileName)) { if(Object.Filesize == 0 && FF_strmatch(filename, Object.FileName, (FF_T_UINT16) strlen(filename)) == FF_TRUE) { // The file really was found! FileCluster = 1; } } } if(!FileCluster) { if((pFile->Mode & FF_MODE_CREATE)) { FileCluster = FF_CreateFile(pIoman, DirCluster, filename, &Object); Object.CurrentItem += 1; } } if(FileCluster) { if(Object.Attrib == FF_FAT_ATTR_DIR) { if(!(pFile->Mode & FF_MODE_DIR)) { // Not the object, File Not Found! FF_FREE(pFile); if(pError) { *pError = FF_ERR_FILE_OBJECT_IS_A_DIR; } return (FF_FILE *) NULL; } } //---------- Ensure Read-Only files don't get opened for Writing. if((pFile->Mode & FF_MODE_WRITE) || (pFile->Mode & FF_MODE_APPEND)) { if((Object.Attrib & FF_FAT_ATTR_READONLY)) { FF_FREE(pFile); if(pError) { *pError = FF_ERR_FILE_IS_READ_ONLY; } return (FF_FILE *) NULL; } } pFile->pIoman = pIoman; pFile->FilePointer = 0; pFile->ObjectCluster = Object.ObjectCluster; pFile->Filesize = Object.Filesize; pFile->CurrentCluster = 0; pFile->AddrCurrentCluster = pFile->ObjectCluster; //pFile->Mode = Mode; pFile->Next = NULL; pFile->DirCluster = DirCluster; pFile->DirEntry = Object.CurrentItem - 1; nBytesPerCluster = pFile->pIoman->pPartition->SectorsPerCluster / pIoman->BlkSize; pFile->iChainLength = 0; pFile->iEndOfChain = 0; pFile->FileDeleted = FF_FALSE; // File Permission Processing // Only "w" and "w+" mode strings can erase a file's contents. // Any other combinations will not cause an erase. if((pFile->Mode & FF_MODE_TRUNCATE)) { pFile->Filesize = 0; pFile->FilePointer = 0; } /* Add pFile onto the end of our linked list of FF_FILE objects. */ FF_PendSemaphore(pIoman->pSemaphore); { if(!pIoman->FirstFile) { pIoman->FirstFile = pFile; } else { pFileChain = (FF_FILE *) pIoman->FirstFile; do { if(pFileChain->ObjectCluster == pFile->ObjectCluster) { // File is already open! DON'T ALLOW IT! FF_ReleaseSemaphore(pIoman->pSemaphore); FF_FREE(pFile); if(pError) { *pError = FF_ERR_FILE_ALREADY_OPEN; } return (FF_FILE *) NULL; } if(!pFileChain->Next) { pFileChain->Next = pFile; break; } pFileChain = (FF_FILE *) pFileChain->Next; }while(pFileChain != NULL); } } FF_ReleaseSemaphore(pIoman->pSemaphore); return pFile; }else { FF_FREE(pFile); if(pError) { *pError = FF_ERR_FILE_NOT_FOUND; } return (FF_FILE *) NULL; } } if(pError) { *pError = FF_ERR_FILE_INVALID_PATH; } FF_FREE(pFile); return (FF_FILE *)NULL; }
/** * @public * @brief Creates an FF_IOMAN object, to initialise FullFAT * * @param pCacheMem Pointer to a buffer for the cache. (NULL if ok to Malloc). * @param Size The size of the provided buffer, or size of the cache to be created. (Must be atleast 2 * BlkSize). Always a multiple of BlkSize. * @param BlkSize The block size of devices to be attached. If in doubt use 512. * @param pError Pointer to a signed byte for error checking. Can be NULL if not required. * @param pError To be checked when a NULL pointer is returned. * * @return Returns a pointer to an FF_IOMAN type object. NULL on Error, check the contents of * @return pError **/ FF_IOMAN *FF_CreateIOMAN(FF_T_UINT8 *pCacheMem, FF_T_UINT32 Size, FF_T_UINT16 BlkSize, FF_ERROR *pError) { FF_IOMAN *pIoman = NULL; FF_T_UINT32 *pLong = NULL; // Force malloc to malloc memory on a 32-bit boundary. #ifdef FF_PATH_CACHE FF_T_UINT32 i; #endif if(pError) { *pError = FF_ERR_NONE; } if((BlkSize % 512) != 0 || BlkSize == 0) { if(pError) { *pError = FF_ERR_IOMAN_BAD_BLKSIZE | FF_CREATEIOMAN; } return NULL; // BlkSize Size not a multiple of 512 > 0 } if((Size % BlkSize) != 0 || Size == 0 || Size == BlkSize) { // Size must now be atleast 2 * BlkSize (or a deadlock will occur). if(pError) { *pError = FF_ERR_IOMAN_BAD_MEMSIZE | FF_CREATEIOMAN; } return NULL; // Memory Size not a multiple of BlkSize > 0 } pIoman = (FF_IOMAN *) FF_MALLOC(sizeof(FF_IOMAN)); if(!pIoman) { // Ensure malloc() succeeded. if(pError) { *pError = FF_ERR_NOT_ENOUGH_MEMORY | FF_CREATEIOMAN; } return NULL; } memset (pIoman, '\0', sizeof(FF_IOMAN)); // This is just a bit-mask, to use a byte to keep track of memory. // pIoman->MemAllocation = 0x00; // Unset all allocation identifiers. pIoman->pPartition = (FF_PARTITION *) FF_MALLOC(sizeof(FF_PARTITION)); if(!pIoman->pPartition) { if(pError) { *pError = FF_ERR_NOT_ENOUGH_MEMORY | FF_CREATEIOMAN; } FF_DestroyIOMAN(pIoman); return NULL; } memset (pIoman->pPartition, '\0', sizeof(FF_PARTITION)); pIoman->MemAllocation |= FF_IOMAN_ALLOC_PART; // If succeeded, flag that allocation. pIoman->pPartition->LastFreeCluster = 0; pIoman->pPartition->PartitionMounted = FF_FALSE; // This should be checked by FF_Open(); #ifdef FF_PATH_CACHE pIoman->pPartition->PCIndex = 0; for(i = 0; i < FF_PATH_CACHE_DEPTH; i++) { pIoman->pPartition->PathCache[i].DirCluster = 0; pIoman->pPartition->PathCache[i].Path[0] = '\0'; /*#ifdef FF_HASH_TABLE_SUPPORT pIoman->pPartition->PathCache[i].pHashTable = FF_CreateHashTable(); pIoman->pPartition->PathCache[i].bHashed = FF_FALSE; #endif*/ } #endif #ifdef FF_HASH_CACHE for(i = 0; i < FF_HASH_CACHE_DEPTH; i++) { pIoman->HashCache[i].pHashTable = FF_CreateHashTable(); pIoman->HashCache[i].ulDirCluster = 0; pIoman->HashCache[i].ulMisses = 100; } #endif pIoman->pBlkDevice = (FF_BLK_DEVICE *) FF_MALLOC(sizeof(FF_BLK_DEVICE)); if(!pIoman->pBlkDevice) { // If succeeded, flag that allocation. if(pError) { *pError = FF_ERR_NOT_ENOUGH_MEMORY | FF_CREATEIOMAN; } FF_DestroyIOMAN(pIoman); return NULL; } memset (pIoman->pBlkDevice, '\0', sizeof(FF_BLK_DEVICE)); pIoman->MemAllocation |= FF_IOMAN_ALLOC_BLKDEV; // Make sure all pointers are NULL pIoman->pBlkDevice->fnpReadBlocks = NULL; pIoman->pBlkDevice->fnpWriteBlocks = NULL; pIoman->pBlkDevice->pParam = NULL; // Organise the memory provided, or create our own! if(pCacheMem) { pIoman->pCacheMem = pCacheMem; }else { // No-Cache buffer provided (malloc) pLong = (FF_T_UINT32 *) FF_MALLOC(Size); pIoman->pCacheMem = (FF_T_UINT8 *) pLong; if(!pIoman->pCacheMem) { if(pError) { *pError = FF_ERR_NOT_ENOUGH_MEMORY | FF_CREATEIOMAN; } FF_DestroyIOMAN(pIoman); return NULL; } pIoman->MemAllocation |= FF_IOMAN_ALLOC_BUFFERS; } memset (pIoman->pCacheMem, '\0', Size); pIoman->BlkSize = BlkSize; pIoman->CacheSize = (FF_T_UINT16) (Size / BlkSize); pIoman->FirstFile = NULL; pIoman->Locks = 0; /* Malloc() memory for buffer objects. (FullFAT never refers to a buffer directly but uses buffer objects instead. Allows us to provide thread safety. */ pIoman->pBuffers = (FF_BUFFER *) FF_MALLOC(sizeof(FF_BUFFER) * pIoman->CacheSize); if(!pIoman->pBuffers) { if(pError) { *pError = FF_ERR_NOT_ENOUGH_MEMORY | FF_CREATEIOMAN; } FF_DestroyIOMAN(pIoman); return NULL; // HT added } memset (pIoman->pBuffers, '\0', sizeof(FF_BUFFER) * pIoman->CacheSize); pIoman->MemAllocation |= FF_IOMAN_ALLOC_BUFDESCR; FF_IOMAN_InitBufferDescriptors(pIoman); // Finally create a Semaphore for Buffer Description modifications. pIoman->pSemaphore = FF_CreateSemaphore(); #ifdef FF_BLKDEV_USES_SEM pIoman->pBlkDevSemaphore = FF_CreateSemaphore(); #endif return pIoman; // Sucess, return the created object. }
FF_FILE *FF_Open(FF_IOMAN *pIoman, const FF_T_WCHAR *path, FF_T_UINT8 Mode, FF_ERROR *pError) { #else FF_FILE *FF_Open(FF_IOMAN *pIoman, const FF_T_INT8 *path, FF_T_UINT8 Mode, FF_ERROR *pError) { #endif FF_FILE *pFile; FF_FILE *pFileChain; FF_DIRENT Object; FF_T_UINT32 DirCluster, FileCluster; FF_T_UINT32 nBytesPerCluster; #ifdef FF_UNICODE_SUPPORT FF_T_WCHAR filename[FF_MAX_FILENAME]; #else FF_T_INT8 filename[FF_MAX_FILENAME]; #endif FF_ERROR Error; FF_T_UINT16 i; if(pError) { *pError = 0; } if(!pIoman) { if(pError) { *pError = FF_ERR_NULL_POINTER; } return (FF_FILE *)NULL; } pFile = FF_MALLOC(sizeof(FF_FILE)); if(!pFile) { if(pError) { *pError = FF_ERR_NOT_ENOUGH_MEMORY; } return (FF_FILE *)NULL; } // Get the Mode Bits. pFile->Mode = Mode; #ifdef FF_UNICODE_SUPPORT i = (FF_T_UINT16) wcslen(path); #else i = (FF_T_UINT16) strlen(path); #endif while(i != 0) { if(path[i] == '\\' || path[i] == '/') { break; } i--; } #ifdef FF_UNICODE_SUPPORT wcsncpy(filename, (path + i + 1), FF_MAX_FILENAME); #else strncpy(filename, (path + i + 1), FF_MAX_FILENAME); #endif if(i == 0) { i = 1; } DirCluster = FF_FindDir(pIoman, path, i, &Error); if(Error) { if(pError) { *pError = Error; } FF_FREE(pFile); return (FF_FILE *) NULL; } if(DirCluster) { FileCluster = FF_FindEntryInDir(pIoman, DirCluster, filename, 0x00, &Object, &Error); if(Error) { if(pError) { *pError = Error; } FF_FREE(pFile); return (FF_FILE *) NULL; } if(!FileCluster) { // If 0 was returned, it might be because the file has no allocated cluster #ifdef FF_UNICODE_SUPPORT if(wcslen(filename) == wcslen(Object.FileName)) { if(Object.Filesize == 0 && FF_strmatch(filename, Object.FileName, (FF_T_UINT16) wcslen(filename)) == FF_TRUE) { #else if(strlen(filename) == strlen(Object.FileName)) { if(Object.Filesize == 0 && FF_strmatch(filename, Object.FileName, (FF_T_UINT16) strlen(filename)) == FF_TRUE) { #endif // The file really was found! FileCluster = 1; } } } if(!FileCluster) { if((pFile->Mode & FF_MODE_CREATE)) { FileCluster = FF_CreateFile(pIoman, DirCluster, filename, &Object, &Error); if(Error) { if(pError) { *pError = Error; } FF_FREE(pFile); return (FF_FILE *) NULL; } Object.CurrentItem += 1; } } if(FileCluster) { if(Object.Attrib == FF_FAT_ATTR_DIR) { if(!(pFile->Mode & FF_MODE_DIR)) { // Not the object, File Not Found! FF_FREE(pFile); if(pError) { *pError = FF_ERR_FILE_OBJECT_IS_A_DIR; } return (FF_FILE *) NULL; } } //---------- Ensure Read-Only files don't get opened for Writing. if((pFile->Mode & FF_MODE_WRITE) || (pFile->Mode & FF_MODE_APPEND)) { if((Object.Attrib & FF_FAT_ATTR_READONLY)) { FF_FREE(pFile); if(pError) { *pError = FF_ERR_FILE_IS_READ_ONLY; } return (FF_FILE *) NULL; } } pFile->pIoman = pIoman; pFile->FilePointer = 0; pFile->ObjectCluster = Object.ObjectCluster; pFile->Filesize = Object.Filesize; pFile->CurrentCluster = 0; pFile->AddrCurrentCluster = pFile->ObjectCluster; //pFile->Mode = Mode; pFile->Next = NULL; pFile->DirCluster = DirCluster; pFile->DirEntry = Object.CurrentItem - 1; nBytesPerCluster = pFile->pIoman->pPartition->SectorsPerCluster / pIoman->BlkSize; pFile->iChainLength = 0; pFile->iEndOfChain = 0; pFile->FileDeleted = FF_FALSE; // File Permission Processing // Only "w" and "w+" mode strings can erase a file's contents. // Any other combinations will not cause an erase. if((pFile->Mode & FF_MODE_TRUNCATE)) { pFile->Filesize = 0; pFile->FilePointer = 0; } /* Add pFile onto the end of our linked list of FF_FILE objects. */ FF_PendSemaphore(pIoman->pSemaphore); { if(!pIoman->FirstFile) { pIoman->FirstFile = pFile; } else { pFileChain = (FF_FILE *) pIoman->FirstFile; do { if(pFileChain->ObjectCluster == pFile->ObjectCluster) { // File is already open! DON'T ALLOW IT! FF_ReleaseSemaphore(pIoman->pSemaphore); FF_FREE(pFile); if(pError) { *pError = FF_ERR_FILE_ALREADY_OPEN; } return (FF_FILE *) NULL; } if(!pFileChain->Next) { pFileChain->Next = pFile; break; } pFileChain = (FF_FILE *) pFileChain->Next; }while(pFileChain != NULL); } } FF_ReleaseSemaphore(pIoman->pSemaphore); return pFile; }else { FF_FREE(pFile); if(pError) { *pError = FF_ERR_FILE_NOT_FOUND; } return (FF_FILE *) NULL; } } if(pError) { *pError = FF_ERR_FILE_INVALID_PATH; } FF_FREE(pFile); return (FF_FILE *)NULL; } /** * @public * @brief Tests if a Directory contains any other files or folders. * * @param pIoman FF_IOMAN object returned from the FF_CreateIOMAN() function. * **/ #ifdef FF_UNICODE_SUPPORT FF_T_BOOL FF_isDirEmpty(FF_IOMAN *pIoman, const FF_T_WCHAR *Path) { #else FF_T_BOOL FF_isDirEmpty(FF_IOMAN *pIoman, const FF_T_INT8 *Path) { #endif FF_DIRENT MyDir; FF_ERROR RetVal = FF_ERR_NONE; FF_T_UINT8 i = 0; if(!pIoman) { return FF_FALSE; } RetVal = FF_FindFirst(pIoman, &MyDir, Path); while(RetVal == 0) { i++; RetVal = FF_FindNext(pIoman, &MyDir); if(i > 2) { return FF_FALSE; } } return FF_TRUE; } #ifdef FF_UNICODE_SUPPORT FF_ERROR FF_RmDir(FF_IOMAN *pIoman, const FF_T_WCHAR *path) { #else FF_ERROR FF_RmDir(FF_IOMAN *pIoman, const FF_T_INT8 *path) { #endif FF_FILE *pFile; FF_ERROR Error = FF_ERR_NONE; FF_T_UINT8 EntryBuffer[32]; FF_FETCH_CONTEXT FetchContext; FF_T_SINT8 RetVal = FF_ERR_NONE; #ifdef FF_PATH_CACHE FF_T_UINT32 i; #endif if(!pIoman) { return FF_ERR_NULL_POINTER; } pFile = FF_Open(pIoman, path, FF_MODE_DIR, &Error); if(!pFile) { return Error; // File in use or File not found! } pFile->FileDeleted = FF_TRUE; FF_lockDIR(pIoman); { if(FF_isDirEmpty(pIoman, path)) { FF_lockFAT(pIoman); { Error = FF_UnlinkClusterChain(pIoman, pFile->ObjectCluster, 0); // 0 to delete the entire chain! } FF_unlockFAT(pIoman); if(Error) { FF_unlockDIR(pIoman); FF_Close(pFile); return Error; } // Initialise the dirent Fetch Context object for faster removal of dirents. Error = FF_InitEntryFetch(pIoman, pFile->DirCluster, &FetchContext); if(Error) { FF_unlockDIR(pIoman); FF_Close(pFile); return Error; } // Edit the Directory Entry! (So it appears as deleted); Error = FF_RmLFNs(pIoman, pFile->DirEntry, &FetchContext); if(Error) { FF_CleanupEntryFetch(pIoman, &FetchContext); FF_unlockDIR(pIoman); FF_Close(pFile); return Error; } Error = FF_FetchEntryWithContext(pIoman, pFile->DirEntry, &FetchContext, EntryBuffer); if(Error) { FF_CleanupEntryFetch(pIoman, &FetchContext); FF_unlockDIR(pIoman); FF_Close(pFile); return Error; } EntryBuffer[0] = 0xE5; Error = FF_PushEntryWithContext(pIoman, pFile->DirEntry, &FetchContext, EntryBuffer); if(Error) { FF_CleanupEntryFetch(pIoman, &FetchContext); FF_unlockDIR(pIoman); FF_Close(pFile); return Error; } #ifdef FF_PATH_CACHE FF_PendSemaphore(pIoman->pSemaphore); // Thread safety on shared object! { for(i = 0; i < FF_PATH_CACHE_DEPTH; i++) { #ifdef FF_UNICODE_SUPPORT if(FF_strmatch(pIoman->pPartition->PathCache[i].Path, path, (FF_T_UINT16)wcslen(path))) { #else if(FF_strmatch(pIoman->pPartition->PathCache[i].Path, path, (FF_T_UINT16)strlen(path))) { #endif pIoman->pPartition->PathCache[i].Path[0] = '\0'; pIoman->pPartition->PathCache[i].DirCluster = 0; FF_ReleaseSemaphore(pIoman->pSemaphore); } } } FF_ReleaseSemaphore(pIoman->pSemaphore); #endif Error = FF_IncreaseFreeClusters(pIoman, pFile->iChainLength); if(Error) { FF_CleanupEntryFetch(pIoman, &FetchContext); FF_unlockDIR(pIoman); FF_Close(pFile); return Error; } FF_CleanupEntryFetch(pIoman, &FetchContext); Error = FF_FlushCache(pIoman); if(Error) { FF_unlockDIR(pIoman); FF_Close(pFile); return Error; } } else { RetVal = FF_ERR_DIR_NOT_EMPTY; } } FF_unlockDIR(pIoman); Error = FF_Close(pFile); // Free the file pointer resources if(Error) { return Error; } // File is now lost! return RetVal; }