#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; }