Пример #1
0
/**
 *	@public
 *	@brief	Moves a file or directory from source to destination.
 *
 *	@param	pIoman				The FF_IOMAN object pointer.
 *	@param	szSourceFile		String of the source file to be moved or renamed.
 *	@param	szDestinationFile	String of the destination file to where the source should be moved or renamed.
 *
 *	@return	FF_ERR_NONE on success.
 *	@return FF_ERR_FILE_DESTINATION_EXISTS if the destination file exists.
 *	@return FF_ERR_FILE_COULD_NOT_CREATE_DIRENT if dirent creation failed (fatal error!).
 *	@return FF_ERR_FILE_DIR_NOT_FOUND if destination directory was not found.
 *	@return FF_ERR_FILE_SOURCE_NOT_FOUND if the source file was not found.
 *
 **/
FF_ERROR FF_Move(FF_IOMAN *pIoman, const FF_T_INT8 *szSourceFile, const FF_T_INT8 *szDestinationFile) {
	FF_ERROR	Error;
	FF_FILE		*pSrcFile, *pDestFile;
	FF_DIRENT	MyFile;
	FF_T_UINT8	EntryBuffer[32];
	FF_T_UINT16 i;
	FF_T_UINT32	DirCluster;

	if(!pIoman) {
		return FF_ERR_NULL_POINTER;
	}

	// Check destination file doesn't exist!
	pDestFile = FF_Open(pIoman, szDestinationFile, FF_MODE_READ, &Error);

	if(pDestFile || (Error == FF_ERR_FILE_OBJECT_IS_A_DIR)) {
		FF_Close(pDestFile);
		return FF_ERR_FILE_DESTINATION_EXISTS;	// YES -- FAIL
	}

	pSrcFile = FF_Open(pIoman, szSourceFile, FF_MODE_READ, &Error);

	if(Error == FF_ERR_FILE_OBJECT_IS_A_DIR) {
		// Open a directory for moving!
		pSrcFile = FF_Open(pIoman, szSourceFile, FF_MODE_DIR, &Error);
	}

	if(pSrcFile) {

		// Create the new dirent.
		FF_FetchEntry(pIoman, pSrcFile->DirCluster, pSrcFile->DirEntry, EntryBuffer);
		MyFile.Attrib			= FF_getChar(EntryBuffer,  (FF_T_UINT16)(FF_FAT_DIRENT_ATTRIB));
		MyFile.Filesize			= pSrcFile->Filesize;
		MyFile.ObjectCluster	= pSrcFile->ObjectCluster;
		MyFile.CurrentItem		= 0;

		i = (FF_T_UINT16) strlen(szDestinationFile);

		while(i != 0) {
			if(szDestinationFile[i] == '\\' || szDestinationFile[i] == '/') {
				break;
			}
			i--;
		}

		strncpy(MyFile.FileName, (szDestinationFile + i + 1), FF_MAX_FILENAME);

		if(i == 0) {
			i = 1;
		}
		

		DirCluster = FF_FindDir(pIoman, szDestinationFile, i);
		
		if(DirCluster) {

			// Destination Dir was found, we can now create the new entry.
			if(FF_CreateDirent(pIoman, DirCluster, &MyFile)) {
				FF_Close(pSrcFile);
				return FF_ERR_FILE_COULD_NOT_CREATE_DIRENT;	// FAILED
			}

			// Edit the Directory Entry! (So it appears as deleted);
			FF_lockDIR(pIoman);
			{
				FF_RmLFNs(pIoman, pSrcFile->DirCluster, pSrcFile->DirEntry);
				FF_FetchEntry(pIoman, pSrcFile->DirCluster, pSrcFile->DirEntry, EntryBuffer);
				EntryBuffer[0] = 0xE5;
				FF_PushEntry(pIoman, pSrcFile->DirCluster, pSrcFile->DirEntry, EntryBuffer);
			}
			FF_unlockDIR(pIoman);
			FF_Close(pSrcFile);

			FF_FlushCache(pIoman);

			return FF_ERR_NONE;
		}

		return FF_ERR_FILE_DIR_NOT_FOUND;

	}
		
	return FF_ERR_FILE_SOURCE_NOT_FOUND; // Source not found!
}
Пример #2
0
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;
}
Пример #3
0
FF_ERROR FF_RmFile(FF_IOMAN *pIoman, const FF_T_WCHAR *path) {
#else
FF_ERROR FF_RmFile(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;

	pFile = FF_Open(pIoman, path, FF_MODE_READ, &Error);

	if(!pFile) {
		return Error;	// File in use or File not found!
	}

	pFile->FileDeleted = FF_TRUE;

	if(pFile->ObjectCluster) {	// Ensure there is actually a cluster chain to delete!
		FF_lockFAT(pIoman);	// Lock the FAT so its thread-safe.
		{
			Error = FF_UnlinkClusterChain(pIoman, pFile->ObjectCluster, 0);	// 0 to delete the entire chain!
		}
		FF_unlockFAT(pIoman);

		if(Error) {
			FF_Close(pFile);
			return Error;
		}
	}

	// Edit the Directory Entry! (So it appears as deleted);
	FF_lockDIR(pIoman);
	{
		Error = FF_InitEntryFetch(pIoman, pFile->DirCluster, &FetchContext);
		if(Error) {
			FF_unlockDIR(pIoman);
			FF_Close(pFile);
			return Error;
		}
		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;
		}

		FF_CleanupEntryFetch(pIoman, &FetchContext);
	}
	FF_unlockDIR(pIoman);

	Error = FF_FlushCache(pIoman);
	if(Error) {
		FF_Close(pFile);
		return Error;
	}
	
	Error = FF_Close(pFile); // Free the file pointer resources
	return Error;
}
Пример #4
0
FF_ERROR FF_FormatPartition(FF_IOMAN *pIoman, FF_T_UINT32 ulPartitionNumber, FF_T_UINT32 ulClusterSize) {

	FF_BUFFER *pBuffer;
  	FF_T_SINT8	scPartitionCount;
	FF_T_UINT32 maxClusters, f16MaxClusters, f32MaxClusters;
	FF_T_UINT32 fatSize = 32; // Default to a fat32 format.

	FF_PARTITION_ENTRY partitionGeom;

	FF_T_UINT32 ulBPRLba; ///< The LBA of the boot partition record.

	FF_T_UINT32 fat32Size, fat16Size, newFat32Size, newFat16Size, finalFatSize;
	FF_T_UINT32 sectorsPerCluster = ulClusterSize / pIoman->BlkSize;

	FF_T_UINT32 ulReservedSectors, ulTotalSectors;

	FF_T_UINT32 ul32DataSectors, ul16DataSectors;
	FF_T_UINT32 i;

	FF_T_UINT32 ulClusterBeginLBA;

	FF_ERROR	Error = FF_ERR_NONE;

	// Get Partition Metrics, and pass on to FF_Format() function

	pBuffer = FF_GetBuffer(pIoman, 0, FF_MODE_READ);
	{
		if(!pBuffer) {
			return FF_ERR_DEVICE_DRIVER_FAILED | FF_FORMATPARTITION;
		}


		scPartitionCount = FF_PartitionCount(pBuffer->pBuffer);

		if(!scPartitionCount) {
			// Get Partition Geom from volume boot record.
			ulBPRLba = 0;
			partitionGeom.ulStartLBA = FF_getShort(pBuffer->pBuffer, FF_FAT_RESERVED_SECTORS); // Get offset to start of where we can actually put the FAT table.
			ulReservedSectors = partitionGeom.ulStartLBA;
			partitionGeom.ulLength = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, FF_FAT_16_TOTAL_SECTORS);

			if(partitionGeom.ulLength == 0) { // 32-bit entry was used.
				partitionGeom.ulLength = FF_getLong(pBuffer->pBuffer, FF_FAT_32_TOTAL_SECTORS);
			}

			ulTotalSectors = partitionGeom.ulLength;

			partitionGeom.ulLength -= partitionGeom.ulStartLBA; // Remove the reserved sectors from the count.

		} else {
			// Get partition Geom from the partition table entry.

		}

		// Calculate the max possiblenumber of clusters based on clustersize.
		maxClusters = partitionGeom.ulLength / sectorsPerCluster;

		// Determine the size of a FAT table required to support this.
		fat32Size = (maxClusters * 4) / pIoman->BlkSize; // Potential size in sectors of a fat32 table.
		if((maxClusters * 4) % pIoman->BlkSize) {
			fat32Size++;
		}
		fat32Size *= 2;	// Officially there are 2 copies of the FAT.

		fat16Size = (maxClusters * 2) / pIoman->BlkSize; // Potential size in bytes of a fat16 table.
		if((maxClusters * 2) % pIoman->BlkSize) {
			fat16Size++;
		}
		fat16Size *= 2;

		// A real number of sectors to be available is therefore ~~
		ul16DataSectors = partitionGeom.ulLength - fat16Size;
		ul32DataSectors = partitionGeom.ulLength - fat32Size;

		f16MaxClusters = ul16DataSectors / sectorsPerCluster;
		f32MaxClusters = ul32DataSectors / sectorsPerCluster;

		newFat16Size = (f16MaxClusters * 2) / pIoman->BlkSize;
		if((f16MaxClusters * 2) % pIoman->BlkSize) {
			newFat16Size++;
		}

		newFat32Size = (f32MaxClusters * 4) / pIoman->BlkSize;
		if((f32MaxClusters * 4) % pIoman->BlkSize) {
			newFat32Size++;
		}

		// Now determine if this should be fat16/32 format?

		if(f16MaxClusters < 65525) {
			fatSize = 16;
			finalFatSize = newFat16Size;
		} else {
			fatSize = 32;
			finalFatSize = newFat32Size;
		}

		FF_ReleaseBuffer(pIoman, pBuffer);
		for(i = 0; i < finalFatSize*2; i++) { // Ensure the FAT table is clear.
			if(i == 0) {
				pBuffer = FF_GetBuffer(pIoman, partitionGeom.ulStartLBA, FF_MODE_WR_ONLY);
				if(!pBuffer) {
					return FF_ERR_DEVICE_DRIVER_FAILED;
				}

				memset(pBuffer->pBuffer, 0, pIoman->BlkSize);
			} else {
				FF_BlockWrite(pIoman, partitionGeom.ulStartLBA+i, 1, pBuffer->pBuffer, FF_FALSE);
			}
		}

		switch(fatSize) {
		case 16: {
			FF_putShort(pBuffer->pBuffer, 0, 0xFFF8); // First FAT entry.
			FF_putShort(pBuffer->pBuffer, 2, 0xFFFF); // RESERVED alloc.
			break;
		}

		case 32: {
			FF_putLong(pBuffer->pBuffer, 0, 0x0FFFFFF8); // FAT32 FAT sig.
			FF_putLong(pBuffer->pBuffer, 4, 0xFFFFFFFF); // RESERVED alloc.
			FF_putLong(pBuffer->pBuffer, 8, 0x0FFFFFFF); // Root dir allocation.
			break;
		}

		default:
			break;
		}

		FF_ReleaseBuffer(pIoman, pBuffer);


		// Clear and initialise the root dir.
		ulClusterBeginLBA = partitionGeom.ulStartLBA + (finalFatSize*2);

		for(i = 0; i < sectorsPerCluster; i++) {
			if(i == 0) {
				pBuffer = FF_GetBuffer(pIoman, ulClusterBeginLBA, FF_MODE_WR_ONLY);
				memset(pBuffer->pBuffer, 0, pIoman->BlkSize);
			} else  {
				FF_BlockWrite(pIoman, ulClusterBeginLBA+i, 1, pBuffer->pBuffer, FF_FALSE);
			}

		}

		FF_ReleaseBuffer(pIoman, pBuffer);

		// Correctly modify the second FAT item again.
		pBuffer = FF_GetBuffer(pIoman, partitionGeom.ulStartLBA + finalFatSize, FF_MODE_WRITE);
		{
			switch(fatSize) {
			case 16: {
				FF_putShort(pBuffer->pBuffer, 0, 0xFFF8);
				FF_putShort(pBuffer->pBuffer, 2, 0xFFFF);
				break;
			}
			   
			case 32:
				FF_putLong(pBuffer->pBuffer, 0, 0x0FFFFFF8);
				FF_putLong(pBuffer->pBuffer, 4, 0xFFFFFFFF);
				FF_putLong(pBuffer->pBuffer, 8, 0x0FFFFFFF); // Root dir allocation.
			}
		}
		FF_ReleaseBuffer(pIoman, pBuffer);


		// Modify the fields in the VBR/PBR for correct mounting.
		pBuffer = FF_GetBuffer(pIoman, ulBPRLba, FF_MODE_WRITE); 		// Modify the FAT descriptions.
		{

			// -- First section Common vars to Fat12/16 and 32.
			memset(pBuffer->pBuffer, 0, pIoman->BlkSize); 				// Clear the boot record.

			FF_putChar(pBuffer->pBuffer, 0, 0xEB);						// Place the Jump to bootstrap x86 instruction.
			FF_putChar(pBuffer->pBuffer, 1, 0x3C);						// Even though we won't populate the bootstrap code.
			FF_putChar(pBuffer->pBuffer, 2, 0x90);						// Some devices look for this as a signature.

			memcpy(((FF_T_UINT8 *)pBuffer->pBuffer+3), "FULLFAT2", 8); // Place the FullFAT OEM code.

			FF_putShort(pBuffer->pBuffer, 11, pIoman->BlkSize);
			FF_putChar(pBuffer->pBuffer, 13, (FF_T_UINT8) sectorsPerCluster);

			FF_putShort(pBuffer->pBuffer, FF_FAT_RESERVED_SECTORS, (FF_T_UINT16)partitionGeom.ulStartLBA); 	// Number of reserved sectors. (1 for fat12/16, 32 for f32).
			FF_putShort(pBuffer->pBuffer, FF_FAT_NUMBER_OF_FATS, 2); 	// Always 2 copies.


			//FF_putShort(pBuffer->pBuffer, 19, 0);						// Number of sectors in partition if size < 32mb.

			FF_putChar(pBuffer->pBuffer, 21, 0xF8);             		// Media type -- HDD.
		   

			FF_putShort(pBuffer->pBuffer, 510, 0xAA55);					// MBR sig.
			FF_putLong(pBuffer->pBuffer, 32, partitionGeom.ulLength+partitionGeom.ulStartLBA); // Total sectors of this partition.

			if(fatSize == 32) {
				FF_putShort(pBuffer->pBuffer, 36, (FF_T_UINT16)finalFatSize);		// Number of sectors per fat. 
				FF_putShort(pBuffer->pBuffer, 44, 2);					// Root dir cluster (2).
				FF_putShort(pBuffer->pBuffer, 48, 1);					// FSINFO sector at LBA1.
				FF_putShort(pBuffer->pBuffer, 50, 6);					// 0 for no backup boot sector.
				FF_putChar(pBuffer->pBuffer, 66, 0x29);					// Indicate extended signature is present.
				memcpy(((FF_T_UINT8 *)pBuffer->pBuffer+71), "FullFAT2-V", 10); // Volume name.
				memcpy(((FF_T_UINT8 *)pBuffer->pBuffer+81), "FAT32   ", 8);

				// Put backup boot sector.
				FF_BlockWrite(pIoman, 6, 1, pBuffer->pBuffer, FF_FALSE);
			} else {
				FF_putChar(pBuffer->pBuffer, 38, 0x28);					// Signal this contains an extended signature.
				memcpy(((FF_T_UINT8 *)pBuffer->pBuffer+43), "FullFAT2-V", 10); // Volume name.
				memcpy(((FF_T_UINT8 *)pBuffer->pBuffer+54), "FAT16   ", 8);
				FF_putShort(pBuffer->pBuffer, FF_FAT_16_SECTORS_PER_FAT, (FF_T_UINT16) finalFatSize);
				FF_putShort(pBuffer->pBuffer, 17, 512);				 		// Number of Dir entries. (FAT32 0).
				//FF_putShort(pBuffer->pBuffer, FF_FAT_ROOT_ENTRY_COUNT, 
			}
		}
	}
	FF_ReleaseBuffer(pIoman, pBuffer);

	if(fatSize == 32) {
		// Finally if FAT32, create an FSINFO sector.
		pBuffer = FF_GetBuffer(pIoman, 1, FF_MODE_WRITE);
		{
			memset(pBuffer->pBuffer, 0, pIoman->BlkSize);
			FF_putLong(pBuffer->pBuffer, 0, 0x41615252);	// FSINFO sect magic number.
			FF_putLong(pBuffer->pBuffer, 484, 0x61417272);	// FSINFO second sig.
			// Calculate total sectors, -1 for the root dir allocation. (Free sectors count).
			FF_putLong(pBuffer->pBuffer, 488, ((ulTotalSectors - (ulReservedSectors + (2*finalFatSize))) / sectorsPerCluster)-1);
			FF_putLong(pBuffer->pBuffer, 492, 2);			// Hint for next free cluster.
			FF_putShort(pBuffer->pBuffer, 510, 0xAA55);
		}
		FF_ReleaseBuffer(pIoman, pBuffer);
	}

	FF_FlushCache(pIoman);

	return Error;
}
Пример #5
0
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;
}