fs: change how rename() is handled

This commit is contained in:
Matheus
2025-10-17 13:51:35 -03:00
parent 36c3976c05
commit 7da3557d32
7 changed files with 282 additions and 151 deletions
+90 -36
View File
@@ -672,12 +672,42 @@ static int fatfs_create(vnode_t *parent, char *name, vattr_t *attr, int type, vn
return error;
}
static int fatfs_rename(vnode_t *vsource_dir, vnode_t *vsource, char *old_name, vnode_t *vtarget_dir, vnode_t *vtarget, char *new_name, int flags) {
static int is_directory_empty(fatnode_t *node) {
// check if its actually empty (readcount > 2, asserted 2 for . and ..)
size_t readcount;
dent_t *dents = alloc(sizeof(dent_t) * 3);
if (dents == NULL)
return ENOMEM;
int error = fatfs_getdents((vnode_t *)node, dents, 3, 0, &readcount);
if (error) {
free(dents);
return error;
}
if (readcount > 2) {
free(dents);
return ENOTEMPTY;
}
// if any of the dot entries is missing, do not do anything as we cannot be sure of consistency
if (readcount != 2 ||
strcmp(dents[0].d_name, ".") != 0 || strcmp(dents[1].d_name, "..") != 0) {
free(dents);
printf("fatfs: filesystem corruption detected: missing or bad dot entries\n");
return ENOTEMPTY;
}
free(dents);
return 0;
}
static int fatfs_rename(vnode_t *vsource_dir, vnode_t *vsource, char *old_name, vnode_t *vtarget_dir, char *new_name, int flags) {
fatfs_t *fatfs = (fatfs_t *)vsource_dir->vfs;
fatnode_t *source_dir = (fatnode_t *)vsource_dir;
fatnode_t *source = (fatnode_t *)vsource;
fatnode_t *target_dir = (fatnode_t *)vtarget_dir;
fatnode_t *target = (fatnode_t *)vtarget;
if (vsource_dir->vfs != vtarget_dir->vfs)
return EXDEV;
@@ -685,6 +715,49 @@ static int fatfs_rename(vnode_t *vsource_dir, vnode_t *vsource, char *old_name,
if (vsource_dir->type != V_TYPE_DIR || vtarget_dir->type != V_TYPE_DIR)
return ENOTDIR;
if (vsource->vfsmounted)
return EBUSY;
vnode_t *vtarget = NULL;
fatnode_t *target;
int error = fatfs_lookup(vtarget_dir, new_name, &vtarget, NULL);
if (error && error != ENOENT)
return error;
target = (fatnode_t *)vtarget;
if (target) {
// we are replacing an existing link
// do some checks expected by posix
if (vsource->type != V_TYPE_DIR && vtarget->type == V_TYPE_DIR) {
VOP_RELEASE(vtarget);
return EISDIR;
}
if (vsource->type == V_TYPE_DIR && vtarget->type != V_TYPE_DIR) {
VOP_RELEASE(vtarget);
return ENOTDIR;
}
if (vtarget->vfsmounted) {
VOP_RELEASE(vtarget);
return EBUSY;
}
error = vtarget->type == V_TYPE_DIR ? is_directory_empty(target) : 0;
if (error) {
VOP_RELEASE(vtarget);
return error;
}
if (source == target) {
// POSIX says that if both are the same file, rename is a no-op
VOP_RELEASE(vtarget);
return 0;
}
}
fatfs_dent_t new_dent = {
.attributes = (vsource->type == V_TYPE_DIR) ? FATFS_DENT_ATTRIBUTE_DIRECTORY : 0,
.cluster_low = source->cluster & 0xffff,
@@ -695,9 +768,9 @@ static int fatfs_rename(vnode_t *vsource_dir, vnode_t *vsource, char *old_name,
// switch out or create the target dent
fatfs_dent_t target_dent;
size_t dent_disk_offset;
int error = fatfs_directory_lookup(fatfs, target_dir, new_name, &target_dent, &dent_disk_offset);
error = fatfs_directory_lookup(fatfs, target_dir, new_name, &target_dent, &dent_disk_offset);
if (error == ENOENT) {
__assert(target == NULL);
// create dirent
error = fatfs_write_directory_entry(fatfs, target_dir, &new_dent, new_name, &dent_disk_offset);
if (error)
@@ -708,21 +781,29 @@ static int fatfs_rename(vnode_t *vsource_dir, vnode_t *vsource, char *old_name,
// already exists, switch it up
size_t written;
error = vfs_write(fatfs->backing, &new_dent, sizeof(new_dent), dent_disk_offset, &written, 0);
if (error)
if (error) {
VOP_RELEASE(vtarget);
return error;
}
__assert(written == sizeof(new_dent));
// remove from the vnode map
MUTEX_ACQUIRE(&fatfs->vnode_map_mutex);
__assert(hashtable_remove(&fatfs->vnode_map, &target->dent_disk_offset, sizeof(target->dent_disk_offset)) == 0);
// remove from the vnode map
__assert(hashtable_remove(&fatfs->vnode_map, &dent_disk_offset, sizeof(dent_disk_offset)) == 0);
MUTEX_RELEASE(&fatfs->vnode_map_mutex);
VOP_RELEASE(vtarget); // table hold
__assert(target->parent_dir == target_dir);
VOP_RELEASE(vtarget_dir)
target->parent_dir = NULL;
target->dent_disk_offset = 0;
VOP_RELEASE(vtarget); // lookup hold
} else {
VOP_RELEASE(vtarget);
return error;
}
MUTEX_ACQUIRE(&fatfs->vnode_map_mutex);
@@ -808,38 +889,11 @@ static int fatfs_unlink(vnode_t *vnode, vnode_t *child, char *name, cred_t *cred
fatfs_t *fs = (fatfs_t *)vnode->vfs;
fatnode_t *child_fatnode = (fatnode_t *)child;
// check if its actually empty (readcount > 2, asserted 2 for . and ..)
if (child->type == V_TYPE_DIR) {
size_t readcount;
dent_t *dents = alloc(sizeof(dent_t) * 3);
if (dents == NULL)
return ENOMEM;
int error = fatfs_getdents(child, dents, 3, 0, &readcount);
if (error) {
free(dents);
return error;
}
if (readcount > 2) {
free(dents);
return ENOTEMPTY;
}
// if any of the dot entries is missing, do not do anything as we cannot be sure of consistency
if (readcount != 2 ||
strcmp(dents[0].d_name, ".") != 0 || strcmp(dents[1].d_name, "..") != 0) {
free(dents);
printf("fatfs: filesystem corruption detected: missng or bad dot entries\n");
return ENOTEMPTY;
}
free(dents);
}
int error = child->type == V_TYPE_DIR ? is_directory_empty(child_fatnode) : 0;
// remove the dent
fatfs_dent_t dent;
int error = fatfs_directory_lookup(fs, node, name, &dent, NULL);
error = fatfs_directory_lookup(fs, node, name, &dent, NULL);
if (error)
return error;
+17 -5
View File
@@ -137,7 +137,6 @@ int fatfs_cluster_chain_length(fatfs_t *fatfs, fatfs_cluster_t cluster, size_t *
if (cluster == 0)
return 0;
while (fatfs_is_cluster_eof(fatfs, cluster) == false) {
printf("chain length: %x\n", cluster);
int error = fatfs_next_cluster(fatfs, cluster, &cluster);
if (error)
return error;
@@ -275,6 +274,21 @@ int fatfs_allocate_cluster(fatfs_t *fatfs, fatfs_cluster_t reference, fatfs_clus
return error;
}
int fatfs_destroy_chain(fatfs_t *fatfs, fatfs_cluster_t cluster) {
// truncate whole file
int error = fatfs_cut_chain(fatfs, cluster);
if (error)
return error;
error = fatfs_set_next_cluster(fatfs, cluster, FATFS_CLUSTER_FREE, true);
if (error)
return error;
// [increment and update fsinfo]
return error;
}
int fatfs_resize_file(fatfs_t *fatfs, fatnode_t *fatnode, size_t new_size) {
size_t current_cluster_count = ROUND_UP(fatnode->size, fatfs->cluster_size) / fatfs->cluster_size;
size_t new_cluster_count = ROUND_UP(new_size, fatfs->cluster_size) / fatfs->cluster_size;
@@ -317,13 +331,11 @@ int fatfs_resize_file(fatfs_t *fatfs, fatnode_t *fatnode, size_t new_size) {
}
} else if (new_cluster_count == 0) {
// truncate whole file
error = fatfs_cut_chain(fatfs, fatnode->cluster);
error = fatfs_destroy_chain(fatfs, fatnode->cluster);
if (error)
return error;
error = fatfs_set_next_cluster(fatfs, fatnode->cluster, FATFS_CLUSTER_FREE, true);
if (error)
return error;
// [update fsinfo]
fatnode->cluster = 0;
fatnode->saved_cluster = 0;