/** * @public * @brief Unmounts the active partition. * * @param pIoman FF_IOMAN Object. * * @return FF_ERR_NONE on success. **/ FF_ERROR FF_UnmountPartition(FF_IOMAN *pIoman) { FF_T_SINT8 RetVal = FF_ERR_NONE; if(!pIoman) { return FF_ERR_NULL_POINTER; } FF_PendSemaphore(pIoman->pSemaphore); // Ensure that there are no File Handles { if(!FF_ActiveHandles(pIoman)) { if(pIoman->FirstFile == NULL) { // Release Semaphore to call this function! FF_ReleaseSemaphore(pIoman->pSemaphore); FF_FlushCache(pIoman); // Flush any unwritten sectors to disk. // Reclaim Semaphore FF_PendSemaphore(pIoman->pSemaphore); pIoman->pPartition->PartitionMounted = FF_FALSE; } else { RetVal = FF_ERR_IOMAN_ACTIVE_HANDLES; } } else { RetVal = FF_ERR_IOMAN_ACTIVE_HANDLES; // Active handles found on the cache. } } FF_ReleaseSemaphore(pIoman->pSemaphore); return RetVal; }
void FF_lockFAT(FF_IOMAN *pIoman) { FF_PendSemaphore(pIoman->pSemaphore); // Use Semaphore to protect FAT modifications. { while((pIoman->Locks & FF_FAT_LOCK)) { FF_ReleaseSemaphore(pIoman->pSemaphore); FF_Yield(); // Keep Releasing and Yielding until we have the Fat protector. FF_PendSemaphore(pIoman->pSemaphore); } pIoman->Locks |= FF_FAT_LOCK; } FF_ReleaseSemaphore(pIoman->pSemaphore); }
/** * @public * @brief Unmounts the active partition. * * @param pIoman FF_IOMAN Object. * * @return FF_ERR_NONE on success. **/ FF_ERROR FF_UnmountPartition(FF_IOMAN *pIoman) { FF_ERROR RetVal = FF_ERR_NONE; #ifdef FF_MIRROR_FATS_UMOUNT FF_T_UINT i, y; FF_BUFFER *pBuffer; #endif if(!pIoman || !pIoman->pPartition) { return FF_ERR_NULL_POINTER | FF_UNMOUNTPARTITION; } if (!pIoman->pPartition->PartitionMounted) return FF_ERR_NONE; FF_PendSemaphore(pIoman->pSemaphore); // Ensure that there are no File Handles { if(!FF_ActiveHandles(pIoman)) { if(pIoman->FirstFile == NULL) { // Release Semaphore to call this function! FF_ReleaseSemaphore(pIoman->pSemaphore); RetVal = FF_FlushCache(pIoman); // Flush any unwritten sectors to disk. if(FF_isERR(RetVal)) { return RetVal; } // Reclaim Semaphore FF_PendSemaphore(pIoman->pSemaphore); pIoman->pPartition->PartitionMounted = FF_FALSE; #ifdef FF_MIRROR_FATS_UMOUNT FF_ReleaseSemaphore(pIoman->pSemaphore); for(i = 0; i < pIoman->pPartition->SectorsPerFAT; i++) { pBuffer = FF_GetBuffer(pIoman, pIoman->pPartition->FatBeginLBA + i, FF_MODE_READ); if(!pBuffer) { RetVal = FF_ERR_DEVICE_DRIVER_FAILED | FF_UNMOUNTPARTITION; break; } for(y = 0; y < pIoman->pPartition->NumFATS; y++) { FF_BlockWrite(pIoman, pIoman->pPartition->FatBeginLBA + (y*pIoman->pPartition->SectorsPerFAT) + i, 1, pBuffer->pBuffer, FF_FALSE); } } FF_PendSemaphore(pIoman->pSemaphore); #endif } else { RetVal = FF_ERR_IOMAN_ACTIVE_HANDLES | FF_UNMOUNTPARTITION; } } else { RetVal = FF_ERR_IOMAN_ACTIVE_HANDLES | FF_UNMOUNTPARTITION; // Active handles found on the cache. } } FF_ReleaseSemaphore(pIoman->pSemaphore); return RetVal; }
FF_T_SINT32 FF_BlockWrite(FF_IOMAN *pIoman, FF_T_UINT32 ulSectorLBA, FF_T_UINT32 ulNumSectors, void *pBuffer, FF_T_BOOL aSemLocked) { FF_T_SINT32 slRetVal = 0; if(pIoman->pPartition->TotalSectors) { if((ulSectorLBA + ulNumSectors) > (pIoman->pPartition->TotalSectors + pIoman->pPartition->BeginLBA)) { return (FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_BLOCKWRITE); } } if(pIoman->pBlkDevice->fnpWriteBlocks) do { // Make sure we don't execute a NULL. #ifdef FF_BLKDEV_USES_SEM if (!aSemLocked || pIoman->pSemaphore != pIoman->pBlkDevSemaphore) FF_PendSemaphore(pIoman->pBlkDevSemaphore); #endif slRetVal = pIoman->pBlkDevice->fnpWriteBlocks(pBuffer, ulSectorLBA, ulNumSectors, pIoman->pBlkDevice->pParam); #ifdef FF_BLKDEV_USES_SEM if (!aSemLocked || pIoman->pSemaphore != pIoman->pBlkDevSemaphore) FF_ReleaseSemaphore(pIoman->pBlkDevSemaphore); #endif if(!FF_isERR(slRetVal) && FF_GETERROR(slRetVal) != FF_ERR_DRIVER_BUSY) { break; } FF_Sleep(FF_DRIVER_BUSY_SLEEP); } while (FF_TRUE); return slRetVal; }
/** * @private * @brief Flushes all Write cache buffers with no active Handles. * * @param pIoman IOMAN Object. * * @return FF_ERR_NONE on Success. **/ FF_ERROR FF_FlushCache(FF_IOMAN *pIoman) { FF_T_UINT16 i,x; if(!pIoman) { return FF_ERR_NULL_POINTER | FF_FLUSHCACHE; } FF_PendSemaphore(pIoman->pSemaphore); { for(i = 0; i < pIoman->CacheSize; i++) { if((pIoman->pBuffers + i)->NumHandles == 0 && (pIoman->pBuffers + i)->Modified == FF_TRUE) { FF_BlockWrite(pIoman, (pIoman->pBuffers + i)->Sector, 1, (pIoman->pBuffers + i)->pBuffer); // Buffer has now been flushed, mark it as a read buffer and unmodified. (pIoman->pBuffers + i)->Mode = FF_MODE_READ; (pIoman->pBuffers + i)->Modified = FF_FALSE; // Search for other buffers that used this sector, and mark them as modified // So that further requests will result in the new sector being fetched. for(x = 0; x < pIoman->CacheSize; x++) { if(x != i) { if((pIoman->pBuffers + x)->Sector == (pIoman->pBuffers + i)->Sector && (pIoman->pBuffers + x)->Mode == FF_MODE_READ) { (pIoman->pBuffers + x)->Modified = FF_TRUE; } } } } } } FF_ReleaseSemaphore(pIoman->pSemaphore); return FF_ERR_NONE; }
void FF_unlockFAT(FF_IOMAN *pIoman) { FF_PendSemaphore(pIoman->pSemaphore); { pIoman->Locks &= ~FF_FAT_LOCK; } FF_ReleaseSemaphore(pIoman->pSemaphore); }
/** * @public * @brief Equivalent to fclose() * * @param pFile FF_FILE object that was created by FF_Open(). * * @return 0 on sucess. * @return -1 if a null pointer was provided. * **/ FF_ERROR FF_Close(FF_FILE *pFile) { FF_FILE *pFileChain; FF_DIRENT OriginalEntry; FF_ERROR Error; if(!pFile) { return FF_ERR_NULL_POINTER; } // UpDate Dirent if File-size has changed? // Update the Dirent! Error = FF_GetEntry(pFile->pIoman, pFile->DirEntry, pFile->DirCluster, &OriginalEntry); if(Error) { return Error; } if(!pFile->FileDeleted) { if(pFile->Filesize != OriginalEntry.Filesize) { OriginalEntry.Filesize = pFile->Filesize; Error = FF_PutEntry(pFile->pIoman, pFile->DirEntry, pFile->DirCluster, &OriginalEntry); if(Error) { return Error; } } } Error = FF_FlushCache(pFile->pIoman); // Ensure all modfied blocks are flushed to disk! // Handle Linked list! FF_PendSemaphore(pFile->pIoman->pSemaphore); { // Semaphore is required, or linked list could become corrupted. if(pFile->pIoman->FirstFile == pFile) { pFile->pIoman->FirstFile = pFile->Next; } else { pFileChain = (FF_FILE *) pFile->pIoman->FirstFile; while(pFileChain->Next != pFile) { pFileChain = pFileChain->Next; } pFileChain->Next = pFile->Next; } } // Semaphore released, linked list was shortened! FF_ReleaseSemaphore(pFile->pIoman->pSemaphore); // If file written, flush to disk FF_FREE(pFile); if(Error) { return Error; } // Simply free the pointer! return FF_ERR_NONE; }
/** * @private * @brief Releases a buffer resource. * * @param pIoman Pointer to an FF_IOMAN object. * @param pBuffer Pointer to an FF_BUFFER object. * **/ void FF_ReleaseBuffer(FF_IOMAN *pIoman, FF_BUFFER *pBuffer) { // Protect description changes with a semaphore. FF_PendSemaphore(pIoman->pSemaphore); { if (pBuffer->NumHandles) { pBuffer->NumHandles--; } else { //printf ("FF_ReleaseBuffer: buffer not claimed\n"); } } FF_ReleaseSemaphore(pIoman->pSemaphore); }
signed int fnRead(unsigned char *buffer, unsigned long sector, unsigned short sectors, BLK_DEV_LINUX pDevice) { unsigned long long address; unsigned long Read; address = (unsigned long long) sector * pDevice->BlockSize; FF_PendSemaphore(pDevice->AccessSem); { fseeko64(pDevice->pDevice, address, 0); Read = fread(buffer, pDevice->BlockSize, sectors, pDevice->pDevice); } FF_ReleaseSemaphore(pDevice->AccessSem); read_inc(); return Read / pDevice->BlockSize; }
/** * @brief Thread-Safe BlockDevice Write operation for Windows. * **/ signed int fnWrite(unsigned char *buffer, unsigned long sector, unsigned short sectors, HANDLE hDevice) { struct _DEV_INFO *ptDevInfo = (struct _DEV_INFO *) hDevice; LARGE_INTEGER address; DWORD Written; address.QuadPart = (unsigned long long) sector * ptDevInfo->BlockSize; FF_PendSemaphore(ptDevInfo->AccessSem); { SetFilePointerEx(ptDevInfo->hDev, address, NULL, FILE_BEGIN); WriteFile(ptDevInfo->hDev, buffer, ptDevInfo->BlockSize * sectors, &Written, NULL); } FF_ReleaseSemaphore(ptDevInfo->AccessSem); write_inc(); return Written / ptDevInfo->BlockSize; }
signed int fnWrite(unsigned char *buffer, unsigned long sector, unsigned short sectors, BLK_DEV_LINUX pDevice) { unsigned long long address; unsigned long Written; address = (unsigned long long) sector * pDevice->BlockSize; FF_PendSemaphore(pDevice->AccessSem); { fseeko64(pDevice->pDevice, address, 0); Written = fwrite(buffer, pDevice->BlockSize, sectors, pDevice->pDevice); } FF_ReleaseSemaphore(pDevice->AccessSem); write_inc(); return Written / pDevice->BlockSize; }
signed int fnWrite(unsigned char *buffer, unsigned long sector, unsigned short sectors, HANDLE hDevice) { struct _DEV_INFO *ptDevInfo = (struct _DEV_INFO *) hDevice; LARGE_INTEGER address; DWORD Written; //unsigned long retVal; address.QuadPart = (unsigned long long) sector * ptDevInfo->BlockSize; FF_PendSemaphore(ptDevInfo->AccessSem); { //_fseeki64(pParam, address, SEEK_SET); SetFilePointerEx(ptDevInfo->hDev, address, NULL, FILE_BEGIN); //retVal = fwrite(buffer, BLOCK_SIZE, sectors, pParam); WriteFile(ptDevInfo->hDev, buffer, ptDevInfo->BlockSize * sectors, &Written, NULL); } FF_ReleaseSemaphore(ptDevInfo->AccessSem); return Written; }
static FF_T_SINT32 FF_WriteClusters(FF_FILE *pFile, FF_T_UINT32 Count, FF_T_UINT8 *buffer) { FF_T_UINT32 Sectors; FF_T_UINT32 SequentialClusters = 0; FF_T_UINT32 nItemLBA; FF_T_SINT32 RetVal; while(Count != 0) { if((Count - 1) > 0) { SequentialClusters = FF_GetSequentialClusters(pFile->pIoman, pFile->AddrCurrentCluster, (Count - 1)); } Sectors = (SequentialClusters + 1) * pFile->pIoman->pPartition->SectorsPerCluster; nItemLBA = FF_Cluster2LBA(pFile->pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pFile->pIoman, nItemLBA); do { if(pFile->pIoman->pBlkDevice->fnWriteBlocks) { #ifdef FF_BLKDEV_USES_SEM FF_PendSemaphore(pFile->pIoman->pSemaphore); #endif // Called from FF_Write, sem not claimed RetVal = pFile->pIoman->pBlkDevice->fnWriteBlocks(buffer, nItemLBA, Sectors, pFile->pIoman->pBlkDevice->pParam); #ifdef FF_BLKDEV_USES_SEM FF_ReleaseSemaphore(pFile->pIoman->pSemaphore); #endif if(RetVal == FF_ERR_DRIVER_BUSY) { FF_Yield(); FF_Sleep(FF_DRIVER_BUSY_SLEEP); } } else { RetVal = FF_ERR_DEVICE_DRIVER_FAILED; } }while(RetVal == FF_ERR_DRIVER_BUSY); Count -= (SequentialClusters + 1); pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->AddrCurrentCluster, (SequentialClusters + 1)); pFile->CurrentCluster += (SequentialClusters + 1); buffer += Sectors * pFile->pIoman->BlkSize; SequentialClusters = 0; } return 0; }
/** * @public * @brief Unregister a Blockdevice, so that the IOMAN can be re-used for another device. * * Any active partitions must be Unmounted first. * * @param pIoman FF_IOMAN object. * * @return FF_ERR_NONE on success. **/ FF_ERROR FF_UnregisterBlkDevice(FF_IOMAN *pIoman) { FF_T_SINT8 RetVal = FF_ERR_NONE; if(!pIoman) { return FF_ERR_NULL_POINTER; } FF_PendSemaphore(pIoman->pSemaphore); { if(pIoman->pPartition->PartitionMounted == FF_FALSE) { pIoman->pBlkDevice->devBlkSize = 0; pIoman->pBlkDevice->fnpReadBlocks = NULL; pIoman->pBlkDevice->fnpWriteBlocks = NULL; pIoman->pBlkDevice->pParam = NULL; } else { RetVal = FF_ERR_IOMAN_PARTITION_MOUNTED; } } FF_ReleaseSemaphore(pIoman->pSemaphore); return RetVal; }
FF_T_SINT32 FF_BlockRead(FF_IOMAN *pIoman, FF_T_UINT32 ulSectorLBA, FF_T_UINT32 ulNumSectors, void *pBuffer) { FF_T_SINT32 slRetVal = 0; if(pIoman->pPartition->TotalSectors) { if((ulSectorLBA + ulNumSectors) > (pIoman->pPartition->TotalSectors + pIoman->pPartition->BeginLBA)) { return -(FF_ERR_IOMAN_OUT_OF_BOUNDS_READ | FF_BLOCKREAD); } } if(pIoman->pBlkDevice->fnpReadBlocks) { // Make sure we don't execute a NULL. #ifdef FF_BLKDEV_USES_SEM FF_PendSemaphore(pIoman->pBlkDevSemaphore); #endif slRetVal = pIoman->pBlkDevice->fnpReadBlocks(pBuffer, ulSectorLBA, ulNumSectors, pIoman->pBlkDevice->pParam); #ifdef FF_BLKDEV_USES_SEM FF_ReleaseSemaphore(pIoman->pBlkDevSemaphore); #endif if(FF_GETERROR(slRetVal) == FF_ERR_DRIVER_BUSY) { FF_Sleep(FF_DRIVER_BUSY_SLEEP); } } while(FF_GETERROR(slRetVal) == FF_ERR_DRIVER_BUSY); return slRetVal; }
/** * @private * @brief Releases a buffer resource. * * @param pIoman Pointer to an FF_IOMAN object. * @param pBuffer Pointer to an FF_BUFFER object. * **/ FF_ERROR FF_ReleaseBuffer(FF_IOMAN *pIoman, FF_BUFFER *pBuffer) { FF_ERROR Error = FF_ERR_NONE; // Protect description changes with a semaphore. FF_PendSemaphore(pIoman->pSemaphore); { if (pBuffer->NumHandles) { pBuffer->NumHandles--; } else { //printf ("FF_ReleaseBuffer: buffer not claimed\n"); } #ifdef FF_CACHE_WRITE_THROUGH if(pBuffer->Modified == FF_TRUE) { Error = FF_BlockWrite(pIoman, pBuffer->Sector, 1, pBuffer->pBuffer, FF_TRUE); if(!FF_isERR(Error)) { // Ensure if an error occurs its still possible to write the block again. pBuffer->Modified = FF_FALSE; } } #endif } FF_ReleaseSemaphore(pIoman->pSemaphore); return Error; }
FF_BUFFER *FF_GetBuffer(FF_IOMAN *pIoman, FF_T_UINT32 Sector, FF_T_UINT8 Mode) { FF_BUFFER *pBuffer; FF_BUFFER *pBufLRU; // FF_BUFFER *pBufLHITS = NULL; // Wasn't use anymore? FF_BUFFER *pBufMatch = NULL; FF_T_SINT32 RetVal; FF_T_INT LoopCount = FF_GETBUFFER_WAIT_TIME; FF_T_INT cacheSize = pIoman->CacheSize; if (cacheSize <= 0) { return NULL; } while(!pBufMatch) { if (!--LoopCount) { // // *pError = FF_ERR_IOMAN_GETBUFFER_TIMEOUT; // return NULL; } FF_PendSemaphore(pIoman->pSemaphore); { for(pBuffer = pIoman->pBuffers; pBuffer < pIoman->pBuffers + cacheSize; pBuffer++) { if(pBuffer->Sector == Sector && pBuffer->Valid) { pBufMatch = pBuffer; break; // Don't look further if you found a perfect match } } if(pBufMatch) { // A Match was found process! if(Mode == FF_MODE_READ && pBufMatch->Mode == FF_MODE_READ) { pBufMatch->NumHandles += 1; pBufMatch->Persistance += 1; break; } if(pBufMatch->NumHandles == 0) { pBufMatch->Mode = (Mode & FF_MODE_RD_WR); if((Mode & FF_MODE_WRITE) != 0) { // This buffer has no attached handles. pBufMatch->Modified = FF_TRUE; } pBufMatch->NumHandles = 1; pBufMatch->Persistance += 1; break; } pBufMatch = NULL; // Sector is already in use, keep yielding until its available! } else { pBufLRU = NULL; // So put them to NULL here for(pBuffer = pIoman->pBuffers; pBuffer < pIoman->pBuffers + cacheSize; pBuffer++) { if(pBuffer->NumHandles) continue; // Occupied pBuffer->LRU += 1; if(!pBufLRU) { pBufLRU = pBuffer; } if(pBuffer->LRU > pBufLRU->LRU || (pBuffer->LRU == pBufLRU->LRU && pBuffer->Persistance > pBufLRU->Persistance)) { pBufLRU = pBuffer; } } // Choose a suitable buffer! if(pBufLRU) { // Process the suitable candidate. if(pBufLRU->Modified == FF_TRUE) { // Along with the FF_TRUE parameter to indicate semapahore has been claimed RetVal = FF_BlockWrite(pIoman, pBufLRU->Sector, 1, pBufLRU->pBuffer, FF_TRUE); if (RetVal < 0) { pBufMatch = NULL; break; } } if (Mode == FF_MODE_WR_ONLY) { memset (pBufLRU->pBuffer, '\0', pIoman->BlkSize); } else { RetVal = FF_BlockRead(pIoman, Sector, 1, pBufLRU->pBuffer, FF_TRUE); if (RetVal < 0) { pBufMatch = NULL; break; } } pBufLRU->Mode = (Mode & FF_MODE_RD_WR); pBufLRU->Persistance = 1; pBufLRU->LRU = 0; pBufLRU->NumHandles = 1; pBufLRU->Sector = Sector; pBufLRU->Modified = (Mode & FF_MODE_WRITE) != 0; pBufLRU->Valid = FF_TRUE; pBufMatch = pBufLRU; break; } } } FF_ReleaseSemaphore(pIoman->pSemaphore); // Better to go asleep to give low-priority task a chance to release buffer(s) FF_Sleep (FF_GETBUFFER_SLEEP_TIME); } // while(!pBufMatch) FF_ReleaseSemaphore(pIoman->pSemaphore); return pBufMatch; // Return the Matched Buffer! }
FF_BUFFER *FF_GetBuffer(FF_IOMAN *pIoman, FF_T_UINT32 Sector, FF_T_UINT8 Mode) { FF_BUFFER *pBuffer; FF_BUFFER *pBufLRU = NULL; FF_BUFFER *pBufLHITS = NULL; FF_BUFFER *pBufMatch = NULL; FF_T_UINT32 i; while(!pBufMatch) { FF_PendSemaphore(pIoman->pSemaphore); { pBuffer = pIoman->pBuffers; // HT if a perfect match has priority, find that first for(i = 0; i < pIoman->CacheSize; i++, pBuffer++) { pBuffer = (pIoman->pBuffers + i); if(pBuffer->Sector == Sector && pBuffer->Valid == FF_TRUE) { pBufMatch = pBuffer; break; // Why look further if you found a perfect match? } } if(pBufMatch) { // A Match was found process! if(Mode == FF_MODE_READ && pBufMatch->Mode == FF_MODE_READ) { pBufMatch->NumHandles += 1; pBufMatch->Persistance += 1; FF_ReleaseSemaphore(pIoman->pSemaphore); return pBufMatch; } if(pBufMatch->Mode == FF_MODE_WRITE && pBufMatch->NumHandles == 0) { // This buffer has no attached handles. pBufMatch->Mode = Mode; pBufMatch->NumHandles = 1; pBufMatch->Persistance += 1; FF_ReleaseSemaphore(pIoman->pSemaphore); return pBufMatch; } if(pBufMatch->Mode == FF_MODE_READ && Mode == FF_MODE_WRITE && pBufMatch->NumHandles == 0) { pBufMatch->Mode = Mode; pBufMatch->Modified = FF_TRUE; pBufMatch->NumHandles = 1; pBufMatch->Persistance += 1; FF_ReleaseSemaphore(pIoman->pSemaphore); return pBufMatch; } pBufMatch = NULL; // Sector is already in use, keep yielding until its available! } else { pBuffer = pIoman->pBuffers; for(i = 0; i < pIoman->CacheSize; i++, pBuffer++) { if(pBuffer->NumHandles == 0) { pBuffer->LRU += 1; if(!pBufLRU) { pBufLRU = pBuffer; } if(!pBufLHITS) { pBufLHITS = pBuffer; } if(pBuffer->LRU >= pBufLRU->LRU) { if(pBuffer->LRU == pBufLRU->LRU) { if(pBuffer->Persistance > pBufLRU->Persistance) { pBufLRU = pBuffer; } } else { pBufLRU = pBuffer; } } if(pBuffer->Persistance < pBufLHITS->Persistance) { pBufLHITS = pBuffer; } } } if(pBufLRU) { // Process the suitable candidate. if(pBufLRU->Modified == FF_TRUE) { FF_BlockWrite(pIoman, pBufLRU->Sector, 1, pBufLRU->pBuffer); } pBufLRU->Mode = Mode; pBufLRU->Persistance = 1; pBufLRU->LRU = 0; pBufLRU->NumHandles = 1; pBufLRU->Sector = Sector; if(Mode == FF_MODE_WRITE) { pBufLRU->Modified = FF_TRUE; } else { pBufLRU->Modified = FF_FALSE; } FF_BlockRead(pIoman, Sector, 1, pBufLRU->pBuffer); pBufLRU->Valid = FF_TRUE; FF_ReleaseSemaphore(pIoman->pSemaphore); return pBufLRU; } } } FF_ReleaseSemaphore(pIoman->pSemaphore); FF_Yield(); // Better to go asleep to give low-priority task a chance to release buffer(s) } return pBufMatch; // Return the Matched Buffer! }
/** * @public * @brief Equivalent to fread() * * @param pFile FF_FILE object that was created by FF_Open(). * @param ElementSize The size of an element to read. * @param Count The number of elements to read. * @param buffer A pointer to a buffer of adequate size to be filled with the requested data. * * @return Number of bytes read. * **/ FF_T_SINT32 FF_Read(FF_FILE *pFile, FF_T_UINT32 ElementSize, FF_T_UINT32 Count, FF_T_UINT8 *buffer) { FF_T_UINT32 nBytes = ElementSize * Count; FF_T_UINT32 nBytesRead = 0; FF_T_UINT32 nBytesToRead; FF_IOMAN *pIoman; FF_BUFFER *pBuffer; FF_T_UINT32 nRelBlockPos; FF_T_UINT32 nItemLBA; FF_T_SINT32 RetVal = 0; FF_T_UINT16 sSectors; FF_T_UINT32 nRelClusterPos; FF_T_UINT32 nBytesPerCluster; FF_T_UINT32 nClusterDiff; if(!pFile) { return FF_ERR_NULL_POINTER; } if(!(pFile->Mode & FF_MODE_READ)) { return FF_ERR_FILE_NOT_OPENED_IN_READ_MODE; } pIoman = pFile->pIoman; if(pFile->FilePointer == pFile->Filesize) { return 0; } if((pFile->FilePointer + nBytes) > pFile->Filesize) { nBytes = pFile->Filesize - pFile->FilePointer; } nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff); pFile->CurrentCluster += nClusterDiff; } } nRelBlockPos = FF_getMinorBlockEntry(pIoman, pFile->FilePointer, 1); // Get the position within a block. nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); if((nRelBlockPos + nBytes) < pIoman->BlkSize) { // Bytes to read are within a block and less than a block size. pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_READ); { memcpy(buffer, (pBuffer->pBuffer + nRelBlockPos), nBytes); } FF_ReleaseBuffer(pIoman, pBuffer); pFile->FilePointer += nBytes; return nBytes; // Return the number of bytes read. } else { //---------- Read (memcpy) to a Sector Boundary if(nRelBlockPos != 0) { // Not on a sector boundary, at this point the LBA is known. nBytesToRead = pIoman->BlkSize - nRelBlockPos; pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_READ); { // Here we copy to the sector boudary. memcpy(buffer, (pBuffer->pBuffer + nRelBlockPos), nBytesToRead); } FF_ReleaseBuffer(pIoman, pBuffer); nBytes -= nBytesToRead; nBytesRead += nBytesToRead; pFile->FilePointer += nBytesToRead; buffer += nBytesToRead; } //---------- Read to a Cluster Boundary nRelClusterPos = FF_getClusterPosition(pIoman, pFile->FilePointer, 1); nBytesPerCluster = (pIoman->pPartition->SectorsPerCluster * pIoman->BlkSize); if(nRelClusterPos != 0 && nBytes >= nBytesPerCluster) { // Need to get to cluster boundary nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff); pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); sSectors = (FF_T_UINT16) (pIoman->pPartition->SectorsPerCluster - (nRelClusterPos / pIoman->BlkSize)); do { if(pIoman->pBlkDevice->fnReadBlocks) { #ifdef FF_BLKDEV_USES_SEM FF_PendSemaphore(pFile->pIoman->pSemaphore); #endif RetVal = pFile->pIoman->pBlkDevice->fnReadBlocks(buffer, nItemLBA, sSectors, pIoman->pBlkDevice->pParam); #ifdef FF_BLKDEV_USES_SEM FF_ReleaseSemaphore(pFile->pIoman->pSemaphore); #endif } if(RetVal == FF_ERR_DRIVER_BUSY) { FF_Yield(); FF_Sleep(FF_DRIVER_BUSY_SLEEP); } }while(RetVal == FF_ERR_DRIVER_BUSY); nBytesToRead = sSectors * pIoman->BlkSize; nBytes -= nBytesToRead; buffer += nBytesToRead; nBytesRead += nBytesToRead; pFile->FilePointer += nBytesToRead; } //---------- Read Clusters if(nBytes >= nBytesPerCluster) { //----- Thanks to Christopher Clark of DigiPen Institute of Technology in Redmond, US adding this traversal check. nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff); pFile->CurrentCluster += nClusterDiff; } } //----- End of Contributor fix. FF_ReadClusters(pFile, (nBytes / nBytesPerCluster), buffer); nBytesToRead = (nBytesPerCluster * (nBytes / nBytesPerCluster)); pFile->FilePointer += nBytesToRead; nBytes -= nBytesToRead; buffer += nBytesToRead; nBytesRead += nBytesToRead; } //---------- Read Remaining Blocks if(nBytes >= pIoman->BlkSize) { sSectors = (FF_T_UINT16) (nBytes / pIoman->BlkSize); nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff); pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); do { if(pIoman->pBlkDevice->fnReadBlocks) { #ifdef FF_BLKDEV_USES_SEM FF_PendSemaphore(pFile->pIoman->pSemaphore); #endif RetVal = pFile->pIoman->pBlkDevice->fnReadBlocks(buffer, nItemLBA, sSectors, pIoman->pBlkDevice->pParam); #ifdef FF_BLKDEV_USES_SEM FF_ReleaseSemaphore(pFile->pIoman->pSemaphore); #endif } if(RetVal == FF_ERR_DRIVER_BUSY) { FF_Yield(); FF_Sleep(FF_DRIVER_BUSY_SLEEP); } }while(RetVal == FF_ERR_DRIVER_BUSY); nBytesToRead = sSectors * pIoman->BlkSize; pFile->FilePointer += nBytesToRead; nBytes -= nBytesToRead; buffer += nBytesToRead; nBytesRead += nBytesToRead; } //---------- Read (memcpy) Remaining Bytes if(nBytes > 0) { nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff); pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_READ); { memcpy(buffer, pBuffer->pBuffer, nBytes); } FF_ReleaseBuffer(pIoman, pBuffer); nBytesToRead = nBytes; pFile->FilePointer += nBytesToRead; nBytes -= nBytesToRead; buffer += nBytesToRead; nBytesRead += nBytesToRead; } } return nBytesRead; }
FF_ERROR FF_RmDir(FF_IOMAN *pIoman, const FF_T_INT8 *path) { FF_FILE *pFile; FF_ERROR Error = FF_ERR_NONE; FF_T_UINT8 EntryBuffer[32]; 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); { FF_UnlinkClusterChain(pIoman, pFile->ObjectCluster, 0); // 0 to delete the entire chain! } FF_unlockFAT(pIoman); // Edit the Directory Entry! (So it appears as deleted); FF_RmLFNs(pIoman, pFile->DirCluster, pFile->DirEntry); FF_FetchEntry(pIoman, pFile->DirCluster, pFile->DirEntry, EntryBuffer); EntryBuffer[0] = 0xE5; FF_PushEntry(pIoman, pFile->DirCluster, pFile->DirEntry, EntryBuffer); #ifdef FF_PATH_CACHE FF_PendSemaphore(pIoman->pSemaphore); // Thread safety on shared object! { for(i = 0; i < FF_PATH_CACHE_DEPTH; i++) { if(FF_strmatch(pIoman->pPartition->PathCache[i].Path, path, (FF_T_UINT16)strlen(path))) { pIoman->pPartition->PathCache[i].Path[0] = '\0'; pIoman->pPartition->PathCache[i].DirCluster = 0; FF_ReleaseSemaphore(pIoman->pSemaphore); } } } FF_ReleaseSemaphore(pIoman->pSemaphore); #endif FF_IncreaseFreeClusters(pIoman, pFile->iChainLength); FF_FlushCache(pIoman); } else { RetVal = FF_ERR_DIR_NOT_EMPTY; } } FF_unlockDIR(pIoman); FF_Close(pFile); // Free the file pointer resources // File is now lost! return RetVal; }
/** * @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 Writes data to a File. * * @param pFile FILE Pointer. * @param ElementSize Size of an Element of Data to be copied. (in bytes). * @param Count Number of Elements of Data to be copied. (ElementSize * Count must not exceed ((2^31)-1) bytes. (2GB). For best performance, multiples of 512 bytes or Cluster sizes are best. * @param buffer Byte-wise buffer containing the data to be written. * * @return **/ FF_T_SINT32 FF_Write(FF_FILE *pFile, FF_T_UINT32 ElementSize, FF_T_UINT32 Count, FF_T_UINT8 *buffer) { FF_T_UINT32 nBytes = ElementSize * Count; FF_T_UINT32 nBytesWritten = 0; FF_T_UINT32 nBytesToWrite; FF_IOMAN *pIoman; FF_BUFFER *pBuffer; FF_T_UINT32 nRelBlockPos; FF_T_UINT32 nItemLBA; FF_T_SINT32 RetVal = 0; FF_T_UINT16 sSectors; FF_T_UINT32 nRelClusterPos; FF_T_UINT32 nBytesPerCluster, nClusterDiff, nClusters; FF_ERROR Error; if(!pFile) { return FF_ERR_NULL_POINTER; } if(!(pFile->Mode & FF_MODE_WRITE)) { return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE; } // Make sure a write is after the append point. if((pFile->Mode & FF_MODE_APPEND)) { if(pFile->FilePointer < pFile->Filesize) { FF_Seek(pFile, 0, FF_SEEK_END); } } pIoman = pFile->pIoman; nBytesPerCluster = (pIoman->pPartition->SectorsPerCluster * pIoman->BlkSize); // Extend File for atleast nBytes! // Handle file-space allocation Error = FF_ExtendFile(pFile, pFile->FilePointer + nBytes); if(Error) { return Error; } nRelBlockPos = FF_getMinorBlockEntry(pIoman, pFile->FilePointer, 1); // Get the position within a block. nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster != FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff); pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); if((nRelBlockPos + nBytes) < pIoman->BlkSize) { // Bytes to read are within a block and less than a block size. pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE); { memcpy((pBuffer->pBuffer + nRelBlockPos), buffer, nBytes); } FF_ReleaseBuffer(pIoman, pBuffer); pFile->FilePointer += nBytes; nBytesWritten = nBytes; //return nBytes; // Return the number of bytes read. } else { //---------- Write (memcpy) to a Sector Boundary if(nRelBlockPos != 0) { // Not on a sector boundary, at this point the LBA is known. nBytesToWrite = pIoman->BlkSize - nRelBlockPos; pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE); { // Here we copy to the sector boudary. memcpy((pBuffer->pBuffer + nRelBlockPos), buffer, nBytesToWrite); } FF_ReleaseBuffer(pIoman, pBuffer); nBytes -= nBytesToWrite; nBytesWritten += nBytesToWrite; pFile->FilePointer += nBytesToWrite; buffer += nBytesToWrite; } //---------- Write to a Cluster Boundary nRelClusterPos = FF_getClusterPosition(pIoman, pFile->FilePointer, 1); if(nRelClusterPos != 0 && nBytes >= nBytesPerCluster) { // Need to get to cluster boundary nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff); pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); sSectors = (FF_T_UINT16) (pIoman->pPartition->SectorsPerCluster - (nRelClusterPos / pIoman->BlkSize)); do { if(pIoman->pBlkDevice->fnWriteBlocks) { #ifdef FF_BLKDEV_USES_SEM FF_PendSemaphore(pFile->pIoman->pSemaphore); #endif RetVal = pFile->pIoman->pBlkDevice->fnWriteBlocks(buffer, nItemLBA, sSectors, pIoman->pBlkDevice->pParam); #ifdef FF_BLKDEV_USES_SEM FF_ReleaseSemaphore(pFile->pIoman->pSemaphore); #endif } if(RetVal == FF_ERR_DRIVER_BUSY) { FF_Yield(); FF_Sleep(FF_DRIVER_BUSY_SLEEP); } }while(RetVal == FF_ERR_DRIVER_BUSY); nBytesToWrite = sSectors * pIoman->BlkSize; nBytes -= nBytesToWrite; buffer += nBytesToWrite; nBytesWritten += nBytesToWrite; pFile->FilePointer += nBytesToWrite; } //---------- Write Clusters if(nBytes >= nBytesPerCluster) { //----- Thanks to Christopher Clark of DigiPen Institute of Technology in Redmond, US adding this traversal check. nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff); pFile->CurrentCluster += nClusterDiff; } } //----- End of Contributor fix. nClusters = (nBytes / nBytesPerCluster); FF_WriteClusters(pFile, nClusters, buffer); nBytesToWrite = (nBytesPerCluster * nClusters); pFile->FilePointer += nBytesToWrite; nBytes -= nBytesToWrite; buffer += nBytesToWrite; nBytesWritten += nBytesToWrite; } //---------- Write Remaining Blocks if(nBytes >= pIoman->BlkSize) { sSectors = (FF_T_UINT16) (nBytes / pIoman->BlkSize); nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff); pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); do { if(pIoman->pBlkDevice->fnWriteBlocks) { #ifdef FF_BLKDEV_USES_SEM FF_PendSemaphore(pFile->pIoman->pSemaphore); #endif RetVal = pFile->pIoman->pBlkDevice->fnWriteBlocks(buffer, nItemLBA, sSectors, pIoman->pBlkDevice->pParam); #ifdef FF_BLKDEV_USES_SEM FF_ReleaseSemaphore(pFile->pIoman->pSemaphore); #endif } if(RetVal == FF_ERR_DRIVER_BUSY) { FF_Yield(); FF_Sleep(FF_DRIVER_BUSY_SLEEP); } }while(RetVal == FF_ERR_DRIVER_BUSY); nBytesToWrite = sSectors * pIoman->BlkSize; pFile->FilePointer += nBytesToWrite; nBytes -= nBytesToWrite; buffer += nBytesToWrite; nBytesWritten += nBytesToWrite; } //---------- Write (memcpy) Remaining Bytes if(nBytes > 0) { nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff); pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE); { memcpy(pBuffer->pBuffer, buffer, nBytes); } FF_ReleaseBuffer(pIoman, pBuffer); nBytesToWrite = nBytes; pFile->FilePointer += nBytesToWrite; nBytes -= nBytesToWrite; buffer += nBytesToWrite; nBytesWritten += nBytesToWrite; } } if(pFile->FilePointer > pFile->Filesize) { pFile->Filesize = pFile->FilePointer; } return nBytesWritten; }
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; }