mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-11-01 15:08:21 +00:00
ext2: do not load entire directories into memory when doing directory operations
This commit is contained in:
+52
-44
@@ -879,36 +879,41 @@ static int rwbytes(ext2fs_t *fs, ext2node_t *node, void *buffer, size_t count, u
|
|||||||
|
|
||||||
#define BUFFER_MAP_FLAGS (ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC)
|
#define BUFFER_MAP_FLAGS (ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC)
|
||||||
|
|
||||||
|
// returns the inode in inode
|
||||||
|
// if switchnode is non null, switches the found dent to point to that inode (such as on a rename)
|
||||||
static int findindir(ext2fs_t *fs, ext2node_t *node, char *name, int *inode, ext2node_t *switchnode) {
|
static int findindir(ext2fs_t *fs, ext2node_t *node, char *name, int *inode, ext2node_t *switchnode) {
|
||||||
// XXX loading the whole dir into memory at once isn't the best idea but works for now
|
void *dirbuffer = alloc(fs->blocksize);
|
||||||
void *dirbuffer = vmm_map(NULL, INODE_SIZE(&node->inode) + 1, VMM_FLAGS_ALLOCATE, BUFFER_MAP_FLAGS, NULL); // + 1 to make sure there is at least one page mapped
|
|
||||||
if (dirbuffer == NULL)
|
if (dirbuffer == NULL)
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
|
|
||||||
int err = rwbytes(fs, node, dirbuffer, INODE_SIZE(&node->inode), 0, false, true);
|
int err;
|
||||||
if (err)
|
size_t size = INODE_SIZE(&node->inode);
|
||||||
goto cleanup;
|
size_t bytes_done = 0;
|
||||||
|
|
||||||
uintmax_t offset = 0;
|
|
||||||
int inodefound = 0;
|
int inodefound = 0;
|
||||||
size_t namelen = strlen(name);
|
size_t namelen = strlen(name);
|
||||||
|
|
||||||
// iterate through directory to find the inode number
|
while (bytes_done < size) {
|
||||||
while (offset < INODE_SIZE(&node->inode)) {
|
size_t block_offset = bytes_done % fs->blocksize;
|
||||||
ext2dent_t *dent = (ext2dent_t *)((uintptr_t)dirbuffer + offset);
|
if (block_offset == 0) {
|
||||||
|
err = rwbytes(fs, node, dirbuffer, fs->blocksize, bytes_done, false, true);
|
||||||
|
if (err)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ext2dent_t *dent = (ext2dent_t *)((uintptr_t)dirbuffer + block_offset);
|
||||||
|
|
||||||
if (namelen == dent->namelen && dent->inode && strncmp(name, dent->name, dent->namelen) == 0) {
|
if (namelen == dent->namelen && dent->inode && strncmp(name, dent->name, dent->namelen) == 0) {
|
||||||
inodefound = dent->inode;
|
inodefound = dent->inode;
|
||||||
if (switchnode) {
|
if (switchnode) {
|
||||||
dent->inode = switchnode->id;
|
dent->inode = switchnode->id;
|
||||||
dent->type = vfstoext2denttypetable[switchnode->vnode.type];
|
dent->type = vfstoext2denttypetable[switchnode->vnode.type];
|
||||||
err = rwbytes(fs, node, dent, dent->size, offset, true, true);
|
err = rwbytes(fs, node, dent, dent->size, bytes_done, true, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
__assert(dent->size);
|
__assert(dent->size);
|
||||||
offset += dent->size;
|
bytes_done += dent->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inodefound == 0)
|
if (inodefound == 0)
|
||||||
@@ -917,9 +922,7 @@ static int findindir(ext2fs_t *fs, ext2node_t *node, char *name, int *inode, ext
|
|||||||
*inode = inodefound;
|
*inode = inodefound;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (dirbuffer)
|
free(dirbuffer);
|
||||||
vmm_unmap(dirbuffer, INODE_SIZE(&node->inode) + 1, 0);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -936,21 +939,23 @@ static int insertdent(ext2fs_t *fs, ext2node_t *node, char *name, int inode, int
|
|||||||
memcpy(dentbuffer->name, name, namelen);
|
memcpy(dentbuffer->name, name, namelen);
|
||||||
|
|
||||||
size_t inodesize = INODE_SIZE(&node->inode);
|
size_t inodesize = INODE_SIZE(&node->inode);
|
||||||
// XXX loading the whole dir into memory isn't the best idea
|
void *dirbuffer = alloc(fs->blocksize);
|
||||||
void *dirbuffer = vmm_map(NULL, inodesize + 1, VMM_FLAGS_ALLOCATE, BUFFER_MAP_FLAGS, NULL); // + 1 to make sure there is at least one page mapped
|
|
||||||
int err = dirbuffer ? 0 : ENOMEM;
|
int err = dirbuffer ? 0 : ENOMEM;
|
||||||
if (err)
|
if (err)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
err = rwbytes(fs, node, dirbuffer, inodesize, 0, false, true);
|
|
||||||
if (err)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
ext2dent_t *splitdent = NULL;
|
ext2dent_t *splitdent = NULL;
|
||||||
uintmax_t offset = 0;
|
uintmax_t offset = 0;
|
||||||
size_t truesize, freesize;
|
size_t truesize, freesize;
|
||||||
while (offset < inodesize) {
|
while (offset < inodesize) {
|
||||||
splitdent = (ext2dent_t *)((uintptr_t)dirbuffer + offset);
|
size_t block_offset = offset % fs->blocksize;
|
||||||
|
if (block_offset == 0) {
|
||||||
|
err = rwbytes(fs, node, dirbuffer, fs->blocksize, offset, false, true);
|
||||||
|
if (err)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
splitdent = (ext2dent_t *)((uintptr_t)dirbuffer + block_offset);
|
||||||
truesize = splitdent->inode == 0 ? 0 : ROUND_UP(sizeof(ext2dent_t) + splitdent->namelen, 4);
|
truesize = splitdent->inode == 0 ? 0 : ROUND_UP(sizeof(ext2dent_t) + splitdent->namelen, 4);
|
||||||
freesize = splitdent->size - truesize;
|
freesize = splitdent->size - truesize;
|
||||||
|
|
||||||
@@ -980,30 +985,34 @@ static int insertdent(ext2fs_t *fs, ext2node_t *node, char *name, int inode, int
|
|||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (dirbuffer)
|
if (dirbuffer)
|
||||||
vmm_unmap(dirbuffer, inodesize + 1, 0);
|
free(dirbuffer);
|
||||||
free(dentbuffer);
|
free(dentbuffer);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO defrag dents in block
|
||||||
static int removedent(ext2fs_t *fs, ext2node_t *node, char *name, int *inode) {
|
static int removedent(ext2fs_t *fs, ext2node_t *node, char *name, int *inode) {
|
||||||
// XXX loading the whole dir into memory at once isn't the best idea but works for now
|
void *dirbuffer = alloc(fs->blocksize);
|
||||||
void *dirbuffer = vmm_map(NULL, INODE_SIZE(&node->inode) + 1, VMM_FLAGS_ALLOCATE, BUFFER_MAP_FLAGS, NULL); // + 1 to make sure there is at least one page mapped
|
|
||||||
if (dirbuffer == NULL)
|
if (dirbuffer == NULL)
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
|
|
||||||
int err = rwbytes(fs, node, dirbuffer, INODE_SIZE(&node->inode), 0, false, true);
|
|
||||||
if (err)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
uintmax_t offset = 0;
|
uintmax_t offset = 0;
|
||||||
int inodefound = 0;
|
int inodefound = 0;
|
||||||
size_t namelen = strlen(name);
|
size_t namelen = strlen(name);
|
||||||
uintmax_t lastsize = 0;
|
uintmax_t lastsize = 0;
|
||||||
ext2dent_t *dent = NULL;
|
ext2dent_t *dent = NULL;
|
||||||
|
int err;
|
||||||
|
|
||||||
// iterate through directory to find the inode number and dent offset
|
// iterate through directory to find the inode number and dent offset
|
||||||
while (offset < INODE_SIZE(&node->inode)) {
|
while (offset < INODE_SIZE(&node->inode)) {
|
||||||
dent = (ext2dent_t *)((uintptr_t)dirbuffer + offset);
|
size_t block_offset = offset % fs->blocksize;
|
||||||
|
if (block_offset == 0) {
|
||||||
|
err = rwbytes(fs, node, dirbuffer, fs->blocksize, offset, false, true);
|
||||||
|
if (err)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
dent = (ext2dent_t *)((uintptr_t)dirbuffer + block_offset);
|
||||||
|
|
||||||
if (namelen == dent->namelen && dent->inode && strncmp(name, dent->name, dent->namelen) == 0) {
|
if (namelen == dent->namelen && dent->inode && strncmp(name, dent->name, dent->namelen) == 0) {
|
||||||
inodefound = dent->inode;
|
inodefound = dent->inode;
|
||||||
@@ -1033,8 +1042,7 @@ static int removedent(ext2fs_t *fs, ext2node_t *node, char *name, int *inode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (dirbuffer)
|
free(dirbuffer);
|
||||||
vmm_unmap(dirbuffer, INODE_SIZE(&node->inode) + 1, 0);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1255,15 +1263,9 @@ static int ext2_getdents(vnode_t *vnode, dent_t *buffer, size_t count, uintmax_t
|
|||||||
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
// XXX loading the whole dir into memory at once isn't the best idea but works for now
|
// XXX loading the whole dir into memory at once isn't the best idea but works for now
|
||||||
void *dirbuffer = vmm_map(NULL, INODE_SIZE(&node->inode) + 1, VMM_FLAGS_ALLOCATE, BUFFER_MAP_FLAGS, NULL); // + 1 to make sure there is at least one page mapped
|
void *dirbuffer = alloc(fs->blocksize);
|
||||||
if (dirbuffer == NULL) {
|
if (dirbuffer == NULL)
|
||||||
err = ENOMEM;
|
return ENOMEM;
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = rwbytes(fs, node, dirbuffer, INODE_SIZE(&node->inode), 0, false, true);
|
|
||||||
if (err)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
uintmax_t currdiroffset = 0;
|
uintmax_t currdiroffset = 0;
|
||||||
uintmax_t offset = 0;
|
uintmax_t offset = 0;
|
||||||
@@ -1271,7 +1273,14 @@ static int ext2_getdents(vnode_t *vnode, dent_t *buffer, size_t count, uintmax_t
|
|||||||
|
|
||||||
// iterate through directory and build dent entries
|
// iterate through directory and build dent entries
|
||||||
while (offset < INODE_SIZE(&node->inode) && i < count) {
|
while (offset < INODE_SIZE(&node->inode) && i < count) {
|
||||||
ext2dent_t *dent = (ext2dent_t *)((uintptr_t)dirbuffer + offset);
|
size_t block_offset = offset % fs->blocksize;
|
||||||
|
if (block_offset == 0) {
|
||||||
|
err = rwbytes(fs, node, dirbuffer, fs->blocksize, offset, false, true);
|
||||||
|
if (err)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ext2dent_t *dent = (ext2dent_t *)((uintptr_t)dirbuffer + block_offset);
|
||||||
|
|
||||||
if (dent->inode && currdiroffset++ >= diroffset) {
|
if (dent->inode && currdiroffset++ >= diroffset) {
|
||||||
buffer[i].d_ino = dent->inode;
|
buffer[i].d_ino = dent->inode;
|
||||||
@@ -1289,8 +1298,7 @@ static int ext2_getdents(vnode_t *vnode, dent_t *buffer, size_t count, uintmax_t
|
|||||||
*readcount = i;
|
*readcount = i;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (dirbuffer)
|
free(dirbuffer);
|
||||||
vmm_unmap(dirbuffer, INODE_SIZE(&node->inode) + 1, 0);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user