* 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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user