Hi Steen
Thanks for the suggestion - that seems to be a good method.
Please try with the following code, which I tested by truncating files, and then adding extra data to the end of them, then truncating them again to different points in the file. If you watch the free cluster count ("info" command) you will see that any freed up clusters are deleted so that they can be reused (this was the most sentitive part because it is not that visible - the actual file length change is simple).
// Truncate the file to the present file pointer location
// this usually makes the file smaller and cleans up no longer used clusters
//
extern int utTruncateFile(UTFILE *ptr_utFile) // {59}
{
if (ptr_utFile->ulFileSize > ptr_utFile->ulFilePosition) { // if the file pointer is at the end of the present file there is nothing to do
if (!(ptr_utFile->ucFileMode & UTFAT_OPEN_FOR_WRITE)) { // only allow truncation if the file is open for writing
return UTFAT_FILE_NOT_WRITEABLE;
}
else {
UTDISK *ptr_utDisk = &utDisks[ptr_utFile->ucDrive];
DIR_ENTRY_STRUCTURE_FAT32 *ptrFileEntry = (DIR_ENTRY_STRUCTURE_FAT32 *)ptr_utDisk->ptrSectorData; // the directory entry in the sector buffer
unsigned long ulOriginalSize = ptr_utFile->ulFileSize;
ptrFileEntry += ptr_utFile->private_disk_location.ucDirectoryEntry; // move to the file entry
if (fnLoadSector(ptr_utDisk, ptr_utFile->private_disk_location.directory_location.ulSector) != UTFAT_SUCCESS) { // ensure that the directory sector is loaded
return UTFAT_DISK_READ_ERROR;
}
// We set the new file size to be equal to the present file pointer location
//
ptr_utFile->ulFileSize = ptr_utFile->ulFilePosition;
fnSetFileInformation(ptrFileEntry, ptr_utFile->ulFileSize);
while (utCommitSector(ptr_utDisk, ptr_utFile->private_disk_location.directory_location.ulSector) == CARD_BUSY_WAIT) {} // force writeback to finalise the operation
ptr_utDisk->usDiskFlags &= ~WRITEBACK_BUFFER_FLAG; // the disk is up to date with the buffer
// We need to free all clusters no longer needed by content after the new end of the file
//
if (ptr_utFile->public_file_location.ulSector >= ptr_utDisk->ulLogicalBaseAddress) { // don't delete cluster chain if there is none allocated
return (fnDeleteClusterChain(ptr_utFile->public_file_location.ulSector, ptr_utDisk->ucDriveNumber, 0)); // free up all clusters belonging to the file content
}
}
}
return UTFAT_SUCCESS;
}
If you are happy with the operation and don't find any errors I will commit it to the next utFAT version.
Good luck
Regards
Mark
P.S. Below is the test command that I added to the command line menu. As you see utSeek() is used to move to the new file location that defines the new file size (you will probably get to the new location with seeks and writes but the important point is that the present location at the call to the new truncation routine defines the new end of file.
Example of its use is "trunc 1000 test_file.txt" - this truncates the existing file test_file.txt to be 1000 bytes long and any following writes are added to the end (unless a seek to a different location is performed first).
case DO_TEST_TRUNCATE: // {59}
{
UTFILE utFile; // temporary file object
unsigned long ulFileLength = fnDecStrHex(ptrInput);
utFile.ptr_utDirObject = ptr_utDirectory;
while (*ptrInput != ' ') {
ptrInput++;
}
if (utOpenFile(++ptrInput, &utFile, UTFAT_OPEN_FOR_WRITE) != UTFAT_PATH_IS_FILE) { // open an existing file
fnDebugMsg("File not found\r\n");
}
else {
const UTDISK *ptrDiskInfo = fnGetDiskInfo(DISK_D);
unsigned long ulOriginalFreeClusters = ptrDiskInfo->utFileInfo.ulFreeClusterCount;
utSeek(&utFile, ulFileLength, UTFAT_SEEK_SET); // move the file pointer to the end of the file size that is required
if (utTruncateFile(&utFile) == UTFAT_SUCCESS) { // truncate to new length
fnDebugMsg("File successfully truncated");
ulOriginalFreeClusters = (ptrDiskInfo->utFileInfo.ulFreeClusterCount - ulOriginalFreeClusters);
if (ulOriginalFreeClusters != 0) {
fnDebugDec(ulOriginalFreeClusters, WITH_SPACE);
fnDebugMsg(" clusters freed");
}
fnDebugMsg("\r\n");
}
else {
fnDebugMsg("Truncation failed\r\n");
}
}
}
break;