feat: fix file rename implementation
This commit is contained in:
@@ -81,6 +81,10 @@ namespace Montauk {
|
||||
return Fs::Vfs::VfsMkdir(path);
|
||||
}
|
||||
|
||||
static int Sys_FRename(const char* oldPath, const char* newPath) {
|
||||
return Fs::Vfs::VfsRename(oldPath, newPath);
|
||||
}
|
||||
|
||||
static int Sys_DriveList(int* outDrives, int maxEntries) {
|
||||
return Fs::Vfs::VfsDriveList(outDrives, maxEntries);
|
||||
}
|
||||
|
||||
@@ -200,6 +200,10 @@ namespace Montauk {
|
||||
case SYS_FMKDIR:
|
||||
if (!ValidUserPtr(frame->arg1)) return -1;
|
||||
return (int64_t)Sys_FMkdir((const char*)frame->arg1);
|
||||
case SYS_FRENAME:
|
||||
if (!ValidUserPtr(frame->arg1)) return -1;
|
||||
if (!ValidUserPtr(frame->arg2)) return -1;
|
||||
return (int64_t)Sys_FRename((const char*)frame->arg1, (const char*)frame->arg2);
|
||||
case SYS_DRIVELIST:
|
||||
if (!ValidUserPtr(frame->arg1)) return -1;
|
||||
return (int64_t)Sys_DriveList((int*)frame->arg1, (int)frame->arg2);
|
||||
|
||||
@@ -182,6 +182,9 @@ namespace Montauk {
|
||||
static constexpr uint64_t SYS_SETUSER = 92;
|
||||
static constexpr uint64_t SYS_GETUSER = 93;
|
||||
|
||||
/* Filesystem.hpp */
|
||||
static constexpr uint64_t SYS_FRENAME = 94;
|
||||
|
||||
static constexpr int SOCK_TCP = 1;
|
||||
static constexpr int SOCK_UDP = 2;
|
||||
|
||||
|
||||
@@ -1437,6 +1437,109 @@ namespace Fs::Ext2 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Rename — move a directory entry (inode stays the same)
|
||||
// =========================================================================
|
||||
|
||||
static int RenameImpl(int inst, const char* oldPath, const char* newPath) {
|
||||
if (inst < 0 || inst >= g_instanceCount || !g_instances[inst].active) return -1;
|
||||
auto& self = g_instances[inst];
|
||||
|
||||
// Split old path
|
||||
char oldParentPath[MaxNameLen];
|
||||
char oldFileName[MaxNameLen];
|
||||
SplitPath(oldPath, oldParentPath, MaxNameLen, oldFileName, MaxNameLen);
|
||||
if (oldFileName[0] == '\0') return -1;
|
||||
|
||||
// Split new path
|
||||
char newParentPath[MaxNameLen];
|
||||
char newFileName[MaxNameLen];
|
||||
SplitPath(newPath, newParentPath, MaxNameLen, newFileName, MaxNameLen);
|
||||
if (newFileName[0] == '\0') return -1;
|
||||
|
||||
// Find old entry
|
||||
uint32_t oldParentInodeNum;
|
||||
Inode oldParentInode;
|
||||
if (!TraversePath(self, oldParentPath, &oldParentInodeNum, &oldParentInode)) return -1;
|
||||
if ((oldParentInode.i_mode & IMODE_TYPE_MASK) != IMODE_DIR) return -1;
|
||||
|
||||
ParsedEntry oldEntry;
|
||||
if (!FindInDirectory(self, oldParentInode, oldFileName, &oldEntry)) return -1;
|
||||
|
||||
// Find new parent
|
||||
uint32_t newParentInodeNum;
|
||||
Inode newParentInode;
|
||||
if (!TraversePath(self, newParentPath, &newParentInodeNum, &newParentInode)) return -1;
|
||||
if ((newParentInode.i_mode & IMODE_TYPE_MASK) != IMODE_DIR) return -1;
|
||||
|
||||
// If destination already exists, delete it
|
||||
ParsedEntry destEntry;
|
||||
if (FindInDirectory(self, newParentInode, newFileName, &destEntry)) {
|
||||
Inode destInode;
|
||||
if (!ReadInode(self, destEntry.inodeNum, &destInode)) return -1;
|
||||
|
||||
bool destIsDir = (destInode.i_mode & IMODE_TYPE_MASK) == IMODE_DIR;
|
||||
if (destIsDir) {
|
||||
ParsedEntry children[1];
|
||||
int childCount = ReadDirectoryEntries(self, destInode, children, 1);
|
||||
if (childCount > 0) return -1;
|
||||
}
|
||||
|
||||
if (!RemoveDirEntry(self, newParentInode, newFileName)) return -1;
|
||||
|
||||
if (destIsDir) {
|
||||
newParentInode.i_links_count--;
|
||||
WriteInode(self, newParentInodeNum, &newParentInode);
|
||||
}
|
||||
|
||||
destInode.i_links_count--;
|
||||
if (destIsDir) destInode.i_links_count--;
|
||||
if (destInode.i_links_count <= 0) {
|
||||
FreeInodeBlocks(self, destInode);
|
||||
destInode.i_mode = 0;
|
||||
WriteInode(self, destEntry.inodeNum, &destInode);
|
||||
FreeInode(self, destEntry.inodeNum);
|
||||
} else {
|
||||
WriteInode(self, destEntry.inodeNum, &destInode);
|
||||
}
|
||||
|
||||
// Re-read new parent inode since RemoveDirEntry may have modified it
|
||||
ReadInode(self, newParentInodeNum, &newParentInode);
|
||||
}
|
||||
|
||||
// Remove old directory entry (does not touch the inode)
|
||||
// Re-read old parent in case it's the same dir and was modified above
|
||||
ReadInode(self, oldParentInodeNum, &oldParentInode);
|
||||
if (!RemoveDirEntry(self, oldParentInode, oldFileName)) return -1;
|
||||
|
||||
// Add new directory entry pointing to the same inode
|
||||
if (!AddDirEntry(self, newParentInodeNum, newParentInode,
|
||||
oldEntry.inodeNum, newFileName, oldEntry.fileType)) {
|
||||
// Failed to add — try to restore old entry
|
||||
ReadInode(self, oldParentInodeNum, &oldParentInode);
|
||||
AddDirEntry(self, oldParentInodeNum, oldParentInode,
|
||||
oldEntry.inodeNum, oldFileName, oldEntry.fileType);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If moving a directory across parents, update ".." entry and link counts
|
||||
bool isDir = oldEntry.fileType == EXT2_FT_DIR;
|
||||
if (isDir && oldParentInodeNum != newParentInodeNum) {
|
||||
// Decrement old parent link count, increment new parent
|
||||
ReadInode(self, oldParentInodeNum, &oldParentInode);
|
||||
oldParentInode.i_links_count--;
|
||||
WriteInode(self, oldParentInodeNum, &oldParentInode);
|
||||
|
||||
ReadInode(self, newParentInodeNum, &newParentInode);
|
||||
newParentInode.i_links_count++;
|
||||
WriteInode(self, newParentInodeNum, &newParentInode);
|
||||
|
||||
// TODO: update ".." entry inside the moved directory to point to new parent
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Template thunks — generate unique function pointers per instance
|
||||
// =========================================================================
|
||||
@@ -1451,6 +1554,7 @@ namespace Fs::Ext2 {
|
||||
static int Create(const char* p) { return CreateImpl(N, p); }
|
||||
static int Delete(const char* p) { return DeleteImpl(N, p); }
|
||||
static int Mkdir(const char* p) { return MkdirImpl(N, p); }
|
||||
static int Rename(const char* o, const char* n) { return RenameImpl(N, o, n); }
|
||||
};
|
||||
|
||||
template<int N>
|
||||
@@ -1465,6 +1569,7 @@ namespace Fs::Ext2 {
|
||||
Thunks<N>::Create,
|
||||
Thunks<N>::Delete,
|
||||
Thunks<N>::Mkdir,
|
||||
Thunks<N>::Rename,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1469,6 +1469,220 @@ namespace Fs::Fat32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Rename — atomic directory entry move
|
||||
// =========================================================================
|
||||
|
||||
static int RenameImpl(int inst, const char* oldPath, const char* newPath) {
|
||||
if (inst < 0 || inst >= g_instanceCount || !g_instances[inst].active) return -1;
|
||||
auto& self = g_instances[inst];
|
||||
|
||||
// Split old path
|
||||
char oldParentPath[MaxNameLen];
|
||||
char oldFileName[MaxNameLen];
|
||||
SplitPath(oldPath, oldParentPath, MaxNameLen, oldFileName, MaxNameLen);
|
||||
if (oldFileName[0] == '\0') return -1;
|
||||
|
||||
// Split new path
|
||||
char newParentPath[MaxNameLen];
|
||||
char newFileName[MaxNameLen];
|
||||
SplitPath(newPath, newParentPath, MaxNameLen, newFileName, MaxNameLen);
|
||||
if (newFileName[0] == '\0') return -1;
|
||||
|
||||
// Traverse to old parent directory
|
||||
ParsedEntry oldParent;
|
||||
if (!TraversePath(inst, oldParentPath, &oldParent)) return -1;
|
||||
if (!(oldParent.attributes & ATTR_DIRECTORY)) return -1;
|
||||
|
||||
// Find old entry
|
||||
ParsedEntry oldEntry;
|
||||
if (!FindInDirectory(inst, oldParent.firstCluster, oldFileName, &oldEntry)) return -1;
|
||||
|
||||
// Save the data we need from the old entry before deleting it
|
||||
uint32_t savedFirstCluster = oldEntry.firstCluster;
|
||||
uint32_t savedFileSize = oldEntry.fileSize;
|
||||
uint8_t savedAttributes = oldEntry.attributes;
|
||||
|
||||
// Traverse to new parent directory
|
||||
ParsedEntry newParent;
|
||||
if (!TraversePath(inst, newParentPath, &newParent)) return -1;
|
||||
if (!(newParent.attributes & ATTR_DIRECTORY)) return -1;
|
||||
|
||||
// If destination already exists, delete it (including its cluster chain)
|
||||
ParsedEntry destEntry;
|
||||
if (FindInDirectory(inst, newParent.firstCluster, newFileName, &destEntry)) {
|
||||
// Don't allow overwriting a non-empty directory
|
||||
if (destEntry.attributes & ATTR_DIRECTORY) {
|
||||
ParsedEntry children[1];
|
||||
int childCount = ReadDirectory(inst, destEntry.firstCluster, children, 1);
|
||||
if (childCount > 0) return -1;
|
||||
}
|
||||
// Free destination cluster chain
|
||||
uint32_t cl = destEntry.firstCluster;
|
||||
while (!IsEndOfChain(cl) && cl >= 2) {
|
||||
uint32_t next = GetNextCluster(self, cl);
|
||||
WriteFatEntry(self, cl, CLUSTER_FREE);
|
||||
cl = next;
|
||||
}
|
||||
// Mark destination directory entries as deleted
|
||||
// (reuse the delete-entry-marking logic inline)
|
||||
uint32_t cluster = newParent.firstCluster;
|
||||
while (!IsEndOfChain(cluster)) {
|
||||
if (!ReadCluster(self, cluster)) return -1;
|
||||
int perCluster = (int)(self.clusterSize / 32);
|
||||
bool modified = false;
|
||||
for (int i = 0; i < perCluster; i++) {
|
||||
uint8_t* e = self.clusterBuf + i * 32;
|
||||
if (e[0] == 0x00) break;
|
||||
if (e[0] == 0xE5) continue;
|
||||
uint64_t clusterPartSec = ClusterToPartSector(self, cluster);
|
||||
uint32_t byteOff = i * 32;
|
||||
uint64_t entrySec = clusterPartSec + (byteOff / self.bytesPerSector);
|
||||
uint32_t entryOff = byteOff % self.bytesPerSector;
|
||||
if (entrySec == destEntry.sfnPartSector &&
|
||||
entryOff == destEntry.sfnOffInSector) {
|
||||
e[0] = 0xE5;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
if (modified) WriteClusterData(self, cluster, self.clusterBuf);
|
||||
cluster = GetNextCluster(self, cluster);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark old directory entries as deleted (but DO NOT free the cluster chain)
|
||||
{
|
||||
uint32_t cluster = oldParent.firstCluster;
|
||||
bool inLfnRun = false;
|
||||
|
||||
while (!IsEndOfChain(cluster)) {
|
||||
if (!ReadCluster(self, cluster)) return -1;
|
||||
int perCluster = (int)(self.clusterSize / 32);
|
||||
bool modified = false;
|
||||
|
||||
for (int i = 0; i < perCluster; i++) {
|
||||
uint8_t* e = self.clusterBuf + i * 32;
|
||||
if (e[0] == 0x00) goto old_write_back;
|
||||
if (e[0] == 0xE5) { inLfnRun = false; continue; }
|
||||
|
||||
uint8_t attr = e[11];
|
||||
if (attr == ATTR_LFN) {
|
||||
uint8_t seq = e[0];
|
||||
if (seq & 0x40) {
|
||||
inLfnRun = false;
|
||||
int lfnCount = seq & 0x1F;
|
||||
int sfnIdx = i + lfnCount;
|
||||
if (sfnIdx < perCluster) {
|
||||
uint64_t clusterPartSec = ClusterToPartSector(self, cluster);
|
||||
uint32_t byteOff = sfnIdx * 32;
|
||||
uint64_t sfnSec = clusterPartSec + (byteOff / self.bytesPerSector);
|
||||
uint32_t sfnOff = byteOff % self.bytesPerSector;
|
||||
if (sfnSec == oldEntry.sfnPartSector &&
|
||||
sfnOff == oldEntry.sfnOffInSector) {
|
||||
inLfnRun = true;
|
||||
e[0] = 0xE5;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
} else if (inLfnRun) {
|
||||
e[0] = 0xE5;
|
||||
modified = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t clusterPartSec = ClusterToPartSector(self, cluster);
|
||||
uint32_t byteOff = i * 32;
|
||||
uint64_t entrySec = clusterPartSec + (byteOff / self.bytesPerSector);
|
||||
uint32_t entryOff = byteOff % self.bytesPerSector;
|
||||
if (entrySec == oldEntry.sfnPartSector &&
|
||||
entryOff == oldEntry.sfnOffInSector) {
|
||||
e[0] = 0xE5;
|
||||
modified = true;
|
||||
inLfnRun = false;
|
||||
goto old_write_back;
|
||||
}
|
||||
inLfnRun = false;
|
||||
}
|
||||
|
||||
old_write_back:
|
||||
if (modified) WriteClusterData(self, cluster, self.clusterBuf);
|
||||
{
|
||||
uint64_t clusterPartSec = ClusterToPartSector(self, cluster);
|
||||
uint64_t clusterEndSec = clusterPartSec + self.sectorsPerCluster;
|
||||
if (oldEntry.sfnPartSector >= clusterPartSec &&
|
||||
oldEntry.sfnPartSector < clusterEndSec) {
|
||||
break; // done removing old entry
|
||||
}
|
||||
}
|
||||
cluster = GetNextCluster(self, cluster);
|
||||
}
|
||||
}
|
||||
|
||||
// Create new directory entry with the saved cluster chain and size
|
||||
char shortName[11];
|
||||
GenerateShortName(newFileName, shortName);
|
||||
MakeShortNameUnique(inst, newParent.firstCluster, shortName);
|
||||
|
||||
uint8_t lfnEntries[20 * 32];
|
||||
int lfnCount = 0;
|
||||
bool needsLfn = NeedsLfn(newFileName, shortName);
|
||||
if (needsLfn) {
|
||||
uint8_t checksum = LfnChecksum(shortName);
|
||||
lfnCount = BuildLfnEntries(newFileName, checksum, lfnEntries);
|
||||
}
|
||||
|
||||
int totalSlots = lfnCount + 1;
|
||||
DirSlotPos pos = FindFreeDirSlots(inst, newParent.firstCluster, totalSlots);
|
||||
if (!pos.found) return -1;
|
||||
|
||||
// Build all entries (LFN + SFN) contiguously
|
||||
uint8_t allEntries[21 * 32];
|
||||
if (lfnCount > 0) {
|
||||
memcpy(allEntries, lfnEntries, lfnCount * 32);
|
||||
}
|
||||
|
||||
// Build the SFN entry with preserved cluster/size
|
||||
uint8_t* sfn = allEntries + lfnCount * 32;
|
||||
memset(sfn, 0, 32);
|
||||
memcpy(sfn, shortName, 11);
|
||||
sfn[11] = savedAttributes;
|
||||
|
||||
uint16_t clHi = (uint16_t)(savedFirstCluster >> 16);
|
||||
uint16_t clLo = (uint16_t)(savedFirstCluster & 0xFFFF);
|
||||
memcpy(sfn + 20, &clHi, 2);
|
||||
memcpy(sfn + 26, &clLo, 2);
|
||||
memcpy(sfn + 28, &savedFileSize, 4);
|
||||
|
||||
// Write entries to disk, handling sector boundaries
|
||||
uint64_t curSector = pos.partSector;
|
||||
uint32_t curOff = pos.offsetInSec;
|
||||
uint8_t sectorBuf[512];
|
||||
|
||||
if (!ReadPartSectors(self, curSector, 1, sectorBuf)) return -1;
|
||||
|
||||
int bytesTotal = totalSlots * 32;
|
||||
int written = 0;
|
||||
|
||||
while (written < bytesTotal) {
|
||||
int space = (int)self.bytesPerSector - (int)curOff;
|
||||
int chunk = bytesTotal - written;
|
||||
if (chunk > space) chunk = space;
|
||||
|
||||
memcpy(sectorBuf + curOff, allEntries + written, chunk);
|
||||
written += chunk;
|
||||
|
||||
if (!WritePartSectors(self, curSector, 1, sectorBuf)) return -1;
|
||||
if (written < bytesTotal) {
|
||||
curSector++;
|
||||
curOff = 0;
|
||||
if (!ReadPartSectors(self, curSector, 1, sectorBuf)) return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Template thunks — generate unique function pointers per instance
|
||||
// =========================================================================
|
||||
@@ -1483,6 +1697,7 @@ namespace Fs::Fat32 {
|
||||
static int Create(const char* p) { return CreateImpl(N, p); }
|
||||
static int Delete(const char* p) { return DeleteImpl(N, p); }
|
||||
static int Mkdir(const char* p) { return MkdirImpl(N, p); }
|
||||
static int Rename(const char* o, const char* n) { return RenameImpl(N, o, n); }
|
||||
};
|
||||
|
||||
template<int N>
|
||||
@@ -1497,6 +1712,7 @@ namespace Fs::Fat32 {
|
||||
Thunks<N>::Create,
|
||||
Thunks<N>::Delete,
|
||||
Thunks<N>::Mkdir,
|
||||
Thunks<N>::Rename,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -211,6 +211,25 @@ namespace Fs::Vfs {
|
||||
return count;
|
||||
}
|
||||
|
||||
int VfsRename(const char* oldPath, const char* newPath) {
|
||||
int oldDrive, newDrive;
|
||||
const char* oldLocal;
|
||||
const char* newLocal;
|
||||
|
||||
if (!ParsePath(oldPath, oldDrive, oldLocal)) return -1;
|
||||
if (!ParsePath(newPath, newDrive, newLocal)) return -1;
|
||||
|
||||
// Cross-drive rename not supported
|
||||
if (oldDrive != newDrive) return -1;
|
||||
if (oldDrive < 0 || oldDrive >= MaxDrives || driveTable[oldDrive] == nullptr) return -1;
|
||||
if (driveTable[oldDrive]->Rename == nullptr) return -1;
|
||||
|
||||
vfsLock.Acquire();
|
||||
int result = driveTable[oldDrive]->Rename(oldLocal, newLocal);
|
||||
vfsLock.Release();
|
||||
return result;
|
||||
}
|
||||
|
||||
int VfsReadDir(const char* path, const char** outNames, int maxEntries) {
|
||||
int drive;
|
||||
const char* localPath;
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Fs::Vfs {
|
||||
int (*Create)(const char* path);
|
||||
int (*Delete)(const char* path);
|
||||
int (*Mkdir)(const char* path);
|
||||
int (*Rename)(const char* oldPath, const char* newPath);
|
||||
};
|
||||
|
||||
void Initialize();
|
||||
@@ -37,6 +38,7 @@ namespace Fs::Vfs {
|
||||
void VfsClose(int handle);
|
||||
int VfsReadDir(const char* path, const char** outNames, int maxEntries);
|
||||
int VfsMkdir(const char* path);
|
||||
int VfsRename(const char* oldPath, const char* newPath);
|
||||
|
||||
// Returns number of registered drives, fills outDrives[] with their indices
|
||||
int VfsDriveList(int* outDrives, int maxEntries);
|
||||
|
||||
Reference in New Issue
Block a user