diff --git a/include/sys/fs/ext2/block.h b/include/sys/fs/ext2/block.h index e799e9a..5ace354 100644 --- a/include/sys/fs/ext2/block.h +++ b/include/sys/fs/ext2/block.h @@ -12,3 +12,6 @@ int ext2_read_block(struct fs *ext2, void *blk, uint32_t no); int ext2_read_inode_block(struct fs *ext2, struct ext2_inode *inode, void *block, uint32_t index); int ext2_read_inode(struct fs *ext2, struct ext2_inode *dst, uint32_t ino); +// NOTE: this function automatically updates access and modification time +// on the inode written +int ext2_write_inode(struct fs *ext2, struct ext2_inode *node, uint32_t ino); diff --git a/sys/vfs/ext2/block.c b/sys/vfs/ext2/block.c index 61983d2..8c5a625 100644 --- a/sys/vfs/ext2/block.c +++ b/sys/vfs/ext2/block.c @@ -30,6 +30,18 @@ int ext2_read_block(struct fs *fs, void *block, uint32_t no) { return res; } +int ext2_write_block(struct fs *fs, const void *block, uint32_t no) { + struct ext2_data *data = fs->fs_private; + _assert(data); + kdebug("Write block %u: %p\n", no, no * data->block_size); + int res = blk_write(fs->blk, block, no * data->block_size, data->block_size); + if (res == 1024) { + return 0; + } + kerror("Failed to write block %u: %s\n", no, kstrerror(res)); + return res; +} + int ext2_read_inode(struct fs *fs, struct ext2_inode *inode, uint32_t ino) { struct ext2_data *data = fs->fs_private; _assert(data); @@ -55,6 +67,35 @@ int ext2_read_inode(struct fs *fs, struct ext2_inode *inode, uint32_t ino) { return 0; } +int ext2_write_inode(struct fs *fs, struct ext2_inode *inode, uint32_t ino) { + // Automatically update inode times + inode->mtime = time(); + inode->atime = inode->mtime; + + struct ext2_data *data = fs->fs_private; + _assert(data); + char block[data->block_size]; + if (ino < 1 || ino >= data->sb.inode_count) { + panic("Invalid inode number: %u\n", ino); + } + int res; + + --ino; + uint32_t ino_group = ino / data->sb.block_group_inodes; + uint32_t ino_in_group = ino % data->sb.block_group_inodes; + uint32_t ino_block = data->bgdt[ino_group].inode_table_no + ino_in_group / data->inodes_per_block; + uint32_t offset_in_block = (ino_in_group % data->inodes_per_block) * data->inode_size; + _assert(offset_in_block < data->block_size); + + if ((res = ext2_read_block(fs, block, ino_block)) != 0) { + return res; + } + + memcpy(offset_in_block + block, inode, data->inode_size); + + return ext2_write_block(fs, block, ino_block); +} + int ext2_read_inode_block(struct fs *fs, struct ext2_inode *inode, void *buf, uint32_t index) { if (index < 12) { return ext2_read_block(fs, buf, inode->direct_blocks[index]); diff --git a/sys/vfs/ext2/node.c b/sys/vfs/ext2/node.c index 4f85c0b..e5ab340 100644 --- a/sys/vfs/ext2/node.c +++ b/sys/vfs/ext2/node.c @@ -5,6 +5,7 @@ #include "sys/fs/node.h" #include "sys/string.h" #include "sys/assert.h" +#include "sys/fcntl.h" #include "sys/fs/fs.h" #include "sys/panic.h" #include "sys/debug.h" @@ -13,7 +14,10 @@ static int ext2_vnode_find(struct vnode *at, const char *name, struct vnode **res); static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count); +static int ext2_vnode_open(struct ofile *fd, int opt); static int ext2_vnode_opendir(struct ofile *fd); +static int ext2_vnode_chmod(struct vnode *vn, mode_t new_mode); +static int ext2_vnode_chown(struct vnode *node, uid_t new_uid, gid_t new_gid); static ssize_t ext2_vnode_readdir(struct ofile *fd, struct dirent *ent); static int ext2_vnode_stat(struct vnode *at, struct stat *st); @@ -26,7 +30,10 @@ struct vnode_operations g_ext2_vnode_ops = { .readdir = ext2_vnode_readdir, .stat = ext2_vnode_stat, + .chmod = ext2_vnode_chmod, + .chown = ext2_vnode_chown, + .open = ext2_vnode_open, .read = ext2_vnode_read, }; @@ -88,6 +95,22 @@ static int ext2_vnode_find(struct vnode *at, const char *name, struct vnode **re return -ENOENT; } +static int ext2_vnode_open(struct ofile *fd, int opt) { + if ((opt & O_APPEND) && (opt & O_ACCMODE) == O_RDONLY) { + // Impossible, I guess + return -EINVAL; + } + if ((opt & O_ACCMODE) != O_RDONLY) { + // Not implemented + return -EROFS; + } + _assert(!(opt & O_DIRECTORY)); + + fd->pos = 0; + + return 0; +} + static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count) { _assert(fd); _assert(buf); @@ -209,6 +232,45 @@ static ssize_t ext2_vnode_readdir(struct ofile *fd, struct dirent *ent) { return ent->d_reclen; } +static int ext2_vnode_chmod(struct vnode *node, mode_t new_mode) { + _assert(node); + struct ext2_inode *inode = node->fs_data; + _assert(inode); + struct fs *ext2 = node->fs; + _assert(ext2); + + // Only rewrite inode if something really changed + if ((inode->mode & 0xFFF) != (new_mode & 0xFFF)) { + // chmod() only updates file mode, don't touch file type + inode->mode &= ~0xFFF; + inode->mode |= new_mode & 0xFFF; + node->mode = new_mode & 0xFFF; + + return ext2_write_inode(ext2, inode, node->ino); + } + + return 0; +} + +static int ext2_vnode_chown(struct vnode *node, uid_t new_uid, gid_t new_gid) { + _assert(node); + struct ext2_inode *inode = node->fs_data; + _assert(inode); + struct fs *ext2 = node->fs; + _assert(ext2); + + if (new_uid != inode->uid || new_gid != inode->gid) { + inode->uid = new_uid; + inode->gid = new_gid; + node->uid = new_uid; + node->gid = new_gid; + + return ext2_write_inode(ext2, inode, node->ino); + } + + return 0; +} + static int ext2_vnode_stat(struct vnode *node, struct stat *st) { _assert(node && st); struct ext2_inode *inode = node->fs_data; @@ -262,7 +324,7 @@ void ext2_inode_to_vnode(struct vnode *vnode, struct ext2_inode *inode, uint32_t vnode->type = VN_REG; break; default: - panic("Unsupported inode type: %04x\n", inode->mode); + panic("Unsupported inode type: %04x\n", inode->mode & 0xF000); } }