X-Git-Url: https://scm.lunaixsky.com/lunaix-os.git/blobdiff_plain/baca54322c66983205edecd2ebb00d997878be50..270869139db617e29a35bb9ded41087bb702f9ac:/lunaix-os/kernel/fs/ext2/file.c?ds=inline diff --git a/lunaix-os/kernel/fs/ext2/file.c b/lunaix-os/kernel/fs/ext2/file.c new file mode 100644 index 0000000..0fe4d3d --- /dev/null +++ b/lunaix-os/kernel/fs/ext2/file.c @@ -0,0 +1,313 @@ +#include +#include +#include "ext2.h" + +#define blkpos(e_sb, fpos) ((fpos) / (e_sb)->block_size) +#define blkoff(e_sb, fpos) ((fpos) % (e_sb)->block_size) + +int +ext2_open_inode(struct v_inode* inode, struct v_file* file) +{ + int errno = 0; + struct ext2_file* e_file; + + e_file = valloc(sizeof(*e_file)); + e_file->b_ino = EXT2_INO(inode); + + file->data = e_file; + + if (check_directory_node(inode)) { + errno = ext2dr_open(inode, file); + goto done; + } + + // XXX anything for regular file? + +done: + if (!errno) { + return 0; + } + + vfree(e_file); + file->data = NULL; + return errno; +} + +int +ext2_close_inode(struct v_file* file) +{ + ext2ino_update(file->inode); + + if (check_directory_node(file->inode)) { + ext2dr_close(file->inode, file); + } + + vfree(file->data); + file->data = NULL; + return 0; +} + +int +ext2_sync_inode(struct v_inode* inode) +{ + // TODO + // a modification to an inode may involves multiple + // blkbuf scattering among different groups. + // For now, we just sync everything, until we figure out + // a way to track each dirtied blkbuf w.r.t inode + ext2ino_resizing(inode, inode->fsize); + blkbuf_syncall(inode->sb->blks, false); + + return 0; +} + +int +ext2_file_sync(struct v_file* file) +{ + return ext2_sync_inode(file->inode); +} + +int +ext2_seek_inode(struct v_file* file, size_t offset) +{ + if (check_directory_node(file->inode)) { + return ext2dr_seek(file, offset); + } + + // nothing to do, seek on file pos handled by vfs + return 0; +} + +int +ext2_inode_read(struct v_inode *inode, + void *buffer, size_t len, size_t fpos) +{ + struct ext2_sbinfo* e_sb; + struct ext2_iterator iter; + struct ext2b_inode* b_ino; + struct ext2_inode* e_ino; + unsigned int off; + unsigned int end; + unsigned int sz = 0, blksz, movsz; + + e_sb = EXT2_SB(inode->sb); + e_ino = EXT2_INO(inode); + b_ino = e_ino->ino; + blksz = e_sb->block_size; + end = fpos + len; + + ext2db_itbegin(&iter, inode); + ext2db_itffw(&iter, fpos / blksz); + + while (fpos < end && ext2db_itnext(&iter)) { + off = fpos % blksz; + movsz = MIN(end - fpos, blksz - off); + + memcpy(buffer, offset(iter.data, off), movsz); + + buffer = offset(buffer, movsz); + fpos += movsz; + sz += movsz; + } + + ext2db_itend(&iter); + return itstate_sel(&iter, MIN(sz, e_ino->isize)); +} + +int +ext2_inode_read_page(struct v_inode *inode, void *buffer, size_t fpos) +{ + struct ext2_sbinfo* e_sb; + struct ext2_iterator iter; + struct ext2_inode* e_ino; + struct ext2b_inode* b_ino; + unsigned int blk_start, n, + transfer_sz, total_sz = 0; + + assert(!va_offset(fpos)); + + e_sb = EXT2_SB(inode->sb); + e_ino = EXT2_INO(inode); + b_ino = e_ino->ino; + + blk_start = fpos / e_sb->block_size; + n = PAGE_SIZE / e_sb->block_size; + transfer_sz = MIN(PAGE_SIZE, e_sb->block_size); + + ext2db_itbegin(&iter, inode); + ext2db_itffw(&iter, blk_start); + + while (n-- && ext2db_itnext(&iter)) + { + memcpy(buffer, iter.data, transfer_sz); + buffer = offset(buffer, transfer_sz); + total_sz += transfer_sz; + } + + ext2db_itend(&iter); + return itstate_sel(&iter, MIN(total_sz, e_ino->isize)); +} + +int +ext2_inode_write(struct v_inode *inode, + void *buffer, size_t len, size_t fpos) +{ + int errno; + unsigned int acc, blk_off, end, size; + struct ext2_sbinfo* e_sb; + bbuf_t buf; + + e_sb = EXT2_SB(inode->sb); + + acc = 0; + end = fpos + len; + while (fpos < end) { + errno = ext2db_acquire(inode, blkpos(e_sb, fpos), &buf); + if (errno) { + return errno; + } + + blk_off = blkoff(e_sb, fpos); + size = e_sb->block_size - blk_off; + + memcpy(offset(blkbuf_data(buf), blk_off), buffer, size); + buffer = offset(buffer, size); + + fsblock_dirty(buf); + fsblock_put(buf); + + fpos += size; + acc += size; + } + + return (int)acc; +} + +int +ext2_inode_write_page(struct v_inode *inode, void *buffer, size_t fpos) +{ + return ext2_inode_write(inode, buffer, PAGE_SIZE, fpos); +} + +#define SYMLNK_INPLACE \ + sizeof(((struct ext2b_inode*)0)->i_block_arr) + +static inline int +__readlink_symlink(struct v_inode *this, char* path) +{ + size_t size; + char* link = NULL; + int errno; + bbuf_t buf; + struct ext2_inode* e_ino; + + e_ino = EXT2_INO(this); + size = e_ino->isize; + if (size <= SYMLNK_INPLACE) { + link = (char*) e_ino->ino->i_block_arr; + strncpy(path, link, size); + } + else { + buf = ext2db_get(this, 0); + if (blkbuf_errbuf(buf)) { + return EIO; + } + + link = blkbuf_data(buf); + strncpy(path, link, size); + + fsblock_put(buf); + } + + return 0; +} + +int +ext2_get_symlink(struct v_inode *this, const char **path_out) +{ + int errno; + size_t size; + char* symlink; + struct ext2_inode* e_ino; + + e_ino = EXT2_INO(this); + size = e_ino->isize; + + if (!size) { + return ENOENT; + } + + if (!e_ino->symlink) { + symlink = valloc(size); + if ((errno = __readlink_symlink(this, symlink))) { + vfree(symlink); + return errno; + } + + e_ino->symlink = symlink; + } + + *path_out = e_ino->symlink; + + return size; +} + +int +ext2_set_symlink(struct v_inode *this, const char *target) +{ + int errno = 0; + bbuf_t buf = NULL; + char* link; + size_t size, new_len; + struct ext2_inode* e_ino; + + e_ino = EXT2_INO(this); + size = e_ino->isize; + new_len = strlen(target); + + if (new_len > this->sb->blksize) { + return ENAMETOOLONG; + } + + if (size != new_len) { + vfree_safe(e_ino->symlink); + e_ino->symlink = valloc(new_len); + } + + link = (char*) e_ino->ino->i_block_arr; + + // if new size is shrinked to inplace range + if (size > SYMLNK_INPLACE && new_len <= SYMLNK_INPLACE) + { + ext2db_free_pos(this, 0); + } + + // if new size is too big to fit inpalce + if (new_len > SYMLNK_INPLACE) { + + // repurpose the i_block array back to normal + if (size <= SYMLNK_INPLACE) { + memset(link, 0, SYMLNK_INPLACE); + } + + errno = ext2db_acquire(this, 0, &buf); + if (errno) { + goto done; + } + + link = blkbuf_data(buf); + } + + strncpy(e_ino->symlink, target, new_len); + strncpy(link, target, new_len); + + ext2ino_update(this); + ext2ino_resizing(this, new_len); + + if (buf) { + fsblock_put(buf); + } + +done: + return errno; +} \ No newline at end of file