* More work on the file system initialization. Mounting/unmounting works now

with sshfs.
* Added basic node and entry management, which we need, since the FUSE
  interface works with paths only, while our VFS plays with node IDs and
  node cookies.
* Implemented most of the mandatory hooks (vnode operations, lookup, read stat,
  open/close/read dir). I was hoping to get directory listings with sshfs now,
  but as I had to find out, it implements the deprecated getdir() while we
  only support the new {open,read,release}dir() interface yet.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29619 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-03-20 01:26:53 +00:00
parent 02442071c3
commit f06f733751
6 changed files with 867 additions and 36 deletions
@@ -10,6 +10,7 @@
#include <new>
#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
@@ -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
@@ -5,8 +5,73 @@
#include "FUSEVolume.h"
#include <dirent.h>
#include <algorithm>
#include <fs_info.h>
#include <AutoDeleter.h>
#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<Locker> _(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<Locker> _(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<Locker> _(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<Locker> _(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> 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<DirCookie> cookieDeleter(cookie);
AutoLocker<Locker> 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<DirCookie> cookieDeleter(cookie);
AutoLocker<Locker> 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> 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> 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>& 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<Locker> _(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;
}
@@ -5,6 +5,13 @@
#ifndef USERLAND_FS_FUSE_VOLUME_H
#define USERLAND_FS_FUSE_VOLUME_H
#include <AutoLocker.h>
#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>& 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
@@ -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
:
<nogrist>userlandfs_server
<nogrist>userlandfs_server be
;
@@ -8,5 +8,6 @@
#define FUSE_USE_VERSION FUSE_VERSION
#include <fuse.h>
#include <fuse_lowlevel.h>
#endif // USERLAND_FS_FUSE_FUSE_API_H