ext2: inode set block, inode resize, ext2_write and ext2_resize

This commit is contained in:
Matheus
2023-07-24 19:43:44 -03:00
parent 98c642c6ec
commit 49682e2bf4
+251 -4
View File
@@ -90,7 +90,7 @@ typedef struct {
uint32_t dtime;
uint16_t gid;
uint16_t links;
uint32_t physicalsectorcount;
uint32_t sectcount; // in 512 byte blocks
uint32_t flags;
uint32_t osvalue;
uint32_t directpointer[12];
@@ -116,6 +116,13 @@ typedef struct {
#define INODE_TYPEPERM_PERM(x) ((x) & 0xfff)
#define INODE_TYPEPERM_TYPE(x) (((x) >> 12) & 0xf)
#define INODE_SIZE(x) (INODE_TYPEPERM_TYPE((x)->typeperm) == INODE_TYPE_DIR ? (x)->sizelow : (((uint64_t)(x)->sizehigh << 32) | (uint64_t)(x)->sizelow))
#define INODE_SETSIZE(x, size) { \
(x)->sizelow = size & 0xffffffff; \
if (INODE_TYPEPERM_TYPE((x)->typeperm) != INODE_TYPE_DIR) \
(x)->sizehigh = (size >> 32) & 0xffffffff; \
}
#define INODE_SECTSIZE 512
#define EXT2NODE_INIT(vn, vop, f, t, v, i) \
VOP_INIT(&(vn)->vnode, vop, f, t, v); \
(vn)->id = i;
@@ -153,6 +160,7 @@ typedef uint32_t blockptr_t;
#define INODE_GETGROUP(fs, x) (((x) - 1) / (fs)->superblock.inodespergroup)
#define INODE_GETINDEX(fs, x) (((x) - 1) % (fs)->superblock.inodespergroup)
#define INODE_GETDISKOFFSET(fs, table, x) ((table) + INODE_GETINDEX(fs, x) * (fs)->superblock.inodesize)
#define INODE_SECTSPERBLOCK(fs) ((fs)->blocksize / INODE_SECTSIZE)
#define DESC_GETDISKOFFSET(fs, x) (BLOCK_GETDISKOFFSET(fs, (fs)->superblock.superblockstart + 1) + sizeof(blockgroupdesc_t) * (x))
#define BLOCKS_IN_INDIRECT(fs) ((fs)->blocksize / sizeof(blockptr_t))
@@ -452,6 +460,206 @@ static int getinodeblock(ext2fs_t *fs, ext2node_t *node, uintmax_t index, blockp
return vfs_read(fs->backing, block, sizeof(blockptr_t), BLOCK_GETDISKOFFSET(fs, singlyptr) + singlyoffset, &readc, 0);
}
static int allocandset(ext2fs_t *fs, ext2node_t *node, uintmax_t setoffset, blockptr_t *newvalue) {
uintmax_t block = 0;
int e = allocatestructure(fs, &block, false);
if (e)
return e;
blockptr_t blockptr = block;
size_t writec;
e = vfs_write(fs->backing, &blockptr, sizeof(blockptr_t), setoffset, &writec, 0);
if (e) {
freestructure(fs, block, false); // TODO in case of error set filesystem as unclean?
return e;
}
node->inode.sectcount += INODE_SECTSPERBLOCK(fs);
return e;
}
static int inodeallocateindirect(ext2fs_t *fs, ext2node_t *node, int indirect) {
uintmax_t v;
int e = allocatestructure(fs, &v, false);
if (e)
return e;
switch (indirect) {
case 1:
node->inode.singlypointer = v;
break;
case 2:
node->inode.doublypointer = v;
break;
case 3:
node->inode.triplypointer = v;
break;
}
node->inode.sectcount += INODE_SECTSPERBLOCK(fs);
return e;
}
static int setinodeblock(ext2fs_t *fs, ext2node_t *node, uintmax_t index, blockptr_t block) {
// no indirection needed
size_t blocksinindirect = BLOCKS_IN_INDIRECT(fs);
bool usedirect = index < 12;
bool usesingly = false;
bool usedoubly = false;
bool usetriply = false;
uintmax_t singly = -1;
uintmax_t doubly = -1;
uintmax_t directindex = index;
uintmax_t singlyindex = -1;
uintmax_t doublyindex = -1;
uintmax_t triplyindex = -1;
uintmax_t singlyoffset = -1;
uintmax_t doublyoffset = -1;
uintmax_t triplyoffset = -1;
if (usedirect == false) {
index -= 12;
usesingly = true;
singly = index / blocksinindirect;
singlyindex = index % blocksinindirect;
singlyoffset = singlyindex * sizeof(blockptr_t);
}
if (usesingly && singly > 0) {
singly -= 1;
usedoubly = true;
doubly = singly / blocksinindirect;
doublyindex = singly % blocksinindirect;
doublyoffset = doublyindex * sizeof(blockptr_t);
}
if (usedoubly && doubly > 0) {
doubly -= 1;
usetriply = true;
triplyindex = doubly % blocksinindirect;
triplyoffset = triplyindex * sizeof(blockptr_t);
}
blockptr_t oldblock = 0;
if (usedirect) {
oldblock = node->inode.directpointer[directindex];
node->inode.directpointer[directindex] = block;
} else {
size_t count;
blockptr_t doublyptr;
blockptr_t singlyptr;
int e = 0;
// get doublyptr
if (usetriply && node->inode.triplypointer == 0) {
// allocate triply and doubly
e = inodeallocateindirect(fs, node, 3);
if (e)
return e;
e = allocandset(fs, node, BLOCK_GETDISKOFFSET(fs, node->inode.triplypointer) + triplyoffset, &doublyptr);
if (e)
return e;
} else if (usetriply) {
// use already allocated triply
uintmax_t offset = BLOCK_GETDISKOFFSET(fs, node->inode.triplypointer) + triplyoffset;
e = vfs_read(fs->backing, &doublyptr, sizeof(blockptr_t), offset, &count, 0);
if (e)
return e;
// allocate doubly if needed
if (doublyptr == 0)
e = allocandset(fs, node, offset, &doublyptr);
if (e)
return e;
} else {
// no triply used, allocate doubly if needed
if (node->inode.doublypointer == 0)
e = inodeallocateindirect(fs, node, 2);
if (e)
return e;
doublyptr = node->inode.doublypointer;
}
// doubly is guaranteed to be allocated
// get singlyptr
if (usedoubly) {
uintmax_t offset = BLOCK_GETDISKOFFSET(fs, doublyptr) + doublyoffset;
e = vfs_read(fs->backing, &singlyptr, sizeof(blockptr_t), offset, &count, 0);
if (e)
return e;
// allocate singly if needed
if (singlyptr == 0)
e = allocandset(fs, node, offset, &singlyptr);
if (e)
return e;
} else {
// no doubly used, allocate singly if needed
if (node->inode.singlypointer == 0)
e = inodeallocateindirect(fs, node, 1);
if (e)
return e;
singlyptr = node->inode.singlypointer;
}
// singly guaranteed to be allocated past this point
// read old block and set new block
uintmax_t offset = BLOCK_GETDISKOFFSET(fs, singlyptr) + singlyoffset;
e = vfs_read(fs->backing, &oldblock, sizeof(blockptr_t), offset, &count, 0);
if (e)
return e;
e = vfs_write(fs->backing, &block, sizeof(blockptr_t), offset, &count, 0);
if (e)
return e;
}
if (oldblock) {
node->inode.sectcount -= INODE_SECTSPERBLOCK(fs);
freestructure(fs, oldblock, false); // TODO set as unclean in case of error?
}
if (block)
node->inode.sectcount += INODE_SECTSPERBLOCK(fs);
return writeinode(fs, &node->inode, node->id); // TODO set fs as unclean if an error happened here?
}
static int resizeinode(ext2fs_t *fs, ext2node_t *node, size_t newsize) {
size_t newblockcount = ROUND_UP(newsize, fs->blocksize) / fs->blocksize;
size_t currentblockcount = ROUND_UP(INODE_SIZE(&node->inode), fs->blocksize) / fs->blocksize;
if (newblockcount > currentblockcount) {
// grow
for (int i = currentblockcount; i < newblockcount; ++i) {
uintmax_t newblock;
int e = allocatestructure(fs, &newblock, false);
if (e)
return e;
e = setinodeblock(fs, node, i, newblock);
if (e)
return e; // TODO set as unclean?
}
} else if (newblockcount < currentblockcount) {
// shrink
for (int i = newblockcount; i < currentblockcount; ++i) {
int e = setinodeblock(fs, node, i, 0);
if (e)
return e;
}
}
INODE_SETSIZE(&node->inode, newsize);
writeinode(fs, &node->inode, node->id); // TODO if error mark fs as unclean
return 0;
}
static int rwblocks(ext2fs_t *fs, ext2node_t *node, void *buffer, size_t count, uintmax_t index, bool write) {
for (uintmax_t i = 0; i < count; ++i) {
void *bufferp = (void *)((uintptr_t)buffer + i * fs->blocksize);
@@ -784,6 +992,45 @@ static int ext2_read(vnode_t *vnode, void *buffer, size_t size, uintmax_t offset
return err;
}
int ext2_write(vnode_t *vnode, void *buffer, size_t size, uintmax_t offset, int flags, size_t *writec, cred_t *cred) {
ext2node_t *node = (ext2node_t *)vnode;
ext2fs_t *fs = (ext2fs_t *)vnode->vfs;
uintmax_t endoffset = offset + size;
VOP_LOCK(vnode);
int err = 0;
size_t inodesize = INODE_SIZE(&node->inode);
// overflow check
if (offset > endoffset)
endoffset = -1ll;
if (endoffset > inodesize) {
err = resizeinode(fs, node, endoffset);
if (err)
goto cleanup;
}
err = rwbytes(fs, node, buffer, size, offset, true);
*writec = err ? -1 : size;
if (err)
goto cleanup;
// TODO mtime
cleanup:
VOP_UNLOCK(vnode);
return err;
}
int ext2_resize(vnode_t *vnode, size_t newsize, cred_t *cred) {
ext2node_t *node = (ext2node_t *)vnode;
ext2fs_t *fs = (ext2fs_t *)vnode->vfs;
VOP_LOCK(vnode);
int e = resizeinode(fs, node, newsize);
VOP_UNLOCK(vnode);
return e;
}
// TODO move root variable to vfs
static int ext2_root(vfs_t *vfs, vnode_t **vnodep) {
ext2fs_t *fs = (ext2fs_t *)vfs;
@@ -908,7 +1155,6 @@ static vfsops_t vfsops = {
};
/*
int (*write)(vnode_t *node, void *buffer, size_t size, uintmax_t offset, int flags, size_t *writec, cred_t *cred);
int (*create)(vnode_t *parent, char *name, vattr_t *attr, int type, vnode_t **result, cred_t *cred);
int (*unlink)(vnode_t *node, char *name, cred_t *cred);
int (*link)(vnode_t *node, vnode_t *dir, char *name, cred_t *cred);
@@ -916,10 +1162,10 @@ static vfsops_t vfsops = {
int (*inactive)(vnode_t *node);
int (*mmap)(vnode_t *node, void *addr, uintmax_t offset, int flags, cred_t *cred);
int (*munmap)(vnode_t *node, void *addr, uintmax_t offset, int flags, cred_t *cred);
int (*resize)(vnode_t *node, size_t newsize, cred_t *cred);
*/
static vops_t vnops = {
.write = ext2_write,
.read = ext2_read,
.getdents = ext2_getdents,
.lookup = ext2_lookup,
@@ -929,7 +1175,8 @@ static vops_t vnops = {
.close = ext2_close,
.poll = vfs_pollstub,
.access = ext2_access,
.readlink = ext2_readlink
.readlink = ext2_readlink,
.resize = ext2_resize
};
void ext2_init() {