* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "FUSEFileSystem.h"
#include <stdlib.h>
#include <string.h>
#include <new>
#include "fuse_fs.h"
#include "FUSELowLevel.h"
#include "FUSEVolume.h"
#include "../RequestThread.h"
class FUSEFileSystem::ArgumentVector {
private:
enum { MAX_ARGUMENTS = 128 };
public:
ArgumentVector()
:
fBuffer(NULL),
fCount(0)
{
}
~ArgumentVector()
{
free(fBuffer);
}
const char* const* Arguments() const
{
return fArguments;
}
int ArgumentCount() const
{
return fCount;
}
status_t Init(const char* firstElement, const char* arguments)
{
size_t firstElementSize = firstElement != NULL
? strlen(firstElement) + 1 : 0;
fBuffer = (char*)malloc(firstElementSize + strlen(arguments) + 1);
if (fBuffer == NULL)
return B_NO_MEMORY;
fCount = 0;
bool inArgument = false;
int bufferIndex = 0;
if (firstElement != NULL) {
memcpy(fBuffer, firstElement, firstElementSize);
fArguments[fCount++] = fBuffer;
bufferIndex = firstElementSize;
}
for (; *arguments != '\0'; arguments++) {
char c = *arguments;
switch (c) {
case ' ':
case '\t':
case '\r':
case '\n':
if (inArgument) {
fBuffer[bufferIndex++] = '\0';
inArgument = false;
}
break;
case '\\':
c = *++arguments;
if (c == '\0')
break;
default:
if (!inArgument) {
if (fCount == MAX_ARGUMENTS)
break;
fArguments[fCount++] = fBuffer + bufferIndex;
inArgument = true;
}
fBuffer[bufferIndex++] = c;
break;
}
}
if (inArgument)
fBuffer[bufferIndex++] = '\0';
fArguments[fCount] = NULL;
return B_OK;
}
private:
char* fBuffer;
const char* fArguments[MAX_ARGUMENTS + 1];
int fCount;
};
FUSEFileSystem::FUSEFileSystem(const char* fsName,
int (*mainFunction)(int, const char* const*))
:
FileSystem(fsName),
fMainFunction(mainFunction),
fInitThread(-1),
fInitStatus(B_NO_INIT),
fInitSemaphore(-1),
fExitSemaphore(-1),
fInitParameters(NULL),
fUserData(NULL),
fFS(NULL)
{
fClientFSType = CLIENT_FS_FUSE;
fCapabilities.ClearAll();
fCapabilities.Set(FS_CAPABILITY_MOUNT, true);
}
FUSEFileSystem::~FUSEFileSystem()
{
if (fInitSemaphore >= 0)
delete_sem(fInitSemaphore);
if (fExitSemaphore >= 0)
delete_sem(fExitSemaphore);
if (fInitThread >= 0)
wait_for_thread(fInitThread, NULL);
}
status_t
FUSEFileSystem::CreateVolume(Volume** _volume, dev_t id)
{
printf("FUSEFileSystem::CreateVolume()\n");
if (!fVolumes.IsEmpty())
RETURN_ERROR(B_BUSY);
FUSEVolume* volume = new(std::nothrow) FUSEVolume(this, id);
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;
}
status_t
FUSEFileSystem::DeleteVolume(Volume* volume)
{
delete volume;
return B_OK;
}
void
FUSEFileSystem::InitRequestThreadContext(RequestThreadContext* context)
{
do {
static const int staticAssertHolds
= sizeof(fuse_context) <= REQUEST_THREAD_CONTEXT_FS_DATA_SIZE;
struct __staticAssertStruct__ {
char __static_assert_failed__[2 * staticAssertHolds - 1];
};
} while (false);
KernelRequest* request = context->GetRequest();
fuse_context* fuseContext = (fuse_context*)context->GetFSData();
fuseContext->fuse = (struct fuse*)this;
fuseContext->uid = request->user;
fuseContext->gid = request->group;
fuseContext->pid = request->team;
fuseContext->private_data = fFS != NULL ? fFS->userData : NULL;
}
status_t
FUSEFileSystem::InitClientFS(const char* parameters)
{
PRINT(("FUSEFileSystem::InitClientFS()\n"));
fInitSemaphore = create_sem(0, "FUSE init sem");
if (fInitSemaphore < 0)
RETURN_ERROR(fInitSemaphore);
fExitSemaphore = create_sem(0, "FUSE exit sem");
if (fExitSemaphore < 0)
RETURN_ERROR(fExitSemaphore);
fInitStatus = 1;
fInitParameters = parameters;
fInitThread = spawn_thread(&_InitializationThreadEntry,
"FUSE init", B_NORMAL_PRIORITY, this);
if (fInitThread < 0)
RETURN_ERROR(fInitThread);
resume_thread(fInitThread);
PRINT((" waiting for init thread...\n"));
while (acquire_sem(fInitSemaphore) == B_INTERRUPTED) {
}
PRINT((" waiting for init thread done\n"));
fInitSemaphore = -1;
if (fInitStatus > 0)
RETURN_ERROR(B_ERROR);
if (fInitStatus != B_OK)
RETURN_ERROR(fInitStatus);
return B_OK;
}
void
FUSEFileSystem::ExitClientFS(status_t status)
{
fExitStatus = status;
if (fExitSemaphore >= 0)
delete_sem(fExitSemaphore);
if (fInitThread >= 0)
wait_for_thread(fInitThread, NULL);
}
status_t
FUSEFileSystem::FinishInitClientFS(fuse_config* config,
const fuse_operations* ops, size_t opSize, void* userData)
{
PRINT(("FUSEFileSystem::FinishInitClientFS()\n"));
fExitStatus = B_ERROR;
fFUSEConfig = *config;
fInitStatus = _InitClientFS(ops, opSize, userData);
return fInitStatus;
}
status_t
FUSEFileSystem::FinishInitClientFS(fuse_config* config,
const fuse_lowlevel_ops* ops, size_t opSize, void* userData)
{
PRINT(("FUSEFileSystem::FinishInitClientFS()\n"));
fExitStatus = B_ERROR;
fFUSEConfig = *config;
fInitStatus = _InitClientFS(ops, opSize, userData);
return fInitStatus;
}
status_t
FUSEFileSystem::MainLoop(bool multithreaded)
{
PRINT(("FUSEFileSystem::FinishMounting()\n"));
PRINT((" notifying mount thread\n"));
delete_sem(fInitSemaphore);
PRINT((" waiting for unmounting...\n"));
while (acquire_sem(fExitSemaphore) == B_INTERRUPTED) {
}
PRINT((" waiting for unmounting done\n"));
fExitSemaphore = -1;
if (fFS != NULL)
fuse_fs_destroy(fFS);
else
fuse_ll_destroy(&fLowLevelOps, fUserData);
return fExitStatus;
}
status_t
FUSEFileSystem::_InitializationThreadEntry(void* data)
{
return ((FUSEFileSystem*)data)->_InitializationThread();
}
status_t
FUSEFileSystem::_InitializationThread()
{
ArgumentVector args;
status_t error = args.Init(GetName(), fInitParameters);
if (error != B_OK) {
fInitStatus = error;
delete_sem(fInitSemaphore);
return B_OK;
}
fMainFunction(args.ArgumentCount(), args.Arguments());
printf("FUSEFileSystem::_InitializationThread(): main() returned!\n");
if (fInitStatus > 0 && fInitSemaphore >= 0) {
fInitStatus = B_ERROR;
delete_sem(fInitSemaphore);
}
return B_OK;
}
status_t
FUSEFileSystem::_InitClientFS(const fuse_operations* ops, size_t opSize,
void* userData)
{
fFS = fuse_fs_new(ops, opSize, userData);
if (fFS == NULL)
return B_ERROR;
_InitCapabilities();
PRINT(("volume capabilities:\n"));
fVolumeCapabilities.Dump();
PRINT(("node capabilities:\n"));
fNodeCapabilities.Dump();
fConnectionInfo.proto_major = 0;
fConnectionInfo.proto_minor = 0;
fConnectionInfo.async_read = false;
fConnectionInfo.max_write = 64 * 1024;
fConnectionInfo.max_readahead = 64 * 1024;
fConnectionInfo.capable = FUSE_CAP_ATOMIC_O_TRUNC | FUSE_CAP_BIG_WRITES | FUSE_CAP_IOCTL_DIR
| FUSE_CAP_HAIKU_FUSE_EXTENSIONS;
fuse_fs_init(fFS, &fConnectionInfo);
return B_OK;
}
status_t
FUSEFileSystem::_InitClientFS(const fuse_lowlevel_ops* lowLevelOps, size_t lowLevelOpSize,
void* userData)
{
fLowLevelOps = *lowLevelOps;
_InitCapabilities();
PRINT(("volume capabilities:\n"));
fVolumeCapabilities.Dump();
PRINT(("node capabilities:\n"));
fNodeCapabilities.Dump();
fConnectionInfo.proto_major = 0;
fConnectionInfo.proto_minor = 0;
fConnectionInfo.async_read = false;
fConnectionInfo.max_write = 64 * 1024;
fConnectionInfo.max_readahead = 64 * 1024;
fUserData = userData;
fuse_ll_init(&fLowLevelOps, userData, &fConnectionInfo);
return B_OK;
}
void
FUSEFileSystem::_InitCapabilities()
{
fVolumeCapabilities.ClearAll();
fNodeCapabilities.ClearAll();
fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_UNMOUNT, true);
fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_GET_VNODE, true);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LOOKUP, true);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_GET_VNODE_NAME, true);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_PUT_VNODE, true);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_VNODE, true);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_IO, true);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_SET_FLAGS, true);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE_ATTR, false);
if (fFS == NULL) {
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FSYNC, fLowLevelOps.fsync);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_SYMLINK, fLowLevelOps.readlink);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_SYMLINK, fLowLevelOps.symlink);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LINK, fLowLevelOps.link);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_UNLINK, fLowLevelOps.unlink);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME, fLowLevelOps.rename);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_ACCESS, fLowLevelOps.access);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_STAT, fLowLevelOps.getattr);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_STAT, fLowLevelOps.setattr != NULL);
fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_FS_INFO, fLowLevelOps.statfs);
fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_SYNC, fLowLevelOps.fsync);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE, fLowLevelOps.create);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN, fLowLevelOps.open);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE, fLowLevelOps.flush);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_COOKIE, fLowLevelOps.release);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ, fLowLevelOps.read);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE, fLowLevelOps.write);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_DIR, fLowLevelOps.mkdir);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_DIR, fLowLevelOps.rmdir);
bool readDirSupport = fLowLevelOps.opendir != NULL || fLowLevelOps.readdir != NULL;
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_DIR, readDirSupport);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_DIR_COOKIE, readDirSupport);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_DIR, readDirSupport);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_DIR, readDirSupport);
bool hasAttributes = fLowLevelOps.listxattr != NULL;
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_DIR, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR, fLowLevelOps.getxattr);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT, fLowLevelOps.getxattr);
} else {
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.chmod != NULL || fFS->ops.chown != NULL
|| fFS->ops.truncate != NULL || fFS->ops.utimens != NULL
|| fFS->ops.utime != NULL);
fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_FS_INFO, fFS->ops.statfs);
fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_SYNC, fFS->ops.fsync);
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.flush);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_COOKIE, fFS->ops.release);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ, fFS->ops.read);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE, fFS->ops.write);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_DIR, fFS->ops.mkdir);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_DIR, fFS->ops.rmdir);
bool readDirSupport = fFS->ops.opendir != NULL || fFS->ops.readdir != NULL
|| fFS->ops.getdir;
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_DIR, readDirSupport);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_DIR_COOKIE, readDirSupport);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_DIR, readDirSupport);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_DIR, readDirSupport);
bool hasAttributes = fFS->ops.listxattr != NULL;
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE,
hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_DIR, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE, hasAttributes);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR, fFS->ops.getxattr);
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT,
fFS->ops.getxattr);
}
}
status_t
userlandfs_create_file_system(const char* fsName, image_id image,
FileSystem** _fileSystem)
{
printf("userlandfs_create_file_system()\n");
int (*mainFunction)(int argc, const char* const* argv);
status_t error = get_image_symbol(image, "main", B_SYMBOL_TYPE_TEXT,
(void**)&mainFunction);
if (error != B_OK)
return error;
printf("userlandfs_create_file_system(): found main: %p\n", mainFunction);
FUSEFileSystem* fileSystem = new(std::nothrow) FUSEFileSystem(fsName,
mainFunction);
if (fileSystem == NULL)
return B_NO_MEMORY;
*_fileSystem = fileSystem;
return B_OK;
}