diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp index c7085ae602..9ebdcb1c5e 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.cpp @@ -10,6 +10,7 @@ #include +#include "fuse_fs.h" #include "FUSEVolume.h" @@ -126,6 +127,11 @@ FUSEFileSystem::FUSEFileSystem(const char* fsName, fInitParameters(NULL), fFS(NULL) { + fClientFSType = CLIENT_FS_FUSE; + + // FS capabilities + fCapabilities.ClearAll(); + fCapabilities.Set(FS_CAPABILITY_MOUNT, true); } @@ -155,6 +161,12 @@ printf("FUSEFileSystem::CreateVolume()\n"); if (volume == NULL) return B_NO_MEMORY; + status_t error = volume->Init(); + if (error != B_OK) { + delete volume; + return error; + } + *_volume = volume; return B_OK; } @@ -171,6 +183,7 @@ FUSEFileSystem::DeleteVolume(Volume* volume) status_t FUSEFileSystem::InitClientFS(const char* parameters) { +PRINT(("FUSEFileSystem::InitClientFS()\n")); // create the semaphores we need fInitSemaphore = create_sem(0, "FUSE init sem"); if (fInitSemaphore < 0) @@ -193,9 +206,11 @@ FUSEFileSystem::InitClientFS(const char* parameters) resume_thread(fInitThread); // wait for the initialization to finish +PRINT((" waiting for init thread...\n")); while (acquire_sem(fInitSemaphore) == B_INTERRUPTED) { } +PRINT((" waiting for init thread done\n")); fInitSemaphore = -1; if (fInitStatus > 0) @@ -215,6 +230,9 @@ FUSEFileSystem::ExitClientFS(status_t status) fExitStatus = status; if (fExitSemaphore >= 0) delete_sem(fExitSemaphore); + + if (fInitThread >= 0) + wait_for_thread(fInitThread, NULL); } @@ -222,18 +240,22 @@ status_t FUSEFileSystem::FinishInitClientFS(const fuse_operations* ops, size_t opSize, void* userData) { +PRINT(("FUSEFileSystem::FinishInitClientFS()\n")); fExitStatus = B_ERROR; // do the initialization status_t error = _InitClientFS(ops, opSize, userData); // notify the mount thread +PRINT((" notifying mount thread\n")); fInitStatus = error; delete_sem(fInitSemaphore); // loop until unmounting +PRINT((" waiting for unmounting...\n")); while (acquire_sem(fExitSemaphore) == B_INTERRUPTED) { } +PRINT((" waiting for unmounting done\n")); fExitSemaphore = -1; @@ -287,6 +309,12 @@ FUSEFileSystem::_InitClientFS(const fuse_operations* ops, size_t opSize, if (fFS == NULL) return B_ERROR; + _InitCapabilities(); +PRINT(("volume capabilities:\n")); +fVolumeCapabilities.Dump(); +PRINT(("node capabilities:\n")); +fNodeCapabilities.Dump(); + // init connection info fConnectionInfo.proto_major = 0; fConnectionInfo.proto_minor = 0; @@ -300,6 +328,138 @@ FUSEFileSystem::_InitClientFS(const fuse_operations* ops, size_t opSize, } +void +FUSEFileSystem::_InitCapabilities() +{ + fVolumeCapabilities.ClearAll(); + fNodeCapabilities.ClearAll(); + + // Volume operations + fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_UNMOUNT, true); + + fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_FS_INFO, fFS->ops.statfs); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_WRITE_FS_INFO, +// fFS->ops.wfsstat); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_SYNC, fFS->ops.sync); +// + fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_GET_VNODE, true); + // emulated +// +// // index directory & index operations +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_OPEN_INDEX_DIR, +// fFS->ops.open_indexdir); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_CLOSE_INDEX_DIR, +// fFS->ops.close_indexdir); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_FREE_INDEX_DIR_COOKIE, +// fFS->ops.free_indexdircookie); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_INDEX_DIR, +// fFS->ops.read_indexdir); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_REWIND_INDEX_DIR, +// fFS->ops.rewind_indexdir); +// +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_CREATE_INDEX, +// fFS->ops.create_index); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_REMOVE_INDEX, +// fFS->ops.remove_index); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_INDEX_STAT, +// fFS->ops.stat_index); +// +// // query operations +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_OPEN_QUERY, +// fFS->ops.open_query); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_CLOSE_QUERY, +// fFS->ops.close_query); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_FREE_QUERY_COOKIE, +// fFS->ops.free_querycookie); +// fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_QUERY, +// fFS->ops.read_query); +// // missing: FS_VOLUME_CAPABILITY_REWIND_QUERY, +// +// // vnode operations + fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LOOKUP, true); + // emulated + fNodeCapabilities.Set(FS_VNODE_CAPABILITY_GET_VNODE_NAME, true); + // emulated + fNodeCapabilities.Set(FS_VNODE_CAPABILITY_PUT_VNODE, true); + // emulated + fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_VNODE, true); + // emulated + +// // VM file access +// // missing: FS_VNODE_CAPABILITY_CAN_PAGE, +// // missing: FS_VNODE_CAPABILITY_READ_PAGES, +// // missing: FS_VNODE_CAPABILITY_WRITE_PAGES, +// +// // cache file access +// // missing: FS_VNODE_CAPABILITY_GET_FILE_MAP, +// +// // common operations +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_IOCTL, fFS->ops.ioctl); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_SET_FLAGS, fFS->ops.setflags); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_SELECT, fFS->ops.select); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_DESELECT, fFS->ops.deselect); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FSYNC, fFS->ops.fsync); +// +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_SYMLINK, fFS->ops.readlink); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_SYMLINK, fFS->ops.symlink); +// +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LINK, fFS->ops.link); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_UNLINK, fFS->ops.unlink); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME, fFS->ops.rename); +// +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_ACCESS, fFS->ops.access); + fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_STAT, fFS->ops.getattr); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_STAT, fFS->ops.wstat); +// +// // file operations +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE, fFS->ops.create); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN, fFS->ops.open); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE, fFS->ops.close); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_COOKIE, fFS->ops.free_cookie); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ, fFS->ops.read); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE, fFS->ops.write); +// +// // directory operations +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_DIR, fFS->ops.mkdir); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_DIR, fFS->ops.rmdir); + fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_DIR, fFS->ops.opendir); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE_DIR, true); + // not needed + fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_DIR_COOKIE, true); + fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_DIR, fFS->ops.readdir); + fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_DIR, fFS->ops.readdir); +// +// // attribute directory operations +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, +// fFS->ops.open_attrdir); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR, +// fFS->ops.close_attrdir); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE, +// fFS->ops.free_attrdircookie); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_DIR, +// fFS->ops.read_attrdir); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, +// fFS->ops.rewind_attrdir); +// +// // attribute operations +// // we emulate open_attr() and free_attr_dir_cookie() if either read_attr() +// // or write_attr() is present +// bool hasAttributes = (fFS->ops.read_attr || fFS->ops.write_attr); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_ATTR, hasAttributes); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, hasAttributes); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE_ATTR, false); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE, hasAttributes); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR, fFS->ops.read_attr); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR, fFS->ops.write_attr); +// +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT, +// fFS->ops.stat_attr); +// // missing: FS_VNODE_CAPABILITY_WRITE_ATTR_STAT +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME_ATTR, fFS->ops.rename_attr); +// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_ATTR, fFS->ops.remove_attr); +} + + // #pragma mark - bootstrapping diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.h b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.h index b1fc151708..e1ebd97f5b 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.h +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEFileSystem.h @@ -21,6 +21,17 @@ public: static FUSEFileSystem* GetInstance(); + void GetVolumeCapabilities( + FSVolumeCapabilities& capabilities) const + { capabilities = fVolumeCapabilities; } + void GetNodeCapabilities( + FSVNodeCapabilities& capabilities) const + { capabilities = fNodeCapabilities; } + const FSVNodeCapabilities& GetNodeCapabilities() const + { return fNodeCapabilities; } + + fuse_fs* GetFS() const { return fFS; } + virtual status_t CreateVolume(Volume** _volume, dev_t id); virtual status_t DeleteVolume(Volume* volume); @@ -40,6 +51,8 @@ private: status_t _InitClientFS(const fuse_operations* ops, size_t opSize, void* userData); + void _InitCapabilities(); + private: int (*fMainFunction)(int, const char* const*); thread_id fInitThread; @@ -51,6 +64,9 @@ private: const char* fInitParameters; fuse_fs* fFS; fuse_conn_info fConnectionInfo; + + FSVolumeCapabilities fVolumeCapabilities; + FSVNodeCapabilities fNodeCapabilities; }; } // namespace UserlandFS diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp index 4029855051..3471ca561e 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.cpp @@ -5,8 +5,73 @@ #include "FUSEVolume.h" +#include + +#include + +#include + +#include + #include "FUSEFileSystem.h" +#include "../kernel_emu.h" + + +static inline status_t +from_fuse_error(int error) +{ + return error < 0 ? error : -error; +} + + +struct FUSEVolume::DirCookie : fuse_file_info { + off_t currentEntryOffset; + + DirCookie() + : + currentEntryOffset(0) + { + flags = 0; + fh_old = 0; + writepage = 0; + direct_io = 0; + keep_cache = 0; + flush = 0; + fh = 0; + lock_owner = 0; + } +}; + + +struct FUSEVolume::ReadDirBuffer { + FUSEVolume* volume; + FUSENode* directory; + DirCookie* cookie; + void* buffer; + size_t bufferSize; + size_t usedSize; + uint32 entriesRead; + uint32 maxEntries; + off_t lastOffset; + status_t error; + + ReadDirBuffer(FUSEVolume* volume, FUSENode* directory, DirCookie* cookie, + void* buffer, size_t bufferSize, uint32 maxEntries) + : + directory(directory), + cookie(cookie), + buffer(buffer), + bufferSize(bufferSize), + usedSize(0), + entriesRead(0), + maxEntries(maxEntries), + lastOffset(0), + error(B_OK) + { + } +}; + inline FUSEFileSystem* FUSEVolume::_FileSystem() const @@ -17,7 +82,11 @@ FUSEVolume::_FileSystem() const FUSEVolume::FUSEVolume(FUSEFileSystem* fileSystem, dev_t id) : - Volume(fileSystem, id) + Volume(fileSystem, id), + fFS(NULL), + fRootNode(NULL), + fNextNodeID(FUSE_ROOT_ID + 1), + fUseNodeIDs(false) { } @@ -27,6 +96,27 @@ FUSEVolume::~FUSEVolume() } +status_t +FUSEVolume::Init() +{ + // init entry and node tables + status_t error = fEntries.Init(); + if (error != B_OK) + return error; + + error = fNodes.Init(); + if (error != B_OK) + return error; + + // check lock + error = fLock.InitCheck(); + if (error != B_OK) + return error; + + return B_OK; +} + + // #pragma mark - FS @@ -39,9 +129,47 @@ printf("FUSEVolume::Mount()\n"); if (error != B_OK) RETURN_ERROR(error); - _FileSystem()->ExitClientFS(B_UNSUPPORTED); + fFS = _FileSystem()->GetFS(); + _FileSystem()->GetVolumeCapabilities(fCapabilities); - return B_UNSUPPORTED; + // get the root node + struct stat st; + int fuseError = fuse_fs_getattr(fFS, "/", &st); + if (fuseError != 0) + RETURN_ERROR(from_fuse_error(fuseError)); + + if (!fUseNodeIDs) + st.st_ino = FUSE_ROOT_ID; + + // create a node and an entry object for the root node + AutoLocker _(fLock); + FUSENode* node = new(std::nothrow) FUSENode(st.st_ino, st.st_mode & S_IFMT); + FUSEEntry* entry = node != NULL ? FUSEEntry::Create(node, "/", node) : NULL; + if (node == NULL || entry == NULL) { + delete node; + delete entry; + _FileSystem()->ExitClientFS(B_NO_MEMORY); + RETURN_ERROR(B_NO_MEMORY); + } + + node->entries.Add(entry); + fRootNode = node; + + // insert the node and the entry + fNodes.Insert(node); + fEntries.Insert(entry); + + // publish the root node + error = UserlandFS::KernelEmu::publish_vnode(fID, node->id, node, + node->type, 0, _FileSystem()->GetNodeCapabilities()); + if (error != B_OK) { + _FileSystem()->ExitClientFS(B_NO_MEMORY); + RETURN_ERROR(error); + } + + *rootID = node->id; + + return B_OK; } @@ -49,13 +177,16 @@ status_t FUSEVolume::Unmount() { printf("FUSEVolume::Unmount()\n"); - return B_UNSUPPORTED; + _FileSystem()->ExitClientFS(B_OK); + return B_OK; } status_t FUSEVolume::Sync() { + // TODO: Implement! + // NOTE: There's no hook for sync'ing the whole FS. return B_UNSUPPORTED; } @@ -63,13 +194,32 @@ FUSEVolume::Sync() status_t FUSEVolume::ReadFSInfo(fs_info* info) { - return B_UNSUPPORTED; + if (fFS->ops.statfs == NULL) + return B_UNSUPPORTED; + + struct statvfs st; + int fuseError = fuse_fs_statfs(fFS, "/", &st); + if (fuseError != 0) + return from_fuse_error(fuseError); + + info->flags = ((st.f_fsid & ST_RDONLY) != 0 ? B_FS_IS_READONLY : 0) + | B_FS_IS_PERSISTENT; // assume the FS is persistent + info->block_size = st.f_bsize; + info->io_size = 64 * 1024; // some value + info->total_blocks = st.f_blocks; + info->free_blocks = st.f_bfree; + info->total_nodes = st.f_files; + info->free_nodes = st.f_favail; + info->volume_name[0] = '\0'; // no way to get the name (if any) + + return B_OK; } status_t FUSEVolume::WriteFSInfo(const struct fs_info* info, uint32 mask) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -78,45 +228,76 @@ FUSEVolume::WriteFSInfo(const struct fs_info* info, uint32 mask) status_t -FUSEVolume::Lookup(void* dir, const char* entryName, ino_t* vnid) +FUSEVolume::Lookup(void* _dir, const char* entryName, ino_t* vnid) { - return B_UNSUPPORTED; + FUSENode* dir = (FUSENode*)_dir; + + // look the node up + FUSENode* node; + status_t error = _GetNode(dir, entryName, &node); + if (error != B_OK) + return error; + + *vnid = node->id; + + return B_OK; } status_t -FUSEVolume::GetVNodeType(void* node, int* type) +FUSEVolume::GetVNodeName(void* _node, char* buffer, size_t bufferSize) { - return B_NOT_SUPPORTED; + FUSENode* node = (FUSENode*)_node; + + AutoLocker _(fLock); + + // get one of the node's entries and return its name + FUSEEntry* entry = node->entries.Head(); + if (entry == NULL) + RETURN_ERROR(B_ENTRY_NOT_FOUND); + + if (strlcpy(buffer, entry->name, bufferSize) >= bufferSize) + RETURN_ERROR(B_NAME_TOO_LONG); + + return B_OK; } status_t -FUSEVolume::GetVNodeName(void* node, char* buffer, size_t bufferSize) -{ - return B_NOT_SUPPORTED; -} - - -status_t -FUSEVolume::ReadVNode(ino_t vnid, bool reenter, void** node, int* type, +FUSEVolume::ReadVNode(ino_t vnid, bool reenter, void** _node, int* type, uint32* flags, FSVNodeCapabilities* _capabilities) { - return B_UNSUPPORTED; + AutoLocker _(fLock); + + FUSENode* node = fNodes.Lookup(vnid); + if (node == NULL) + RETURN_ERROR(B_ENTRY_NOT_FOUND); + + node->refCount++; + *_capabilities = _FileSystem()->GetNodeCapabilities(); + + return B_OK; } status_t -FUSEVolume::WriteVNode(void* node, bool reenter) +FUSEVolume::WriteVNode(void* _node, bool reenter) { - return B_UNSUPPORTED; + FUSENode* node = (FUSENode*)_node; + + AutoLocker _(fLock); + + _PutNode(node); + + return B_OK; } status_t FUSEVolume::RemoveVNode(void* node, bool reenter) { - return B_UNSUPPORTED; + // TODO: Implement for real! + return WriteVNode(node, reenter); } @@ -126,6 +307,7 @@ FUSEVolume::RemoveVNode(void* node, bool reenter) status_t FUSEVolume::DoIO(void* node, void* cookie, const IORequestInfo& requestInfo) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -133,6 +315,7 @@ FUSEVolume::DoIO(void* node, void* cookie, const IORequestInfo& requestInfo) status_t FUSEVolume::CancelIO(void* node, void* cookie, int32 ioRequestID) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -141,6 +324,7 @@ status_t FUSEVolume::IterativeIOGetVecs(void* cookie, int32 requestID, off_t offset, size_t size, struct file_io_vec* vecs, size_t* _count) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -149,6 +333,7 @@ status_t FUSEVolume::IterativeIOFinished(void* cookie, int32 requestID, status_t status, bool partialTransfer, size_t bytesTransferred) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -160,6 +345,7 @@ status_t FUSEVolume::IOCtl(void* node, void* cookie, uint32 command, void *buffer, size_t size) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -167,6 +353,7 @@ FUSEVolume::IOCtl(void* node, void* cookie, uint32 command, void *buffer, status_t FUSEVolume::SetFlags(void* node, void* cookie, int flags) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -174,6 +361,7 @@ FUSEVolume::SetFlags(void* node, void* cookie, int flags) status_t FUSEVolume::Select(void* node, void* cookie, uint8 event, selectsync* sync) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -181,6 +369,7 @@ FUSEVolume::Select(void* node, void* cookie, uint8 event, selectsync* sync) status_t FUSEVolume::Deselect(void* node, void* cookie, uint8 event, selectsync* sync) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -188,6 +377,7 @@ FUSEVolume::Deselect(void* node, void* cookie, uint8 event, selectsync* sync) status_t FUSEVolume::FSync(void* node) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -196,6 +386,7 @@ status_t FUSEVolume::ReadSymlink(void* node, char* buffer, size_t bufferSize, size_t* bytesRead) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -204,6 +395,7 @@ status_t FUSEVolume::CreateSymlink(void* dir, const char* name, const char* target, int mode) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -211,6 +403,7 @@ FUSEVolume::CreateSymlink(void* dir, const char* name, const char* target, status_t FUSEVolume::Link(void* dir, const char* name, void* node) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -218,6 +411,7 @@ FUSEVolume::Link(void* dir, const char* name, void* node) status_t FUSEVolume::Unlink(void* dir, const char* name) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -226,6 +420,7 @@ status_t FUSEVolume::Rename(void* oldDir, const char* oldName, void* newDir, const char* newName) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -233,20 +428,41 @@ FUSEVolume::Rename(void* oldDir, const char* oldName, void* newDir, status_t FUSEVolume::Access(void* node, int mode) { + // TODO: Implement! return B_UNSUPPORTED; } status_t -FUSEVolume::ReadStat(void* node, struct stat* st) +FUSEVolume::ReadStat(void* _node, struct stat* st) { - return B_UNSUPPORTED; + FUSENode* node = (FUSENode*)_node; +PRINT(("FUSEVolume::ReadStat(%p (%lld), %p)\n", node, node->id, st)); + + AutoLocker locker(fLock); + + // get a path for the node + char path[B_PATH_NAME_LENGTH]; + size_t pathLen; + status_t error = _BuildPath(node, path, pathLen); + if (error != B_OK) + RETURN_ERROR(error); + + locker.Unlock(); + + // stat the path + int fuseError = fuse_fs_getattr(fFS, path, st); + if (fuseError != 0) + return from_fuse_error(fuseError); + + return B_OK; } status_t FUSEVolume::WriteStat(void* node, const struct stat *st, uint32 mask) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -258,6 +474,7 @@ status_t FUSEVolume::Create(void* dir, const char* name, int openMode, int mode, void** cookie, ino_t* vnid) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -265,6 +482,7 @@ FUSEVolume::Create(void* dir, const char* name, int openMode, int mode, status_t FUSEVolume::Open(void* node, int openMode, void** cookie) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -272,6 +490,7 @@ FUSEVolume::Open(void* node, int openMode, void** cookie) status_t FUSEVolume::Close(void* node, void* cookie) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -279,6 +498,7 @@ FUSEVolume::Close(void* node, void* cookie) status_t FUSEVolume::FreeCookie(void* node, void* cookie) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -287,6 +507,7 @@ status_t FUSEVolume::Read(void* node, void* cookie, off_t pos, void* buffer, size_t bufferSize, size_t* bytesRead) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -295,6 +516,7 @@ status_t FUSEVolume::Write(void* node, void* cookie, off_t pos, const void* buffer, size_t bufferSize, size_t* bytesWritten) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -305,6 +527,7 @@ FUSEVolume::Write(void* node, void* cookie, off_t pos, const void* buffer, status_t FUSEVolume::CreateDir(void* dir, const char* name, int mode, ino_t *newDir) { + // TODO: Implement! return B_UNSUPPORTED; } @@ -312,41 +535,433 @@ FUSEVolume::CreateDir(void* dir, const char* name, int mode, ino_t *newDir) status_t FUSEVolume::RemoveDir(void* dir, const char* name) { + // TODO: Implement! return B_UNSUPPORTED; } status_t -FUSEVolume::OpenDir(void* node, void** cookie) +FUSEVolume::OpenDir(void* _node, void** _cookie) { - return B_UNSUPPORTED; + FUSENode* node = (FUSENode*)_node; +PRINT(("FUSEVolume::OpenDir(%p (%lld), %p)\n", node, node->id, _cookie)); + + // allocate a dir cookie + DirCookie* cookie = new(std::nothrow) DirCookie; + if (cookie == NULL) + RETURN_ERROR(B_NO_MEMORY); + ObjectDeleter cookieDeleter(cookie); + + AutoLocker locker(fLock); + + // get a path for the node + char path[B_PATH_NAME_LENGTH]; + size_t pathLen; + status_t error = _BuildPath(node, path, pathLen); + if (error != B_OK) + RETURN_ERROR(error); + + locker.Unlock(); + + // open the dir + int fuseError = fuse_fs_opendir(fFS, path, cookie); + if (fuseError != 0) + return from_fuse_error(fuseError); + + cookieDeleter.Detach(); + *_cookie = cookie; + + return B_OK; } status_t -FUSEVolume::CloseDir(void* node, void* cookie) +FUSEVolume::CloseDir(void* node, void* _cookie) { - return B_UNSUPPORTED; + return B_OK; } status_t -FUSEVolume::FreeDirCookie(void* node, void* cookie) +FUSEVolume::FreeDirCookie(void* _node, void* _cookie) { - return B_UNSUPPORTED; + FUSENode* node = (FUSENode*)_node; + DirCookie* cookie = (DirCookie*)_cookie; + + ObjectDeleter cookieDeleter(cookie); + + AutoLocker locker(fLock); + + // get a path for the node + char path[B_PATH_NAME_LENGTH]; + size_t pathLen; + status_t error = _BuildPath(node, path, pathLen); + if (error != B_OK) + RETURN_ERROR(error); + + locker.Unlock(); + + // release the dir + int fuseError = fuse_fs_releasedir(fFS, path, cookie); + if (fuseError != 0) + return from_fuse_error(fuseError); + + return B_OK; } status_t -FUSEVolume::ReadDir(void* node, void* cookie, void* buffer, size_t bufferSize, +FUSEVolume::ReadDir(void* _node, void* _cookie, void* buffer, size_t bufferSize, uint32 count, uint32* countRead) { - return B_UNSUPPORTED; + FUSENode* node = (FUSENode*)_node; + DirCookie* cookie = (DirCookie*)_cookie; + + AutoLocker locker(fLock); + + ReadDirBuffer readDirBuffer(this, node, cookie, buffer, bufferSize, count); + + // get a path for the node + char path[B_PATH_NAME_LENGTH]; + size_t pathLen; + status_t error = _BuildPath(node, path, pathLen); + if (error != B_OK) + RETURN_ERROR(error); + + off_t offset = cookie->currentEntryOffset; + + locker.Unlock(); + + // read the dir + int fuseError = fuse_fs_readdir(fFS, path, &readDirBuffer, + &_AddReadDirEntry, offset, cookie); + if (fuseError != 0) + return from_fuse_error(fuseError); + + locker.Lock(); + + // everything went fine + cookie->currentEntryOffset = readDirBuffer.lastOffset; + *countRead = readDirBuffer.entriesRead; + + return B_OK; } status_t FUSEVolume::RewindDir(void* node, void* cookie) { + // TODO: Implement! return B_UNSUPPORTED; } + + +// #pragma mark - + + +ino_t +FUSEVolume::_GenerateNodeID() +{ + ino_t id; + do { + id = fNextNodeID++; + } while (fNodes.Lookup(id) != NULL); + + return id; +} + + +status_t +FUSEVolume::_GetNode(FUSENode* dir, const char* entryName, FUSENode** _node) +{ + while (true) { + AutoLocker locker(fLock); + + FUSENode* node; + status_t error = _InternalGetNode(dir, entryName, &node, locker); + if (error != B_OK) + return error; + + if (node == NULL) + continue; + + ino_t nodeID = node->id; + + locker.Unlock(); + + // get a reference for the caller + void* privateNode; + error = UserlandFS::KernelEmu::get_vnode(fID, nodeID, &privateNode); + if (error != B_OK) + RETURN_ERROR(error); + + locker.Lock(); + + if (privateNode != node) { + // weird, the node changed! + UserlandFS::KernelEmu::put_vnode(fID, nodeID); + _PutNode(node); + continue; + } + + // Put the node reference we got from _InternalGetNode. We've now got + // a reference from get_vnode(). + _PutNode(node); + + *_node = node; + return B_OK; + } +} + + +status_t +FUSEVolume::_InternalGetNode(FUSENode* dir, const char* entryName, + FUSENode** _node, AutoLocker& locker) +{ + // handle special cases + if (strcmp(entryName, ".") == 0) { + // same directory + if (!S_ISDIR(dir->type)) + RETURN_ERROR(B_NOT_A_DIRECTORY); + + dir->refCount++; + *_node = dir; + return B_OK; + } + + if (strcmp(entryName, "..") == 0) { + // parent directory + if (!S_ISDIR(dir->type)) + RETURN_ERROR(B_NOT_A_DIRECTORY); + + FUSEEntry* entry = dir->entries.Head(); + if (entry == NULL) + RETURN_ERROR(B_ENTRY_NOT_FOUND); + + entry->parent->refCount++; + *_node = entry->parent; + return B_OK; + } + + // lookup the entry in the table + FUSEEntry* entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName)); + if (entry != NULL) { + entry->node->refCount++; + *_node = entry->node; + return B_OK; + } + + // construct a path for the entry + char path[B_PATH_NAME_LENGTH]; + size_t pathLen = 0; + status_t error = _BuildPath(dir, entryName, path, pathLen); + if (error != B_OK) + return error; + + locker.Unlock(); + + // stat the path + struct stat st; + int fuseError = fuse_fs_getattr(fFS, path, &st); + if (fuseError != 0) + return from_fuse_error(fuseError); + + locker.Lock(); + + // lookup the entry in the table again + entry = fEntries.Lookup(FUSEEntryRef(dir->id, entryName)); + if (entry != NULL) { + // check whether the node still matches + if (entry->node->id == st.st_ino) { + entry->node->refCount++; + *_node = entry->node; + } else { + // nope, something changed -- return a NULL node and let the caller + // call us again + *_node = NULL; + } + + return B_OK; + } + + // lookup the node in the table + FUSENode* node = NULL; + if (fUseNodeIDs) + node = fNodes.Lookup(st.st_ino); + else + st.st_ino = _GenerateNodeID(); + + if (node == NULL) { + // no node yet -- create one + node = new(std::nothrow) FUSENode(st.st_ino, st.st_mode & S_IFMT); + if (node == NULL) + RETURN_ERROR(B_NO_MEMORY); + + fNodes.Insert(node); + } else { + // get a node reference for the entry + node->refCount++; + } + + // create the entry + entry = FUSEEntry::Create(dir, entryName, node); + if (entry == NULL) { + _PutNode(node); + RETURN_ERROR(B_NO_MEMORY); + } + + fEntries.Insert(entry); + node->entries.Add(entry); + + locker.Unlock(); + + // get a reference for the caller + node->refCount++; + + *_node = node; + return B_OK; +} + + +void +FUSEVolume::_PutNode(FUSENode* node) +{ + if (--node->refCount == 0) { + fNodes.Remove(node); + delete node; + } +} + + +status_t +FUSEVolume::_BuildPath(FUSENode* dir, const char* entryName, char* path, + size_t& pathLen) +{ + // get the directory path + status_t error = _BuildPath(dir, path, pathLen); + if (error != B_OK) + return error; + + if (path[pathLen - 1] != '/') { + path[pathLen++] = '/'; + if (pathLen == B_PATH_NAME_LENGTH) + RETURN_ERROR(B_NAME_TOO_LONG); + } + + // append the entry name + size_t len = strlen(entryName); + if (pathLen + len >= B_PATH_NAME_LENGTH) + RETURN_ERROR(B_NAME_TOO_LONG); + + memcpy(path + pathLen, entryName, len + 1); + pathLen += len; + + return B_OK; +} + + +status_t +FUSEVolume::_BuildPath(FUSENode* node, char* path, size_t& pathLen) +{ + if (node == fRootNode) { + // we hit the root + strcpy(path, "/"); + pathLen = 1; + return B_OK; + } + + // get an entry for the node and get its path + FUSEEntry* entry = node->entries.Head(); + if (entry == NULL) + RETURN_ERROR(B_ENTRY_NOT_FOUND); + + return _BuildPath(entry->parent, entry->name, path, pathLen); +} + + +/*static*/ int +FUSEVolume::_AddReadDirEntry(void* _buffer, const char* name, + const struct stat* st, off_t offset) +{ + ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer; + FUSEVolume* self = buffer->volume; + + AutoLocker _(self->fLock); + + if (offset == 0) { + ERROR(("FUSEVolume::_AddReadDirEntry(): Old interface not " + "supported!\n")); + buffer->error = B_ERROR; + return 1; + } + + if (buffer->entriesRead == buffer->maxEntries) + return 1; + + // compute the entry length and check whether the entry still fits + size_t nameLen = strlen(name); + dirent* dirEntry = (dirent*)((uint8*)buffer->buffer + buffer->usedSize); + size_t entryLen = dirEntry->d_name + nameLen + 1 - (char*)dirEntry; + if (buffer->usedSize + entryLen > buffer->bufferSize) + return 1; + + // create a node and an entry, if necessary + FUSEEntry* entry = self->fEntries.Lookup( + FUSEEntryRef(buffer->directory->id, name)); + if (entry == NULL) { + // get the node + ino_t nodeID = st->st_ino; + FUSENode* node = NULL; + if (self->fUseNodeIDs) + node = self->fNodes.Lookup(st->st_ino); + else + nodeID = self->_GenerateNodeID(); + + if (node == NULL) { + // no node yet -- create one + node = new(std::nothrow) FUSENode(nodeID, st->st_mode & S_IFMT); + if (node == NULL) { + buffer->error = B_NO_MEMORY; + return 1; + } + + self->fNodes.Insert(node); + } else { + // get a node reference for the entry + node->refCount++; + } + + // create the entry + entry = FUSEEntry::Create(buffer->directory, name, node); + if (entry == NULL) { + self->_PutNode(node); + buffer->error = B_NO_MEMORY; + return 1; + } + + self->fEntries.Insert(entry); + node->entries.Add(entry); + } else { + // TODO: Check whether the node's ID matches the one we got! + } + + // fill in the dirent + dirEntry->d_dev = self->fID; + dirEntry->d_ino = entry->node->id; + strcpy(dirEntry->d_name, name); + + if (buffer->entriesRead + 1 < buffer->maxEntries) { + // align the entry length, so the next dirent will be aligned + entryLen = (entryLen + 7) / 8 * 8; + entryLen = std::min(entryLen, buffer->bufferSize - buffer->usedSize); + } + + dirEntry->d_reclen = entryLen; + + // update the buffer + buffer->usedSize += entryLen; + buffer->entriesRead++; + buffer->lastOffset = offset; + + return 0; +} diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.h b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.h index 18984fc6ab..2e0566de85 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.h +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSEVolume.h @@ -5,6 +5,13 @@ #ifndef USERLAND_FS_FUSE_VOLUME_H #define USERLAND_FS_FUSE_VOLUME_H +#include + +#include "Locker.h" + +#include "fuse_fs.h" +#include "FUSEEntry.h" + #include "../Volume.h" @@ -19,6 +26,10 @@ public: dev_t id); virtual ~FUSEVolume(); + status_t Init(); + + void SetFS(fuse_fs* fs) { fFS = fs; } + // FS virtual status_t Mount(const char* device, uint32 flags, const char* parameters, ino_t* rootID); @@ -31,10 +42,6 @@ public: // vnodes virtual status_t Lookup(void* dir, const char* entryName, ino_t* vnid); - virtual status_t GetVNodeType(void* node, int* type); - // Only needs to be implemented when - // the three parameters publish_vnode() is - // used. virtual status_t GetVNodeName(void* node, char* buffer, size_t bufferSize); virtual status_t ReadVNode(ino_t vnid, bool reenter, @@ -111,8 +118,39 @@ public: uint32 count, uint32* countRead); virtual status_t RewindDir(void* node, void* cookie); +private: + struct DirCookie; + struct ReadDirBuffer; + private: inline FUSEFileSystem* _FileSystem() const; + + ino_t _GenerateNodeID(); + + status_t _GetNode(FUSENode* dir, const char* entryName, + FUSENode** _node); + status_t _InternalGetNode(FUSENode* dir, + const char* entryName, FUSENode** _node, + AutoLocker& locker); + void _PutNode(FUSENode* node); + + status_t _BuildPath(FUSENode* dir, const char* entryName, + char* path, size_t& pathLen); + status_t _BuildPath(FUSENode* node, char* path, + size_t& pathLen); + + static int _AddReadDirEntry(void* buffer, const char* name, + const struct stat* st, off_t offset); + +private: + Locker fLock; + fuse_fs* fFS; + FUSEEntryTable fEntries; + FUSENodeTable fNodes; + FUSENode* fRootNode; + ino_t fNextNodeID; + bool fUseNodeIDs; // TODO: Actually read the + // option! }; } // namespace UserlandFS diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/Jamfile b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/Jamfile index c241dfe051..69f6fc71c8 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/Jamfile +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/Jamfile @@ -4,6 +4,7 @@ local userlandFSTop = [ FDirName $(HAIKU_TOP) src add-ons kernel file_systems userlandfs ] ; local userlandFSIncludes = [ PrivateHeaders userlandfs ] ; +UsePrivateHeaders kernel shared ; SubDirSysHdrs [ FDirName $(userlandFSIncludes) ] ; SubDirSysHdrs [ FDirName $(userlandFSIncludes) fuse ] ; SubDirHdrs [ FDirName $(userlandFSIncludes) private ] ; @@ -26,5 +27,5 @@ SharedLibrary libuserlandfs_fuse.so FUSEVolume.cpp : - userlandfs_server + userlandfs_server be ; diff --git a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_api.h b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_api.h index cf554604e6..ebe13d2f0c 100644 --- a/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_api.h +++ b/src/add-ons/kernel/file_systems/userlandfs/server/fuse/fuse_api.h @@ -8,5 +8,6 @@ #define FUSE_USE_VERSION FUSE_VERSION #include +#include #endif // USERLAND_FS_FUSE_FUSE_API_H