From 26336de7ff8df555658d8fbd3ecb51a6fcdf3034 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Fri, 30 Jan 2015 16:28:03 +0100 Subject: [PATCH] Split extfs frontends into their own files. --- ext/device.cpp | 2 + ext/extfs.cpp | 1256 +------------------------------------------- ext/extfs.h | 32 ++ ext/fsmarshall.cpp | 717 +++++++++++++++++++++++++ ext/fsmarshall.h | 33 ++ ext/fuse.cpp | 586 +++++++++++++++++++++ ext/fuse.h | 35 ++ 7 files changed, 1417 insertions(+), 1244 deletions(-) create mode 100644 ext/extfs.h create mode 100644 ext/fsmarshall.cpp create mode 100644 ext/fsmarshall.h create mode 100644 ext/fuse.cpp create mode 100644 ext/fuse.h diff --git a/ext/device.cpp b/ext/device.cpp index 7e451857..09c777e3 100644 --- a/ext/device.cpp +++ b/ext/device.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "block.h" #include "device.h" @@ -52,6 +53,7 @@ Device::~Device() Sync(); while ( mru_block ) delete mru_block; + close(fd); } Block* Device::GetBlock(uint32_t block_id) diff --git a/ext/extfs.cpp b/ext/extfs.cpp index fd261ad4..c5d3daad 100644 --- a/ext/extfs.cpp +++ b/ext/extfs.cpp @@ -20,10 +20,6 @@ *******************************************************************************/ -#define __STDC_CONSTANT_MACROS -#define __STDC_LIMIT_MACROS - -#include #include #include @@ -31,44 +27,16 @@ #include #include #include -#include -#include #include #include #include #include #include -#include -#include - -#if defined(__OFF_MAX) && !defined(OFF_MAX) -#define OFF_MAX __OFF_MAX -#endif #if defined(__sortix__) -#include -#endif - -#if defined(__sortix__) -#include -#include +#include "fsmarshall.h" #else -struct timespec timespec_make(time_t sec, long nsec) -{ - struct timespec ret; - ret.tv_sec = sec; - ret.tv_nsec = nsec; - return ret; -} -#endif - -#if defined(__linux__) -#define FUSE_USE_VERSION 26 -#include -#endif - -#if defined(__sortix__) -#include +#include "fuse.h" #endif #include "ext-constants.h" @@ -77,12 +45,10 @@ struct timespec timespec_make(time_t sec, long nsec) #include "blockgroup.h" #include "block.h" #include "device.h" +#include "extfs.h" #include "filesystem.h" #include "inode.h" #include "ioleast.h" -#include "util.h" - -static volatile bool should_terminate = false; const uint32_t EXT2_FEATURE_COMPAT_SUPPORTED = 0; const uint32_t EXT2_FEATURE_INCOMPAT_SUPPORTED = \ @@ -90,7 +56,7 @@ const uint32_t EXT2_FEATURE_INCOMPAT_SUPPORTED = \ const uint32_t EXT2_FEATURE_RO_COMPAT_SUPPORTED = \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE; -// TODO: Inode 0 is not legal, but a lot of functions here accept it! +// TODO: Inode 0 is not valid, but a lot of functions here accept it! mode_t HostModeFromExtMode(uint32_t extmode) { @@ -133,1080 +99,16 @@ void StatInode(Inode* inode, struct stat* st) st->st_uid = inode->UserId(); st->st_gid = inode->GroupId(); st->st_size = inode->Size(); - st->st_atim = timespec_make(inode->data->i_atime, 0); - st->st_ctim = timespec_make(inode->data->i_ctime, 0); - st->st_mtim = timespec_make(inode->data->i_mtime, 0); + st->st_atim.tv_sec = inode->data->i_atime; + st->st_atim.tv_nsec = 0; + st->st_ctim.tv_sec = inode->data->i_ctime; + st->st_ctim.tv_nsec = 0; + st->st_mtim.tv_sec = inode->data->i_mtime; + st->st_mtim.tv_nsec = 0; st->st_blksize = inode->filesystem->block_size; st->st_blocks = inode->data->i_blocks; } -#if defined(__sortix__) - -bool RespondData(int chl, const void* ptr, size_t count) -{ - return writeall(chl, ptr, count) == count; -} - -bool RespondHeader(int chl, size_t type, size_t size) -{ - struct fsm_msg_header hdr; - hdr.msgtype = type; - hdr.msgsize = size; - return RespondData(chl, &hdr, sizeof(hdr)); -} - -bool RespondMessage(int chl, unsigned int type, const void* ptr, size_t count) -{ - return RespondHeader(chl, type, count) && - RespondData(chl, ptr, count); -} - -bool RespondError(int chl, int errnum) -{ - struct fsm_resp_error body; - body.errnum = errnum; - //fprintf(stderr, "extfs: sending error %i (%s)\n", errnum, strerror(errnum)); - return RespondMessage(chl, FSM_RESP_ERROR, &body, sizeof(body)); -} - -bool RespondSuccess(int chl) -{ - struct fsm_resp_success body; - return RespondMessage(chl, FSM_RESP_SUCCESS, &body, sizeof(body)); -} - -bool RespondStat(int chl, struct stat* st) -{ - struct fsm_resp_stat body; - body.st = *st; - return RespondMessage(chl, FSM_RESP_STAT, &body, sizeof(body)); -} - -bool RespondSeek(int chl, off_t offset) -{ - struct fsm_resp_lseek body; - body.offset = offset; - return RespondMessage(chl, FSM_RESP_LSEEK, &body, sizeof(body)); -} - -bool RespondRead(int chl, const uint8_t* buf, size_t count) -{ - struct fsm_resp_read body; - body.count = count; - return RespondMessage(chl, FSM_RESP_READ, &body, sizeof(body)) && - RespondData(chl, buf, count); -} - -bool RespondReadlink(int chl, const uint8_t* buf, size_t count) -{ - struct fsm_resp_readlink body; - body.targetlen = count; - return RespondMessage(chl, FSM_RESP_READLINK, &body, sizeof(body)) && - RespondData(chl, buf, count); -} - -bool RespondWrite(int chl, size_t count) -{ - struct fsm_resp_write body; - body.count = count; - return RespondMessage(chl, FSM_RESP_WRITE, &body, sizeof(body)); -} - -bool RespondOpen(int chl, ino_t ino, mode_t type) -{ - struct fsm_resp_open body; - body.ino = ino; - body.type = type; - return RespondMessage(chl, FSM_RESP_OPEN, &body, sizeof(body)); -} - -bool RespondMakeDir(int chl, ino_t ino) -{ - struct fsm_resp_mkdir body; - body.ino = ino; - return RespondMessage(chl, FSM_RESP_MKDIR, &body, sizeof(body)); -} - -bool RespondReadDir(int chl, struct kernel_dirent* dirent) -{ - struct fsm_resp_readdirents body; - body.ino = dirent->d_ino; - body.type = dirent->d_type; - body.namelen = dirent->d_namlen; - return RespondMessage(chl, FSM_RESP_READDIRENTS, &body, sizeof(body)) && - RespondData(chl, dirent->d_name, dirent->d_namlen); -} - -void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs) -{ - (void) chl; - if ( fs->num_inodes <= msg->ino ) - return; - if ( Inode* inode = fs->GetInode((uint32_t) msg->ino) ) - inode->RemoteRefer(); -} - -void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs) -{ - (void) chl; - if ( fs->num_inodes <= msg->ino ) - return; - if ( Inode* inode = fs->GetInode((uint32_t) msg->ino) ) - inode->RemoteUnref(); -} - -void HandleSync(int chl, struct fsm_req_sync* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - inode->Sync(); - inode->Unref(); - RespondSuccess(chl); -} - -void HandleStat(int chl, struct fsm_req_stat* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - struct stat st; - StatInode(inode, &st); - inode->Unref(); - RespondStat(chl, &st); -} - -void HandleChangeMode(int chl, struct fsm_req_chmod* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - uint32_t req_mode = ExtModeFromHostMode(msg->mode); - uint32_t old_mode = inode->Mode(); - uint32_t new_mode = (old_mode & ~S_SETABLE) | (req_mode & S_SETABLE); - inode->SetMode(new_mode); - inode->Unref(); - RespondSuccess(chl); -} - -void HandleChangeOwner(int chl, struct fsm_req_chown* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - inode->SetUserId((uint32_t) msg->uid); - inode->SetGroupId((uint32_t) msg->gid); - inode->Unref(); - RespondSuccess(chl); -} - -void HandleUTimens(int chl, struct fsm_req_utimens* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - inode->BeginWrite(); - inode->data->i_atime = msg->times[0].tv_sec; - inode->data->i_mtime = msg->times[1].tv_sec; - inode->FinishWrite(); - inode->Unref(); - RespondSuccess(chl); -} - -void HandleTruncate(int chl, struct fsm_req_truncate* msg, Filesystem* fs) -{ - if( msg->size < 0 ) { RespondError(chl, EINVAL); return; } - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - inode->Truncate((uint64_t) msg->size); - inode->Unref(); - RespondSuccess(chl); -} - -void HandleSeek(int chl, struct fsm_req_lseek* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - if ( msg->whence == SEEK_SET ) - RespondSeek(chl, msg->offset); - else if ( msg->whence == SEEK_END ) - { - off_t inode_size = inode->Size(); - if ( (msg->offset < 0 && inode_size + msg->offset < 0) || - (0 <= msg->offset && OFF_MAX - inode_size < msg->offset) ) - RespondError(chl, EOVERFLOW); - else - RespondSeek(chl, msg->offset + inode_size); - } - else - RespondError(chl, EINVAL); - inode->Unref(); -} - -void HandleReadAt(int chl, struct fsm_req_pread* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - uint8_t* buf = (uint8_t*) malloc(msg->count); - if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; } - ssize_t amount = inode->ReadAt(buf, msg->count, msg->offset); - RespondRead(chl, buf, amount); - inode->Unref(); - free(buf); -} - -void HandleWriteAt(int chl, struct fsm_req_pread* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - const uint8_t* buf = (const uint8_t*) &msg[1]; - ssize_t amount = inode->WriteAt(buf, msg->count, msg->offset); - RespondWrite(chl, amount); - inode->Unref(); -} - -void HandleOpen(int chl, struct fsm_req_open* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); - if ( !inode ) { RespondError(chl, errno); return; } - - char* pathraw = (char*) &(msg[1]); - char* path = (char*) malloc(msg->namelen+1); - if ( !path ) - { - RespondError(chl, errno); - inode->Unref(); - return; - } - memcpy(path, pathraw, msg->namelen); - path[msg->namelen] = '\0'; - - Inode* result = inode->Open(path, msg->flags, ExtModeFromHostMode(msg->mode)); - - free(path); - inode->Unref(); - - if ( !result ) { RespondError(chl, errno); return; } - - RespondOpen(chl, result->inode_id, result->Mode() & S_IFMT); - result->Unref(); -} - -void HandleMakeDir(int chl, struct fsm_req_open* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); - if ( !inode ) { RespondError(chl, errno); return; } - - char* pathraw = (char*) &(msg[1]); - char* path = (char*) malloc(msg->namelen+1); - if ( !path ) - { - RespondError(chl, errno); - inode->Unref(); - return; - } - memcpy(path, pathraw, msg->namelen); - path[msg->namelen] = '\0'; - - Inode* result = inode->CreateDirectory(path, ExtModeFromHostMode(msg->mode)); - - free(path); - inode->Unref(); - - if ( !result ) { RespondError(chl, errno); return; } - - RespondMakeDir(chl, result->inode_id); - result->Unref(); -} - -void HandleReadDir(int chl, struct fsm_req_readdirents* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - if ( !S_ISDIR(inode->Mode()) ) - { - inode->Unref(); - RespondError(chl, ENOTDIR); - return; - } - union - { - struct kernel_dirent kernel_entry; - uint8_t padding[sizeof(struct kernel_dirent) + 256]; - }; - memset(&kernel_entry, 0, sizeof(kernel_entry)); - - uint64_t file_size = inode->Size(); - uint64_t offset = 0; - Block* block = NULL; - uint64_t block_id = 0; - while ( offset < file_size ) - { - uint64_t entry_block_id = offset / fs->block_size; - uint64_t entry_block_offset = offset % fs->block_size; - if ( block && block_id != entry_block_id ) - block->Unref(), - block = NULL; - if ( !block && !(block = inode->GetBlock(block_id = entry_block_id)) ) - { - inode->Unref(); - RespondError(chl, errno); - return; - } - const uint8_t* block_data = block->block_data + entry_block_offset; - const struct ext_dirent* entry = (const struct ext_dirent*) block_data; - if ( entry->inode && entry->name_len && !(msg->rec_num--) ) - { - kernel_entry.d_reclen = sizeof(kernel_entry) + entry->name_len; - kernel_entry.d_nextoff = 0; - kernel_entry.d_ino = entry->inode; - kernel_entry.d_dev = 0; - kernel_entry.d_type = 0; // TODO: Support this! - kernel_entry.d_namlen = entry->name_len; - memcpy(kernel_entry.d_name, entry->name, entry->name_len); - size_t dname_offset = offsetof(struct kernel_dirent, d_name); - padding[dname_offset + kernel_entry.d_namlen] = '\0'; - block->Unref(); - inode->Unref(); - RespondReadDir(chl, &kernel_entry); - return; - } - offset += entry->reclen; - } - if ( block ) - block->Unref(); - - kernel_entry.d_reclen = sizeof(kernel_entry); - RespondReadDir(chl, &kernel_entry); -} - -void HandleIsATTY(int chl, struct fsm_req_isatty* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - RespondError(chl, ENOTTY); - inode->Unref(); -} - -void HandleUnlink(int chl, struct fsm_req_unlink* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); - if ( !inode ) { RespondError(chl, errno); return; } - - char* pathraw = (char*) &(msg[1]); - char* path = (char*) malloc(msg->namelen+1); - if ( !path ) - { - RespondError(chl, errno); - inode->Unref(); - return; - } - memcpy(path, pathraw, msg->namelen); - path[msg->namelen] = '\0'; - - Inode* result = inode->Unlink(path, false); - free(path); - inode->Unref(); - - if ( !result ) { RespondError(chl, errno); return; } - - result->Unref(); - - RespondSuccess(chl); -} - -void HandleRemoveDir(int chl, struct fsm_req_unlink* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); - if ( !inode ) { RespondError(chl, errno); return; } - - char* pathraw = (char*) &(msg[1]); - char* path = (char*) malloc(msg->namelen+1); - if ( !path ) - { - RespondError(chl, errno); - inode->Unref(); - return; - } - memcpy(path, pathraw, msg->namelen); - path[msg->namelen] = '\0'; - - if ( inode->RemoveDirectory(path) ) - RespondSuccess(chl); - else - RespondError(chl, errno); - - free(path); - inode->Unref(); -} - -void HandleLink(int chl, struct fsm_req_link* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - if ( fs->num_inodes <= msg->linkino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); - if ( !inode ) { RespondError(chl, errno); return; } - Inode* dest = fs->GetInode((uint32_t) msg->linkino); - if ( !dest ) { inode->Unref(); RespondError(chl, errno); return; } - - char* pathraw = (char*) &(msg[1]); - char* path = (char*) malloc(msg->namelen+1); - if ( !path ) - { - RespondError(chl, errno); - inode->Unref(); - return; - } - memcpy(path, pathraw, msg->namelen); - path[msg->namelen] = '\0'; - - if ( inode->Link(path, dest, false) ) - RespondSuccess(chl); - else - RespondError(chl, errno); - - free(path); - dest->Unref(); - inode->Unref(); -} - -void HandleSymlink(int chl, struct fsm_req_symlink* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); - if ( !inode ) { RespondError(chl, errno); return; } - - char* dest_raw = (char*) &(msg[1]); - char* dest = (char*) malloc(msg->targetlen + 1); - if ( !dest ) - { - RespondError(chl, errno); - inode->Unref(); - return; - } - memcpy(dest, dest_raw, msg->targetlen); - dest[msg->targetlen] = '\0'; - - char* path_raw = (char*) dest_raw + msg->targetlen; - char* path = (char*) malloc(msg->namelen + 1); - if ( !path ) - { - RespondError(chl, errno); - inode->Unref(); - return; - } - memcpy(path, path_raw, msg->namelen); - path[msg->namelen] = '\0'; - - if ( inode->Symlink(path, dest) ) - RespondSuccess(chl); - else - RespondError(chl, errno); - - free(path); - free(dest); - inode->Unref(); -} - -void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); - if ( !inode ) { RespondError(chl, errno); return; } - if ( !EXT2_S_ISLNK(inode->Mode()) ) { inode->Unref(); RespondError(chl, EINVAL); return; } - size_t count = inode->Size(); - uint8_t* buf = (uint8_t*) malloc(count); - if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; } - ssize_t amount = inode->ReadAt(buf, count, 0); - RespondReadlink(chl, buf, amount); - inode->Unref(); - free(buf); -} - -void HandleRename(int chl, struct fsm_req_rename* msg, Filesystem* fs) -{ - if ( fs->num_inodes <= msg->olddirino ) { RespondError(chl, EBADF); return; } - if ( fs->num_inodes <= msg->newdirino ) { RespondError(chl, EBADF); return; } - - char* pathraw = (char*) &(msg[1]); - char* path = (char*) malloc(msg->oldnamelen+1 + msg->newnamelen+1); - if ( !path ) { RespondError(chl, errno); return; } - memcpy(path, pathraw, msg->oldnamelen); - path[msg->oldnamelen] = '\0'; - memcpy(path + msg->oldnamelen + 1, pathraw + msg->oldnamelen, msg->newnamelen); - path[msg->oldnamelen + 1 + msg->newnamelen] = '\0'; - - const char* oldname = path; - const char* newname = path + msg->oldnamelen + 1; - - Inode* olddir = fs->GetInode((uint32_t) msg->olddirino); - if ( !olddir ) { free(path); RespondError(chl, errno); return; } - Inode* newdir = fs->GetInode((uint32_t) msg->newdirino); - if ( !newdir ) { olddir->Unref(); free(path); RespondError(chl, errno); return; } - - if ( newdir->Rename(olddir, oldname, newname) ) - RespondSuccess(chl); - else - RespondError(chl, errno); - - newdir->Unref(); - olddir->Unref(); - free(path); -} - -void HandleIncomingMessage(int chl, struct fsm_msg_header* hdr, Filesystem* fs) -{ - typedef void (*handler_t)(int, void*, Filesystem*); - handler_t handlers[FSM_MSG_NUM] = { NULL }; - handlers[FSM_REQ_SYNC] = (handler_t) HandleSync; - handlers[FSM_REQ_STAT] = (handler_t) HandleStat; - handlers[FSM_REQ_CHMOD] = (handler_t) HandleChangeMode; - handlers[FSM_REQ_CHOWN] = (handler_t) HandleChangeOwner; - handlers[FSM_REQ_TRUNCATE] = (handler_t) HandleTruncate; - handlers[FSM_REQ_LSEEK] = (handler_t) HandleSeek; - handlers[FSM_REQ_PREAD] = (handler_t) HandleReadAt; - handlers[FSM_REQ_OPEN] = (handler_t) HandleOpen; - handlers[FSM_REQ_READDIRENTS] = (handler_t) HandleReadDir; - handlers[FSM_REQ_PWRITE] = (handler_t) HandleWriteAt; - handlers[FSM_REQ_ISATTY] = (handler_t) HandleIsATTY; - handlers[FSM_REQ_UTIMENS] = (handler_t) HandleUTimens; - handlers[FSM_REQ_MKDIR] = (handler_t) HandleMakeDir; - handlers[FSM_REQ_RMDIR] = (handler_t) HandleRemoveDir; - handlers[FSM_REQ_UNLINK] = (handler_t) HandleUnlink; - handlers[FSM_REQ_LINK] = (handler_t) HandleLink; - handlers[FSM_REQ_SYMLINK] = (handler_t) HandleSymlink; - handlers[FSM_REQ_READLINK] = (handler_t) HandleReadlink; - handlers[FSM_REQ_RENAME] = (handler_t) HandleRename; - // TODO: symlink - // TODO: readlink - handlers[FSM_REQ_REFER] = (handler_t) HandleRefer; - handlers[FSM_REQ_UNREF] = (handler_t) HandleUnref; - if ( FSM_MSG_NUM <= hdr->msgtype || !handlers[hdr->msgtype] ) - { - fprintf(stderr, "extfs: message type %zu not supported!\n", hdr->msgtype); - RespondError(chl, ENOTSUP); - return; - } - uint8_t* body = (uint8_t*) malloc(hdr->msgsize); - if ( !body ) - { - fprintf(stderr, "extfs: message of type %zu too large: %zu bytes\n", hdr->msgtype, hdr->msgsize); - RespondError(chl, errno); - return; - } - size_t amount = readall(chl, body, hdr->msgsize); - if ( amount < hdr->msgsize ) - { - fprintf(stderr, "extfs: incomplete message of type %zu: got %zi of %zu bytes\n", hdr->msgtype, amount, hdr->msgsize); - RespondError(chl, errno); - free(body); - return; - } - handlers[hdr->msgtype](chl, body, fs); - free(body); -} - -void AlarmHandler(int) -{ -} - -void TerminationHandler(int) -{ - should_terminate = true; -} - -#endif - -#if defined(__linux__) - -struct ext2_fuse_ctx -{ - Device* dev; - Filesystem* fs; -}; - -#ifndef S_SETABLE -#define S_SETABLE 02777 -#endif - -#define FUSE_FS (((struct ext2_fuse_ctx*) (fuse_get_context()->private_data))->fs) - -void* ext2_fuse_init(struct fuse_conn_info* /*conn*/) -{ - return fuse_get_context()->private_data; -} - -void ext2_fuse_destroy(void* fs_private) -{ - struct ext2_fuse_ctx* ext2_fuse_ctx = (struct ext2_fuse_ctx*) fs_private; - ext2_fuse_ctx->fs->Sync(); - ext2_fuse_ctx->dev->Sync(); - delete ext2_fuse_ctx->fs; ext2_fuse_ctx->fs = NULL; - delete ext2_fuse_ctx->dev; ext2_fuse_ctx->dev = NULL; -} - -Inode* ext2_fuse_resolve_path(const char* path) -{ - Filesystem* fs = FUSE_FS; - Inode* inode = fs->GetInode(EXT2_ROOT_INO); - assert(inode); - while ( path[0] ) - { - if ( *path == '/' ) - { - if ( !EXT2_S_ISDIR(inode->Mode()) ) - return errno = ENOTDIR, (Inode*) NULL; - path++; - continue; - } - size_t elem_len = strcspn(path, "/"); - char* elem = new char[elem_len+1]; - memcpy(elem, path, elem_len); - elem[elem_len] = '\0'; - path += elem_len; - Inode* next = inode->Open(elem, O_RDONLY, 0); - delete[] elem; - inode->Unref(); - if ( !next ) - return NULL; - inode = next; - } - return inode; -} - -// Assumes that the path doesn't end with / unless it's the root directory. -Inode* ext2_fuse_parent_dir(const char** path_ptr) -{ - const char* path = *path_ptr; - Filesystem* fs = FUSE_FS; - Inode* inode = fs->GetInode(EXT2_ROOT_INO); - assert(inode); - while ( strchr(path, '/') ) - { - if ( *path == '/' ) - { - if ( !EXT2_S_ISDIR(inode->Mode()) ) - return errno = ENOTDIR, (Inode*) NULL; - path++; - continue; - } - size_t elem_len = strcspn(path, "/"); - char* elem = new char[elem_len+1]; - memcpy(elem, path, elem_len); - elem[elem_len] = '\0'; - path += elem_len; - Inode* next = inode->Open(elem, O_RDONLY, 0); - delete[] elem; - inode->Unref(); - if ( !next ) - return NULL; - inode = next; - } - *path_ptr = *path ? path : "."; - assert(!strchr(*path_ptr, '/')); - return inode; -} - -int ext2_fuse_getattr(const char* path, struct stat* st) -{ - Inode* inode = ext2_fuse_resolve_path(path); - if ( !inode ) - return -errno; - StatInode(inode, st); - inode->Unref(); - return 0; -} - -int ext2_fuse_fgetattr(const char* /*path*/, struct stat* st, - struct fuse_file_info* fi) -{ - Filesystem* fs = FUSE_FS; - Inode* inode = fs->GetInode((uint32_t) fi->fh); - if ( !inode ) - return -errno; - StatInode(inode, st); - inode->Unref(); - return 0; -} - -int ext2_fuse_readlink(const char* path, char* buf, size_t bufsize) -{ - Inode* inode = ext2_fuse_resolve_path(path); - if ( !inode ) - return -errno; - if ( !EXT2_S_ISLNK(inode->Mode()) ) - return inode->Unref(), -(errno = EINVAL); - if ( !bufsize ) - return inode->Unref(), -(errno = EINVAL); - ssize_t amount = inode->ReadAt((uint8_t*) buf, bufsize, 0); - if ( amount < 0 ) - return inode->Unref(), -errno; - buf[(size_t) amount < bufsize ? (size_t) bufsize : bufsize - 1] = '\0'; - inode->Unref(); - return 0; -} - -int ext2_fuse_mknod(const char* path, mode_t mode, dev_t dev) -{ - (void) path; - (void) mode; - (void) dev; - return -(errno = ENOSYS); -} - -int ext2_fuse_mkdir(const char* path, mode_t mode) -{ - Inode* inode = ext2_fuse_parent_dir(&path); - if ( !inode ) - return -errno; - Inode* newdir = inode->CreateDirectory(path, ExtModeFromHostMode(mode)); - inode->Unref(); - if ( !newdir ) - return -errno; - newdir->Unref(); - return 0; -} - -int ext2_fuse_unlink(const char* path) -{ - Inode* inode = ext2_fuse_parent_dir(&path); - if ( !inode ) - return -errno; - Inode* result = inode->Unlink(path, false); - inode->Unref(); - if ( !result ) - return -errno; - result->Unref(); - return 0; -} - -int ext2_fuse_rmdir(const char* path) -{ - Inode* inode = ext2_fuse_parent_dir(&path); - if ( !inode ) - return -errno; - bool success = inode->RemoveDirectory(path); - inode->Unref(); - return success ? 0 : -errno; -} - -int ext2_fuse_symlink(const char* oldname, const char* newname) -{ - Inode* newdir = ext2_fuse_parent_dir(&newname); - if ( !newdir ) - return -errno; - bool success = newdir->Symlink(newname, oldname); - newdir->Unref(); - return success ? 0 : -errno; -} - -int ext2_fuse_rename(const char* oldname, const char* newname) -{ - Inode* olddir = ext2_fuse_parent_dir(&oldname); - if ( !olddir ) - return -errno; - Inode* newdir = ext2_fuse_parent_dir(&newname); - if ( !newdir ) - return olddir->Unref(), -errno; - bool success = newdir->Rename(olddir, oldname, newname); - newdir->Unref(); - olddir->Unref(); - return success ? 0 : -errno; -} - -int ext2_fuse_link(const char* oldname, const char* newname) -{ - Inode* inode = ext2_fuse_resolve_path(oldname); - if ( !inode ) - return -errno; - Inode* newdir = ext2_fuse_parent_dir(&newname); - if ( !newdir ) - return inode->Unref(), -errno; - bool success = inode->Link(newname, inode, false); - newdir->Unref(); - inode->Unref(); - return success ? 0 : -errno; -} - -int ext2_fuse_chmod(const char* path, mode_t mode) -{ - Inode* inode = ext2_fuse_resolve_path(path); - if ( !inode ) - return -errno; - uint32_t req_mode = ExtModeFromHostMode(mode); - uint32_t old_mode = inode->Mode(); - uint32_t new_mode = (old_mode & ~S_SETABLE) | (req_mode & S_SETABLE); - inode->SetMode(new_mode); - inode->Unref(); - return 0; -} - -int ext2_fuse_chown(const char* path, uid_t owner, gid_t group) -{ - Inode* inode = ext2_fuse_resolve_path(path); - if ( !inode ) - return -errno; - inode->SetUserId((uint32_t) owner); - inode->SetGroupId((uint32_t) group); - inode->Unref(); - return 0; -} - -int ext2_fuse_truncate(const char* path, off_t size) -{ - Inode* inode = ext2_fuse_resolve_path(path); - if ( !inode ) - return -errno; - inode->Truncate((uint64_t) size); - inode->Unref(); - return 0; -} - -int ext2_fuse_ftruncate(const char* /*path*/, off_t size, - struct fuse_file_info* fi) -{ - Filesystem* fs = FUSE_FS; - Inode* inode = fs->GetInode((uint32_t) fi->fh); - if ( !inode ) - return -errno; - inode->Truncate((uint64_t) size); - inode->Unref(); - return 0; -} - -int ext2_fuse_open(const char* path, struct fuse_file_info* fi) -{ - int flags = fi->flags; - Inode* dir = ext2_fuse_parent_dir(&path); - if ( !dir ) - return -errno; - Inode* result = dir->Open(path, flags, 0); - dir->Unref(); - if ( !result ) - return -errno; - fi->fh = (uint64_t) result->inode_id; - fi->keep_cache = 1; - result->RemoteRefer(); - result->Unref(); - return 0; -} - -int ext2_fuse_access(const char* path, int mode) -{ - Inode* dir = ext2_fuse_parent_dir(&path); - if ( !dir ) - return -errno; - Inode* result = dir->Open(path, O_RDONLY, 0); - dir->Unref(); - if ( !result ) - return -errno; - (void) mode; - result->Unref(); - return 0; -} - -int ext2_fuse_create(const char* path, mode_t mode, struct fuse_file_info* fi) -{ - int flags = fi->flags | O_CREAT; - Inode* inode = ext2_fuse_parent_dir(&path); - if ( !inode ) - return -errno; - Inode* result = inode->Open(path, flags, ExtModeFromHostMode(mode)); - inode->Unref(); - if ( !result ) - return -errno; - fi->fh = (uint64_t) result->inode_id; - fi->keep_cache = 1; - result->Unref(); - return 0; -} - -int ext2_fuse_opendir(const char* path, struct fuse_file_info* fi) -{ - return ext2_fuse_open(path, fi); -} - -int ext2_fuse_read(const char* /*path*/, char* buf, size_t count, off_t offset, - struct fuse_file_info* fi) -{ - Filesystem* fs = FUSE_FS; - if ( INT_MAX < count ) - count = INT_MAX; - Inode* inode = fs->GetInode((uint32_t) fi->fh); - if ( !inode ) - return -errno; - ssize_t result = inode->ReadAt((uint8_t*) buf, count, offset); - inode->Unref(); - return 0 <= result ? (int) result : -errno; -} - -int ext2_fuse_write(const char* /*path*/, const char* buf, size_t count, - off_t offset, struct fuse_file_info* fi) -{ - Filesystem* fs = FUSE_FS; - if ( INT_MAX < count ) - count = INT_MAX; - Inode* inode = fs->GetInode((uint32_t) fi->fh); - if ( !inode ) - return -errno; - ssize_t result = inode->WriteAt((const uint8_t*) buf, count, offset); - inode->Unref(); - return 0 <= result ? (int) result : -errno; -} - -int ext2_fuse_statfs(const char* /*path*/, struct statvfs* stvfs) -{ - (void) stvfs; - return errno = -ENOSYS, -1; -} - -int ext2_fuse_flush(const char* /*path*/, struct fuse_file_info* fi) -{ - Filesystem* fs = FUSE_FS; - Inode* inode = fs->GetInode((uint32_t) fi->fh); - if ( !inode ) - return -errno; - inode->Sync(); - inode->Unref(); - return 0; -} - -int ext2_fuse_release(const char* /*path*/, struct fuse_file_info* fi) -{ - Filesystem* fs = FUSE_FS; - Inode* inode = fs->GetInode((uint32_t) fi->fh); - if ( !inode ) - return -errno; - inode->RemoteUnref(); - inode->Unref(); - return 0; -} - -int ext2_fuse_releasedir(const char* path, struct fuse_file_info* fi) -{ - return ext2_fuse_release(path, fi); -} - -int ext2_fuse_fsync(const char* /*path*/, int data, struct fuse_file_info* fi) -{ - (void) data; - Filesystem* fs = FUSE_FS; - Inode* inode = fs->GetInode((uint32_t) fi->fh); - if ( !inode ) - return -errno; - inode->Sync(); - inode->Unref(); - return 0; -} - -/*int ext2_fuse_syncdir(const char* path, int data, struct fuse_file_info* fi) -{ - return ext2_fuse_sync(path, data, fi); -}*/ - -/*int ext2_fuse_setxattr(const char *, const char *, const char *, size_t, int) -{ - return -(errno = ENOSYS); -}*/ - -/*int ext2_fuse_getxattr(const char *, const char *, char *, size_t) -{ - return -(errno = ENOSYS); -}*/ - -/*int ext2_fuse_listxattr(const char *, char *, size_t) -{ - return -(errno = ENOSYS); -}*/ - -/*int ext2_fuse_removexattr(const char *, const char *) -{ - return -(errno = ENOSYS); -}*/ - -int ext2_fuse_readdir(const char* /*path*/, void* buf, fuse_fill_dir_t filler, - off_t rec_num, struct fuse_file_info* fi) -{ - Filesystem* fs = FUSE_FS; - Inode* inode = fs->GetInode((uint32_t) fi->fh); - if ( !inode ) - return -errno; - if ( !S_ISDIR(inode->Mode()) ) - return inode->Unref(), -(errno = ENOTDIR); - - uint64_t file_size = inode->Size(); - uint64_t offset = 0; - Block* block = NULL; - uint64_t block_id = 0; - while ( offset < file_size ) - { - uint64_t entry_block_id = offset / fs->block_size; - uint64_t entry_block_offset = offset % fs->block_size; - if ( block && block_id != entry_block_id ) - block->Unref(), - block = NULL; - if ( !block && !(block = inode->GetBlock(block_id = entry_block_id)) ) - return inode->Unref(), -errno; - const uint8_t* block_data = block->block_data + entry_block_offset; - const struct ext_dirent* entry = (const struct ext_dirent*) block_data; - if ( entry->inode && entry->name_len && (!rec_num || !rec_num--) ) - { - char* entry_name = new char[entry->name_len+1]; - memcpy(entry_name, entry->name, entry->name_len); - entry_name[entry->name_len] = '\0'; - bool full = filler(buf, entry_name, NULL, 0); - delete[] entry_name; - if ( full ) - { - block->Unref(); - inode->Unref(); - return 0; - } - } - offset += entry->reclen; - } - if ( block ) - block->Unref(); - - inode->Unref(); - return 0; -} - -/*int ext2_fuse_lock(const char*, struct fuse_file_info*, int, struct flock*) -{ - return -(errno = ENOSYS); -}*/ - -int ext2_fuse_utimens(const char* path, const struct timespec tv[2]) -{ - Inode* inode = ext2_fuse_resolve_path(path); - if ( !inode ) - return -errno; - inode->BeginWrite(); - inode->data->i_atime = tv[0].tv_sec; - inode->data->i_mtime = tv[1].tv_sec; - inode->FinishWrite(); - inode->Unref(); - return 0; -} - -/*int ext2_fuse_bmap(const char*, size_t blocksize, uint64_t* idx) -{ - return -(errno = ENOSYS); -}*/ - -#endif - static bool is_hex_digit(char c) { return ('0' <= c && c <= '9') || @@ -1480,142 +382,8 @@ int main(int argc, char* argv[]) return 0; #if defined(__sortix__) - // Stat the root inode. - struct stat root_inode_st; - Inode* root_inode = fs->GetInode((uint32_t) EXT2_ROOT_INO); - if ( !root_inode ) - error(1, errno, "GetInode(%u)", EXT2_ROOT_INO); - StatInode(root_inode, &root_inode_st); - root_inode->Unref(); - - // Create a filesystem server connected to the kernel that we'll listen on. - int serverfd = fsm_mountat(AT_FDCWD, mount_path, &root_inode_st, 0); - if ( serverfd < 0 ) - error(1, errno, "%s", mount_path); - - // Make sure the server isn't unexpectedly killed and data is lost. - signal(SIGINT, TerminationHandler); - signal(SIGTERM, TerminationHandler); - signal(SIGQUIT, TerminationHandler); - - // Become a background process in its own process group by default. - if ( !foreground ) - { - pid_t child_pid = fork(); - if ( child_pid < 0 ) - error(1, errno, "fork"); - if ( child_pid ) - exit(0); - setpgid(0, 0); - } - - // Listen for filesystem messages and sync the filesystem every few seconds. - struct timespec last_sync_at; - clock_gettime(CLOCK_MONOTONIC, &last_sync_at); - int channel; - while ( 0 <= (channel = accept(serverfd, NULL, NULL)) ) - { - if ( should_terminate ) - break; - struct fsm_msg_header hdr; - size_t amount; - if ( (amount = readall(channel, &hdr, sizeof(hdr))) != sizeof(hdr) ) - { - error(0, errno, "incomplete header: got %zi of %zu bytes", amount, sizeof(hdr)); - errno = 0; - continue; - } - HandleIncomingMessage(channel, &hdr, fs); - close(channel); - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - if ( write && 5 <= timespec_sub(now, last_sync_at).tv_sec ) - { - fs->Sync(); - last_sync_at = now; - } - } - - // Sync the filesystem before shutting down. - if ( write ) - { - fprintf(stderr, "%s: filesystem server shutting down, syncing...", argv0); - fflush(stderr); - fs->Sync(); - fprintf(stderr, " done.\n"); - } - - close(serverfd); - -#elif defined(__linux__) - - (void) foreground; - - struct fuse_operations operations; - memset(&operations, 0, sizeof(operations)); - - operations.access = ext2_fuse_access; - operations.chmod = ext2_fuse_chmod; - operations.chown = ext2_fuse_chown; - operations.create = ext2_fuse_create; - operations.destroy = ext2_fuse_destroy; - operations.fgetattr = ext2_fuse_fgetattr; - operations.flush = ext2_fuse_flush; - operations.fsync = ext2_fuse_fsync; - operations.ftruncate = ext2_fuse_ftruncate; - operations.getattr = ext2_fuse_getattr; - operations.init = ext2_fuse_init; - operations.link = ext2_fuse_link; - operations.mkdir = ext2_fuse_mkdir; - operations.mknod = ext2_fuse_mknod; - operations.opendir = ext2_fuse_opendir; - operations.open = ext2_fuse_open; - operations.readdir = ext2_fuse_readdir; - operations.read = ext2_fuse_read; - operations.readlink = ext2_fuse_readlink; - operations.releasedir = ext2_fuse_releasedir; - operations.release = ext2_fuse_release; - operations.rename = ext2_fuse_rename; - operations.rmdir = ext2_fuse_rmdir; - operations.statfs = ext2_fuse_statfs; - operations.symlink = ext2_fuse_symlink; - operations.truncate = ext2_fuse_truncate; - operations.unlink = ext2_fuse_unlink; - operations.utimens = ext2_fuse_utimens; - operations.write = ext2_fuse_write; - - operations.flag_nullpath_ok = 1; - operations.flag_nopath = 1; - - char* argv_fuse[] = - { - (char*) argv[0], - (char*) "-s", - (char*) mount_path, - (char*) NULL, - }; - - int argc_fuse = sizeof(argv_fuse) / sizeof(argv_fuse[0]) - 1; - - struct ext2_fuse_ctx ext2_fuse_ctx; - ext2_fuse_ctx.fs = fs; - ext2_fuse_ctx.dev = dev; - - return fuse_main(argc_fuse, argv_fuse, &operations, &ext2_fuse_ctx); - + return fsmarshall_main(argv0, mount_path, foreground, fs, dev); #else - - (void) foreground; - (void) mount_path; - + return ext2_fuse_main(argv0, mount_path, foreground, fs, dev); #endif - - delete fs; - delete dev; - - close(fd); - - return 0; } diff --git a/ext/extfs.h b/ext/extfs.h new file mode 100644 index 00000000..d8abaa9c --- /dev/null +++ b/ext/extfs.h @@ -0,0 +1,32 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2015. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . + + extfs.h + Implementation of the extended filesystem. + +*******************************************************************************/ + +#ifndef EXTFS_H +#define EXTFS_H + +class Inode; + +mode_t HostModeFromExtMode(uint32_t extmode); +uint32_t ExtModeFromHostMode(mode_t hostmode); +void StatInode(Inode* inode, struct stat* st); + +#endif diff --git a/ext/fsmarshall.cpp b/ext/fsmarshall.cpp new file mode 100644 index 00000000..01e45b1a --- /dev/null +++ b/ext/fsmarshall.cpp @@ -0,0 +1,717 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013, 2014, 2015. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . + + fsmarshall.cpp + Sortix fsmarshall frontend. + +*******************************************************************************/ + +#if defined(__sortix__) + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "ext-constants.h" +#include "ext-structs.h" + +#include "blockgroup.h" +#include "block.h" +#include "device.h" +#include "extfs.h" +#include "filesystem.h" +#include "fsmarshall.h" +#include "fuse.h" +#include "inode.h" + +bool RespondData(int chl, const void* ptr, size_t count) +{ + return writeall(chl, ptr, count) == count; +} + +bool RespondHeader(int chl, size_t type, size_t size) +{ + struct fsm_msg_header hdr; + hdr.msgtype = type; + hdr.msgsize = size; + return RespondData(chl, &hdr, sizeof(hdr)); +} + +bool RespondMessage(int chl, unsigned int type, const void* ptr, size_t count) +{ + return RespondHeader(chl, type, count) && + RespondData(chl, ptr, count); +} + +bool RespondError(int chl, int errnum) +{ + struct fsm_resp_error body; + body.errnum = errnum; + //fprintf(stderr, "extfs: sending error %i (%s)\n", errnum, strerror(errnum)); + return RespondMessage(chl, FSM_RESP_ERROR, &body, sizeof(body)); +} + +bool RespondSuccess(int chl) +{ + struct fsm_resp_success body; + return RespondMessage(chl, FSM_RESP_SUCCESS, &body, sizeof(body)); +} + +bool RespondStat(int chl, struct stat* st) +{ + struct fsm_resp_stat body; + body.st = *st; + return RespondMessage(chl, FSM_RESP_STAT, &body, sizeof(body)); +} + +bool RespondSeek(int chl, off_t offset) +{ + struct fsm_resp_lseek body; + body.offset = offset; + return RespondMessage(chl, FSM_RESP_LSEEK, &body, sizeof(body)); +} + +bool RespondRead(int chl, const uint8_t* buf, size_t count) +{ + struct fsm_resp_read body; + body.count = count; + return RespondMessage(chl, FSM_RESP_READ, &body, sizeof(body)) && + RespondData(chl, buf, count); +} + +bool RespondReadlink(int chl, const uint8_t* buf, size_t count) +{ + struct fsm_resp_readlink body; + body.targetlen = count; + return RespondMessage(chl, FSM_RESP_READLINK, &body, sizeof(body)) && + RespondData(chl, buf, count); +} + +bool RespondWrite(int chl, size_t count) +{ + struct fsm_resp_write body; + body.count = count; + return RespondMessage(chl, FSM_RESP_WRITE, &body, sizeof(body)); +} + +bool RespondOpen(int chl, ino_t ino, mode_t type) +{ + struct fsm_resp_open body; + body.ino = ino; + body.type = type; + return RespondMessage(chl, FSM_RESP_OPEN, &body, sizeof(body)); +} + +bool RespondMakeDir(int chl, ino_t ino) +{ + struct fsm_resp_mkdir body; + body.ino = ino; + return RespondMessage(chl, FSM_RESP_MKDIR, &body, sizeof(body)); +} + +bool RespondReadDir(int chl, struct kernel_dirent* dirent) +{ + struct fsm_resp_readdirents body; + body.ino = dirent->d_ino; + body.type = dirent->d_type; + body.namelen = dirent->d_namlen; + return RespondMessage(chl, FSM_RESP_READDIRENTS, &body, sizeof(body)) && + RespondData(chl, dirent->d_name, dirent->d_namlen); +} + +void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs) +{ + (void) chl; + if ( fs->num_inodes <= msg->ino ) + return; + if ( Inode* inode = fs->GetInode((uint32_t) msg->ino) ) + inode->RemoteRefer(); +} + +void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs) +{ + (void) chl; + if ( fs->num_inodes <= msg->ino ) + return; + if ( Inode* inode = fs->GetInode((uint32_t) msg->ino) ) + inode->RemoteUnref(); +} + +void HandleSync(int chl, struct fsm_req_sync* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + inode->Sync(); + inode->Unref(); + RespondSuccess(chl); +} + +void HandleStat(int chl, struct fsm_req_stat* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + struct stat st; + StatInode(inode, &st); + inode->Unref(); + RespondStat(chl, &st); +} + +void HandleChangeMode(int chl, struct fsm_req_chmod* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + uint32_t req_mode = ExtModeFromHostMode(msg->mode); + uint32_t old_mode = inode->Mode(); + uint32_t new_mode = (old_mode & ~S_SETABLE) | (req_mode & S_SETABLE); + inode->SetMode(new_mode); + inode->Unref(); + RespondSuccess(chl); +} + +void HandleChangeOwner(int chl, struct fsm_req_chown* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + inode->SetUserId((uint32_t) msg->uid); + inode->SetGroupId((uint32_t) msg->gid); + inode->Unref(); + RespondSuccess(chl); +} + +void HandleUTimens(int chl, struct fsm_req_utimens* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + inode->BeginWrite(); + inode->data->i_atime = msg->times[0].tv_sec; + inode->data->i_mtime = msg->times[1].tv_sec; + inode->FinishWrite(); + inode->Unref(); + RespondSuccess(chl); +} + +void HandleTruncate(int chl, struct fsm_req_truncate* msg, Filesystem* fs) +{ + if( msg->size < 0 ) { RespondError(chl, EINVAL); return; } + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + inode->Truncate((uint64_t) msg->size); + inode->Unref(); + RespondSuccess(chl); +} + +void HandleSeek(int chl, struct fsm_req_lseek* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + if ( msg->whence == SEEK_SET ) + RespondSeek(chl, msg->offset); + else if ( msg->whence == SEEK_END ) + { + off_t inode_size = inode->Size(); + if ( (msg->offset < 0 && inode_size + msg->offset < 0) || + (0 <= msg->offset && OFF_MAX - inode_size < msg->offset) ) + RespondError(chl, EOVERFLOW); + else + RespondSeek(chl, msg->offset + inode_size); + } + else + RespondError(chl, EINVAL); + inode->Unref(); +} + +void HandleReadAt(int chl, struct fsm_req_pread* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + uint8_t* buf = (uint8_t*) malloc(msg->count); + if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; } + ssize_t amount = inode->ReadAt(buf, msg->count, msg->offset); + RespondRead(chl, buf, amount); + inode->Unref(); + free(buf); +} + +void HandleWriteAt(int chl, struct fsm_req_pread* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + const uint8_t* buf = (const uint8_t*) &msg[1]; + ssize_t amount = inode->WriteAt(buf, msg->count, msg->offset); + RespondWrite(chl, amount); + inode->Unref(); +} + +void HandleOpen(int chl, struct fsm_req_open* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->dirino); + if ( !inode ) { RespondError(chl, errno); return; } + + char* pathraw = (char*) &(msg[1]); + char* path = (char*) malloc(msg->namelen+1); + if ( !path ) + { + RespondError(chl, errno); + inode->Unref(); + return; + } + memcpy(path, pathraw, msg->namelen); + path[msg->namelen] = '\0'; + + Inode* result = inode->Open(path, msg->flags, ExtModeFromHostMode(msg->mode)); + + free(path); + inode->Unref(); + + if ( !result ) { RespondError(chl, errno); return; } + + RespondOpen(chl, result->inode_id, result->Mode() & S_IFMT); + result->Unref(); +} + +void HandleMakeDir(int chl, struct fsm_req_open* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->dirino); + if ( !inode ) { RespondError(chl, errno); return; } + + char* pathraw = (char*) &(msg[1]); + char* path = (char*) malloc(msg->namelen+1); + if ( !path ) + { + RespondError(chl, errno); + inode->Unref(); + return; + } + memcpy(path, pathraw, msg->namelen); + path[msg->namelen] = '\0'; + + Inode* result = inode->CreateDirectory(path, ExtModeFromHostMode(msg->mode)); + + free(path); + inode->Unref(); + + if ( !result ) { RespondError(chl, errno); return; } + + RespondMakeDir(chl, result->inode_id); + result->Unref(); +} + +void HandleReadDir(int chl, struct fsm_req_readdirents* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + if ( !S_ISDIR(inode->Mode()) ) + { + inode->Unref(); + RespondError(chl, ENOTDIR); + return; + } + union + { + struct kernel_dirent kernel_entry; + uint8_t padding[sizeof(struct kernel_dirent) + 256]; + }; + memset(&kernel_entry, 0, sizeof(kernel_entry)); + + uint64_t file_size = inode->Size(); + uint64_t offset = 0; + Block* block = NULL; + uint64_t block_id = 0; + while ( offset < file_size ) + { + uint64_t entry_block_id = offset / fs->block_size; + uint64_t entry_block_offset = offset % fs->block_size; + if ( block && block_id != entry_block_id ) + block->Unref(), + block = NULL; + if ( !block && !(block = inode->GetBlock(block_id = entry_block_id)) ) + { + inode->Unref(); + RespondError(chl, errno); + return; + } + const uint8_t* block_data = block->block_data + entry_block_offset; + const struct ext_dirent* entry = (const struct ext_dirent*) block_data; + if ( entry->inode && entry->name_len && !(msg->rec_num--) ) + { + kernel_entry.d_reclen = sizeof(kernel_entry) + entry->name_len; + kernel_entry.d_nextoff = 0; + kernel_entry.d_ino = entry->inode; + kernel_entry.d_dev = 0; + kernel_entry.d_type = 0; // TODO: Support this! + kernel_entry.d_namlen = entry->name_len; + memcpy(kernel_entry.d_name, entry->name, entry->name_len); + size_t dname_offset = offsetof(struct kernel_dirent, d_name); + padding[dname_offset + kernel_entry.d_namlen] = '\0'; + block->Unref(); + inode->Unref(); + RespondReadDir(chl, &kernel_entry); + return; + } + offset += entry->reclen; + } + if ( block ) + block->Unref(); + + kernel_entry.d_reclen = sizeof(kernel_entry); + RespondReadDir(chl, &kernel_entry); +} + +void HandleIsATTY(int chl, struct fsm_req_isatty* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + RespondError(chl, ENOTTY); + inode->Unref(); +} + +void HandleUnlink(int chl, struct fsm_req_unlink* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->dirino); + if ( !inode ) { RespondError(chl, errno); return; } + + char* pathraw = (char*) &(msg[1]); + char* path = (char*) malloc(msg->namelen+1); + if ( !path ) + { + RespondError(chl, errno); + inode->Unref(); + return; + } + memcpy(path, pathraw, msg->namelen); + path[msg->namelen] = '\0'; + + Inode* result = inode->Unlink(path, false); + free(path); + inode->Unref(); + + if ( !result ) { RespondError(chl, errno); return; } + + result->Unref(); + + RespondSuccess(chl); +} + +void HandleRemoveDir(int chl, struct fsm_req_unlink* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->dirino); + if ( !inode ) { RespondError(chl, errno); return; } + + char* pathraw = (char*) &(msg[1]); + char* path = (char*) malloc(msg->namelen+1); + if ( !path ) + { + RespondError(chl, errno); + inode->Unref(); + return; + } + memcpy(path, pathraw, msg->namelen); + path[msg->namelen] = '\0'; + + if ( inode->RemoveDirectory(path) ) + RespondSuccess(chl); + else + RespondError(chl, errno); + + free(path); + inode->Unref(); +} + +void HandleLink(int chl, struct fsm_req_link* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } + if ( fs->num_inodes <= msg->linkino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->dirino); + if ( !inode ) { RespondError(chl, errno); return; } + Inode* dest = fs->GetInode((uint32_t) msg->linkino); + if ( !dest ) { inode->Unref(); RespondError(chl, errno); return; } + + char* pathraw = (char*) &(msg[1]); + char* path = (char*) malloc(msg->namelen+1); + if ( !path ) + { + RespondError(chl, errno); + inode->Unref(); + return; + } + memcpy(path, pathraw, msg->namelen); + path[msg->namelen] = '\0'; + + if ( inode->Link(path, dest, false) ) + RespondSuccess(chl); + else + RespondError(chl, errno); + + free(path); + dest->Unref(); + inode->Unref(); +} + +void HandleSymlink(int chl, struct fsm_req_symlink* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->dirino); + if ( !inode ) { RespondError(chl, errno); return; } + + char* dest_raw = (char*) &(msg[1]); + char* dest = (char*) malloc(msg->targetlen + 1); + if ( !dest ) + { + RespondError(chl, errno); + inode->Unref(); + return; + } + memcpy(dest, dest_raw, msg->targetlen); + dest[msg->targetlen] = '\0'; + + char* path_raw = (char*) dest_raw + msg->targetlen; + char* path = (char*) malloc(msg->namelen + 1); + if ( !path ) + { + RespondError(chl, errno); + inode->Unref(); + return; + } + memcpy(path, path_raw, msg->namelen); + path[msg->namelen] = '\0'; + + if ( inode->Symlink(path, dest) ) + RespondSuccess(chl); + else + RespondError(chl, errno); + + free(path); + free(dest); + inode->Unref(); +} + +void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } + Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( !inode ) { RespondError(chl, errno); return; } + if ( !EXT2_S_ISLNK(inode->Mode()) ) { inode->Unref(); RespondError(chl, EINVAL); return; } + size_t count = inode->Size(); + uint8_t* buf = (uint8_t*) malloc(count); + if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; } + ssize_t amount = inode->ReadAt(buf, count, 0); + RespondReadlink(chl, buf, amount); + inode->Unref(); + free(buf); +} + +void HandleRename(int chl, struct fsm_req_rename* msg, Filesystem* fs) +{ + if ( fs->num_inodes <= msg->olddirino ) { RespondError(chl, EBADF); return; } + if ( fs->num_inodes <= msg->newdirino ) { RespondError(chl, EBADF); return; } + + char* pathraw = (char*) &(msg[1]); + char* path = (char*) malloc(msg->oldnamelen+1 + msg->newnamelen+1); + if ( !path ) { RespondError(chl, errno); return; } + memcpy(path, pathraw, msg->oldnamelen); + path[msg->oldnamelen] = '\0'; + memcpy(path + msg->oldnamelen + 1, pathraw + msg->oldnamelen, msg->newnamelen); + path[msg->oldnamelen + 1 + msg->newnamelen] = '\0'; + + const char* oldname = path; + const char* newname = path + msg->oldnamelen + 1; + + Inode* olddir = fs->GetInode((uint32_t) msg->olddirino); + if ( !olddir ) { free(path); RespondError(chl, errno); return; } + Inode* newdir = fs->GetInode((uint32_t) msg->newdirino); + if ( !newdir ) { olddir->Unref(); free(path); RespondError(chl, errno); return; } + + if ( newdir->Rename(olddir, oldname, newname) ) + RespondSuccess(chl); + else + RespondError(chl, errno); + + newdir->Unref(); + olddir->Unref(); + free(path); +} + +void HandleIncomingMessage(int chl, struct fsm_msg_header* hdr, Filesystem* fs) +{ + typedef void (*handler_t)(int, void*, Filesystem*); + handler_t handlers[FSM_MSG_NUM] = { NULL }; + handlers[FSM_REQ_SYNC] = (handler_t) HandleSync; + handlers[FSM_REQ_STAT] = (handler_t) HandleStat; + handlers[FSM_REQ_CHMOD] = (handler_t) HandleChangeMode; + handlers[FSM_REQ_CHOWN] = (handler_t) HandleChangeOwner; + handlers[FSM_REQ_TRUNCATE] = (handler_t) HandleTruncate; + handlers[FSM_REQ_LSEEK] = (handler_t) HandleSeek; + handlers[FSM_REQ_PREAD] = (handler_t) HandleReadAt; + handlers[FSM_REQ_OPEN] = (handler_t) HandleOpen; + handlers[FSM_REQ_READDIRENTS] = (handler_t) HandleReadDir; + handlers[FSM_REQ_PWRITE] = (handler_t) HandleWriteAt; + handlers[FSM_REQ_ISATTY] = (handler_t) HandleIsATTY; + handlers[FSM_REQ_UTIMENS] = (handler_t) HandleUTimens; + handlers[FSM_REQ_MKDIR] = (handler_t) HandleMakeDir; + handlers[FSM_REQ_RMDIR] = (handler_t) HandleRemoveDir; + handlers[FSM_REQ_UNLINK] = (handler_t) HandleUnlink; + handlers[FSM_REQ_LINK] = (handler_t) HandleLink; + handlers[FSM_REQ_SYMLINK] = (handler_t) HandleSymlink; + handlers[FSM_REQ_READLINK] = (handler_t) HandleReadlink; + handlers[FSM_REQ_RENAME] = (handler_t) HandleRename; + handlers[FSM_REQ_REFER] = (handler_t) HandleRefer; + handlers[FSM_REQ_UNREF] = (handler_t) HandleUnref; + if ( FSM_MSG_NUM <= hdr->msgtype || !handlers[hdr->msgtype] ) + { + fprintf(stderr, "extfs: message type %zu not supported!\n", hdr->msgtype); + RespondError(chl, ENOTSUP); + return; + } + uint8_t* body = (uint8_t*) malloc(hdr->msgsize); + if ( !body ) + { + fprintf(stderr, "extfs: message of type %zu too large: %zu bytes\n", hdr->msgtype, hdr->msgsize); + RespondError(chl, errno); + return; + } + size_t amount = readall(chl, body, hdr->msgsize); + if ( amount < hdr->msgsize ) + { + fprintf(stderr, "extfs: incomplete message of type %zu: got %zi of %zu bytes\n", hdr->msgtype, amount, hdr->msgsize); + RespondError(chl, errno); + free(body); + return; + } + handlers[hdr->msgtype](chl, body, fs); + free(body); +} +static volatile bool should_terminate = false; + +void TerminationHandler(int) +{ + should_terminate = true; +} + +int fsmarshall_main(const char* argv0, + const char* mount_path, + bool foreground, + Filesystem* fs, + Device* dev) +{ + // Stat the root inode. + struct stat root_inode_st; + Inode* root_inode = fs->GetInode((uint32_t) EXT2_ROOT_INO); + if ( !root_inode ) + error(1, errno, "GetInode(%u)", EXT2_ROOT_INO); + StatInode(root_inode, &root_inode_st); + root_inode->Unref(); + + // Create a filesystem server connected to the kernel that we'll listen on. + int serverfd = fsm_mountat(AT_FDCWD, mount_path, &root_inode_st, 0); + if ( serverfd < 0 ) + error(1, errno, "%s", mount_path); + + // Make sure the server isn't unexpectedly killed and data is lost. + signal(SIGINT, TerminationHandler); + signal(SIGTERM, TerminationHandler); + signal(SIGQUIT, TerminationHandler); + + // Become a background process in its own process group by default. + if ( !foreground ) + { + pid_t child_pid = fork(); + if ( child_pid < 0 ) + error(1, errno, "fork"); + if ( child_pid ) + exit(0); + setpgid(0, 0); + } + + // Listen for filesystem messages and sync the filesystem every few seconds. + struct timespec last_sync_at; + clock_gettime(CLOCK_MONOTONIC, &last_sync_at); + int channel; + while ( 0 <= (channel = accept(serverfd, NULL, NULL)) ) + { + if ( should_terminate ) + break; + struct fsm_msg_header hdr; + size_t amount; + if ( (amount = readall(channel, &hdr, sizeof(hdr))) != sizeof(hdr) ) + { + error(0, errno, "incomplete header: got %zi of %zu bytes", amount, sizeof(hdr)); + errno = 0; + continue; + } + HandleIncomingMessage(channel, &hdr, fs); + close(channel); + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + if ( dev->write && 5 <= timespec_sub(now, last_sync_at).tv_sec ) + { + fs->Sync(); + last_sync_at = now; + } + } + + // Sync the filesystem before shutting down. + if ( dev->write ) + { + fprintf(stderr, "%s: filesystem server shutting down, syncing...", argv0); + fflush(stderr); + fs->Sync(); + fprintf(stderr, " done.\n"); + } + + close(serverfd); + + delete fs; + delete dev; + + return 0; +} + +#endif diff --git a/ext/fsmarshall.h b/ext/fsmarshall.h new file mode 100644 index 00000000..a7155880 --- /dev/null +++ b/ext/fsmarshall.h @@ -0,0 +1,33 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2015. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . + + fsmarshall.h + Sortix fsmarshall frontend. + +*******************************************************************************/ +#ifndef FSMARSHALL_H +#define FSMARSHALL_H + +class Device; +class Filesystem; + +int fsmarshall_main(const char* argv0, + const char* mount_path, + bool foreground, + Filesystem* fs, + Device* dev); +#endif diff --git a/ext/fuse.cpp b/ext/fuse.cpp new file mode 100644 index 00000000..e11b9c11 --- /dev/null +++ b/ext/fuse.cpp @@ -0,0 +1,586 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013, 2014, 2015. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . + + fuse.cpp + FUSE frontend. + +*******************************************************************************/ + +#if !defined(__sortix__) + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define FUSE_USE_VERSION 26 +#include + +#include "ext-constants.h" +#include "ext-structs.h" + +#include "blockgroup.h" +#include "block.h" +#include "device.h" +#include "extfs.h" +#include "filesystem.h" +#include "fuse.h" +#include "inode.h" + +struct ext2_fuse_ctx +{ + Device* dev; + Filesystem* fs; +}; + +#ifndef S_SETABLE +#define S_SETABLE 02777 +#endif + +#define FUSE_FS (((struct ext2_fuse_ctx*) (fuse_get_context()->private_data))->fs) + +void* ext2_fuse_init(struct fuse_conn_info* /*conn*/) +{ + return fuse_get_context()->private_data; +} + +void ext2_fuse_destroy(void* fs_private) +{ + struct ext2_fuse_ctx* ext2_fuse_ctx = (struct ext2_fuse_ctx*) fs_private; + ext2_fuse_ctx->fs->Sync(); + ext2_fuse_ctx->dev->Sync(); + delete ext2_fuse_ctx->fs; ext2_fuse_ctx->fs = NULL; + delete ext2_fuse_ctx->dev; ext2_fuse_ctx->dev = NULL; +} + +Inode* ext2_fuse_resolve_path(const char* path) +{ + Filesystem* fs = FUSE_FS; + Inode* inode = fs->GetInode(EXT2_ROOT_INO); + assert(inode); + while ( path[0] ) + { + if ( *path == '/' ) + { + if ( !EXT2_S_ISDIR(inode->Mode()) ) + return errno = ENOTDIR, (Inode*) NULL; + path++; + continue; + } + size_t elem_len = strcspn(path, "/"); + char* elem = new char[elem_len+1]; + memcpy(elem, path, elem_len); + elem[elem_len] = '\0'; + path += elem_len; + Inode* next = inode->Open(elem, O_RDONLY, 0); + delete[] elem; + inode->Unref(); + if ( !next ) + return NULL; + inode = next; + } + return inode; +} + +// Assumes that the path doesn't end with / unless it's the root directory. +Inode* ext2_fuse_parent_dir(const char** path_ptr) +{ + const char* path = *path_ptr; + Filesystem* fs = FUSE_FS; + Inode* inode = fs->GetInode(EXT2_ROOT_INO); + assert(inode); + while ( strchr(path, '/') ) + { + if ( *path == '/' ) + { + if ( !EXT2_S_ISDIR(inode->Mode()) ) + return errno = ENOTDIR, (Inode*) NULL; + path++; + continue; + } + size_t elem_len = strcspn(path, "/"); + char* elem = new char[elem_len+1]; + memcpy(elem, path, elem_len); + elem[elem_len] = '\0'; + path += elem_len; + Inode* next = inode->Open(elem, O_RDONLY, 0); + delete[] elem; + inode->Unref(); + if ( !next ) + return NULL; + inode = next; + } + *path_ptr = *path ? path : "."; + assert(!strchr(*path_ptr, '/')); + return inode; +} + +int ext2_fuse_getattr(const char* path, struct stat* st) +{ + Inode* inode = ext2_fuse_resolve_path(path); + if ( !inode ) + return -errno; + StatInode(inode, st); + inode->Unref(); + return 0; +} + +int ext2_fuse_fgetattr(const char* /*path*/, struct stat* st, + struct fuse_file_info* fi) +{ + Filesystem* fs = FUSE_FS; + Inode* inode = fs->GetInode((uint32_t) fi->fh); + if ( !inode ) + return -errno; + StatInode(inode, st); + inode->Unref(); + return 0; +} + +int ext2_fuse_readlink(const char* path, char* buf, size_t bufsize) +{ + Inode* inode = ext2_fuse_resolve_path(path); + if ( !inode ) + return -errno; + if ( !EXT2_S_ISLNK(inode->Mode()) ) + return inode->Unref(), -(errno = EINVAL); + if ( !bufsize ) + return inode->Unref(), -(errno = EINVAL); + ssize_t amount = inode->ReadAt((uint8_t*) buf, bufsize, 0); + if ( amount < 0 ) + return inode->Unref(), -errno; + buf[(size_t) amount < bufsize ? (size_t) bufsize : bufsize - 1] = '\0'; + inode->Unref(); + return 0; +} + +int ext2_fuse_mknod(const char* path, mode_t mode, dev_t dev) +{ + (void) path; + (void) mode; + (void) dev; + return -(errno = ENOSYS); +} + +int ext2_fuse_mkdir(const char* path, mode_t mode) +{ + Inode* inode = ext2_fuse_parent_dir(&path); + if ( !inode ) + return -errno; + Inode* newdir = inode->CreateDirectory(path, ExtModeFromHostMode(mode)); + inode->Unref(); + if ( !newdir ) + return -errno; + newdir->Unref(); + return 0; +} + +int ext2_fuse_unlink(const char* path) +{ + Inode* inode = ext2_fuse_parent_dir(&path); + if ( !inode ) + return -errno; + Inode* result = inode->Unlink(path, false); + inode->Unref(); + if ( !result ) + return -errno; + result->Unref(); + return 0; +} + +int ext2_fuse_rmdir(const char* path) +{ + Inode* inode = ext2_fuse_parent_dir(&path); + if ( !inode ) + return -errno; + bool success = inode->RemoveDirectory(path); + inode->Unref(); + return success ? 0 : -errno; +} + +int ext2_fuse_symlink(const char* oldname, const char* newname) +{ + Inode* newdir = ext2_fuse_parent_dir(&newname); + if ( !newdir ) + return -errno; + bool success = newdir->Symlink(newname, oldname); + newdir->Unref(); + return success ? 0 : -errno; +} + +int ext2_fuse_rename(const char* oldname, const char* newname) +{ + Inode* olddir = ext2_fuse_parent_dir(&oldname); + if ( !olddir ) + return -errno; + Inode* newdir = ext2_fuse_parent_dir(&newname); + if ( !newdir ) + return olddir->Unref(), -errno; + bool success = newdir->Rename(olddir, oldname, newname); + newdir->Unref(); + olddir->Unref(); + return success ? 0 : -errno; +} + +int ext2_fuse_link(const char* oldname, const char* newname) +{ + Inode* inode = ext2_fuse_resolve_path(oldname); + if ( !inode ) + return -errno; + Inode* newdir = ext2_fuse_parent_dir(&newname); + if ( !newdir ) + return inode->Unref(), -errno; + bool success = inode->Link(newname, inode, false); + newdir->Unref(); + inode->Unref(); + return success ? 0 : -errno; +} + +int ext2_fuse_chmod(const char* path, mode_t mode) +{ + Inode* inode = ext2_fuse_resolve_path(path); + if ( !inode ) + return -errno; + uint32_t req_mode = ExtModeFromHostMode(mode); + uint32_t old_mode = inode->Mode(); + uint32_t new_mode = (old_mode & ~S_SETABLE) | (req_mode & S_SETABLE); + inode->SetMode(new_mode); + inode->Unref(); + return 0; +} + +int ext2_fuse_chown(const char* path, uid_t owner, gid_t group) +{ + Inode* inode = ext2_fuse_resolve_path(path); + if ( !inode ) + return -errno; + inode->SetUserId((uint32_t) owner); + inode->SetGroupId((uint32_t) group); + inode->Unref(); + return 0; +} + +int ext2_fuse_truncate(const char* path, off_t size) +{ + Inode* inode = ext2_fuse_resolve_path(path); + if ( !inode ) + return -errno; + inode->Truncate((uint64_t) size); + inode->Unref(); + return 0; +} + +int ext2_fuse_ftruncate(const char* /*path*/, off_t size, + struct fuse_file_info* fi) +{ + Filesystem* fs = FUSE_FS; + Inode* inode = fs->GetInode((uint32_t) fi->fh); + if ( !inode ) + return -errno; + inode->Truncate((uint64_t) size); + inode->Unref(); + return 0; +} + +int ext2_fuse_open(const char* path, struct fuse_file_info* fi) +{ + int flags = fi->flags; + Inode* dir = ext2_fuse_parent_dir(&path); + if ( !dir ) + return -errno; + Inode* result = dir->Open(path, flags, 0); + dir->Unref(); + if ( !result ) + return -errno; + fi->fh = (uint64_t) result->inode_id; + fi->keep_cache = 1; + result->RemoteRefer(); + result->Unref(); + return 0; +} + +int ext2_fuse_access(const char* path, int mode) +{ + Inode* dir = ext2_fuse_parent_dir(&path); + if ( !dir ) + return -errno; + Inode* result = dir->Open(path, O_RDONLY, 0); + dir->Unref(); + if ( !result ) + return -errno; + (void) mode; + result->Unref(); + return 0; +} + +int ext2_fuse_create(const char* path, mode_t mode, struct fuse_file_info* fi) +{ + int flags = fi->flags | O_CREAT; + Inode* inode = ext2_fuse_parent_dir(&path); + if ( !inode ) + return -errno; + Inode* result = inode->Open(path, flags, ExtModeFromHostMode(mode)); + inode->Unref(); + if ( !result ) + return -errno; + fi->fh = (uint64_t) result->inode_id; + fi->keep_cache = 1; + result->Unref(); + return 0; +} + +int ext2_fuse_opendir(const char* path, struct fuse_file_info* fi) +{ + return ext2_fuse_open(path, fi); +} + +int ext2_fuse_read(const char* /*path*/, char* buf, size_t count, off_t offset, + struct fuse_file_info* fi) +{ + Filesystem* fs = FUSE_FS; + if ( INT_MAX < count ) + count = INT_MAX; + Inode* inode = fs->GetInode((uint32_t) fi->fh); + if ( !inode ) + return -errno; + ssize_t result = inode->ReadAt((uint8_t*) buf, count, offset); + inode->Unref(); + return 0 <= result ? (int) result : -errno; +} + +int ext2_fuse_write(const char* /*path*/, const char* buf, size_t count, + off_t offset, struct fuse_file_info* fi) +{ + Filesystem* fs = FUSE_FS; + if ( INT_MAX < count ) + count = INT_MAX; + Inode* inode = fs->GetInode((uint32_t) fi->fh); + if ( !inode ) + return -errno; + ssize_t result = inode->WriteAt((const uint8_t*) buf, count, offset); + inode->Unref(); + return 0 <= result ? (int) result : -errno; +} + +int ext2_fuse_statfs(const char* /*path*/, struct statvfs* stvfs) +{ + (void) stvfs; + return errno = -ENOSYS, -1; +} + +int ext2_fuse_flush(const char* /*path*/, struct fuse_file_info* fi) +{ + Filesystem* fs = FUSE_FS; + Inode* inode = fs->GetInode((uint32_t) fi->fh); + if ( !inode ) + return -errno; + inode->Sync(); + inode->Unref(); + return 0; +} + +int ext2_fuse_release(const char* /*path*/, struct fuse_file_info* fi) +{ + Filesystem* fs = FUSE_FS; + Inode* inode = fs->GetInode((uint32_t) fi->fh); + if ( !inode ) + return -errno; + inode->RemoteUnref(); + inode->Unref(); + return 0; +} + +int ext2_fuse_releasedir(const char* path, struct fuse_file_info* fi) +{ + return ext2_fuse_release(path, fi); +} + +int ext2_fuse_fsync(const char* /*path*/, int data, struct fuse_file_info* fi) +{ + (void) data; + Filesystem* fs = FUSE_FS; + Inode* inode = fs->GetInode((uint32_t) fi->fh); + if ( !inode ) + return -errno; + inode->Sync(); + inode->Unref(); + return 0; +} + +/*int ext2_fuse_syncdir(const char* path, int data, struct fuse_file_info* fi) +{ + return ext2_fuse_sync(path, data, fi); +}*/ + +/*int ext2_fuse_setxattr(const char *, const char *, const char *, size_t, int) +{ + return -(errno = ENOSYS); +}*/ + +/*int ext2_fuse_getxattr(const char *, const char *, char *, size_t) +{ + return -(errno = ENOSYS); +}*/ + +/*int ext2_fuse_listxattr(const char *, char *, size_t) +{ + return -(errno = ENOSYS); +}*/ + +/*int ext2_fuse_removexattr(const char *, const char *) +{ + return -(errno = ENOSYS); +}*/ + +int ext2_fuse_readdir(const char* /*path*/, void* buf, fuse_fill_dir_t filler, + off_t rec_num, struct fuse_file_info* fi) +{ + Filesystem* fs = FUSE_FS; + Inode* inode = fs->GetInode((uint32_t) fi->fh); + if ( !inode ) + return -errno; + if ( !S_ISDIR(inode->Mode()) ) + return inode->Unref(), -(errno = ENOTDIR); + + uint64_t file_size = inode->Size(); + uint64_t offset = 0; + Block* block = NULL; + uint64_t block_id = 0; + while ( offset < file_size ) + { + uint64_t entry_block_id = offset / fs->block_size; + uint64_t entry_block_offset = offset % fs->block_size; + if ( block && block_id != entry_block_id ) + block->Unref(), + block = NULL; + if ( !block && !(block = inode->GetBlock(block_id = entry_block_id)) ) + return inode->Unref(), -errno; + const uint8_t* block_data = block->block_data + entry_block_offset; + const struct ext_dirent* entry = (const struct ext_dirent*) block_data; + if ( entry->inode && entry->name_len && (!rec_num || !rec_num--) ) + { + char* entry_name = new char[entry->name_len+1]; + memcpy(entry_name, entry->name, entry->name_len); + entry_name[entry->name_len] = '\0'; + bool full = filler(buf, entry_name, NULL, 0); + delete[] entry_name; + if ( full ) + { + block->Unref(); + inode->Unref(); + return 0; + } + } + offset += entry->reclen; + } + if ( block ) + block->Unref(); + + inode->Unref(); + return 0; +} + +/*int ext2_fuse_lock(const char*, struct fuse_file_info*, int, struct flock*) +{ + return -(errno = ENOSYS); +}*/ + +int ext2_fuse_utimens(const char* path, const struct timespec tv[2]) +{ + Inode* inode = ext2_fuse_resolve_path(path); + if ( !inode ) + return -errno; + inode->BeginWrite(); + inode->data->i_atime = tv[0].tv_sec; + inode->data->i_mtime = tv[1].tv_sec; + inode->FinishWrite(); + inode->Unref(); + return 0; +} + +/*int ext2_fuse_bmap(const char*, size_t blocksize, uint64_t* idx) +{ + return -(errno = ENOSYS); +}*/ + +int ext2_fuse_main(const char* argv0, + const char* mount_path, + bool foreground, + Filesystem* fs, + Device* dev) +{ + struct fuse_operations operations; + memset(&operations, 0, sizeof(operations)); + + operations.access = ext2_fuse_access; + operations.chmod = ext2_fuse_chmod; + operations.chown = ext2_fuse_chown; + operations.create = ext2_fuse_create; + operations.destroy = ext2_fuse_destroy; + operations.fgetattr = ext2_fuse_fgetattr; + operations.flush = ext2_fuse_flush; + operations.fsync = ext2_fuse_fsync; + operations.ftruncate = ext2_fuse_ftruncate; + operations.getattr = ext2_fuse_getattr; + operations.init = ext2_fuse_init; + operations.link = ext2_fuse_link; + operations.mkdir = ext2_fuse_mkdir; + operations.mknod = ext2_fuse_mknod; + operations.opendir = ext2_fuse_opendir; + operations.open = ext2_fuse_open; + operations.readdir = ext2_fuse_readdir; + operations.read = ext2_fuse_read; + operations.readlink = ext2_fuse_readlink; + operations.releasedir = ext2_fuse_releasedir; + operations.release = ext2_fuse_release; + operations.rename = ext2_fuse_rename; + operations.rmdir = ext2_fuse_rmdir; + operations.statfs = ext2_fuse_statfs; + operations.symlink = ext2_fuse_symlink; + operations.truncate = ext2_fuse_truncate; + operations.unlink = ext2_fuse_unlink; + operations.utimens = ext2_fuse_utimens; + operations.write = ext2_fuse_write; + + operations.flag_nullpath_ok = 1; + operations.flag_nopath = 1; + + char* argv_fuse[] = + { + (char*) argv0, + (char*) "-s", + (char*) mount_path, + (char*) NULL, + }; + + int argc_fuse = sizeof(argv_fuse) / sizeof(argv_fuse[0]) - 1; + + struct ext2_fuse_ctx ext2_fuse_ctx; + ext2_fuse_ctx.fs = fs; + ext2_fuse_ctx.dev = dev; + + (void) foreground; + + return fuse_main(argc_fuse, argv_fuse, &operations, &ext2_fuse_ctx); +} + +#endif diff --git a/ext/fuse.h b/ext/fuse.h new file mode 100644 index 00000000..6bd10a42 --- /dev/null +++ b/ext/fuse.h @@ -0,0 +1,35 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2015. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . + + fuse.h + FUSE frontend. + +*******************************************************************************/ + +#ifndef FUSE_H +#define FUSE_H + +class Device; +class Filesystem; + +int ext2_fuse_main(const char* argv0, + const char* mount_path, + bool foreground, + Filesystem* fs, + Device* dev); + +#endif