* Added support for the zero offset method for the readdir() hook's filler

callback.
* Added support for the deprecated getdir() hook.
* Fixed various bugs.

Listing the root directory with sshfs does now work. For subdirectories
stat()ing the entries seems to fail, which might be due to the yet missing
open() support.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29623 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2009-03-20 16:13:12 +00:00
parent b4d95b2afa
commit 8d0f474ca1
3 changed files with 318 additions and 78 deletions

View File

@ -422,12 +422,14 @@ FUSEFileSystem::_InitCapabilities()
// // 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);
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_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);
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);
//
// // attribute directory operations
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR,

View File

@ -25,12 +25,133 @@ from_fuse_error(int error)
}
struct FUSEVolume::DirEntryCache {
DirEntryCache()
:
fEntries(NULL),
fNames(NULL),
fEntryCount(0),
fEntryCapacity(0),
fNamesSize(0),
fNamesCapacity(0)
{
}
~DirEntryCache()
{
free(fEntries);
free(fNames);
}
status_t AddEntry(ino_t nodeID, const char* name)
{
// resize entries array, if full
if (fEntryCount == fEntryCapacity) {
// entries array full -- resize
uint32 newCapacity = std::max(fEntryCapacity * 2, (uint32)8);
Entry* newEntries = (Entry*)realloc(fEntries,
newCapacity * sizeof(Entry));
if (newEntries == NULL)
return B_NO_MEMORY;
fEntries = newEntries;
fEntryCapacity = newCapacity;
}
// resize names buffer, if full
size_t nameSize = strlen(name) + 1;
if (fNamesSize + nameSize > fNamesCapacity) {
size_t newCapacity = std::max(fNamesCapacity * 2, (size_t)256);
while (newCapacity < fNamesSize + nameSize)
newCapacity *= 2;
char* names = (char*)realloc(fNames, newCapacity);
if (names == NULL)
return B_NO_MEMORY;
fNames = names;
fNamesCapacity = newCapacity;
}
// add the entry
fEntries[fEntryCount].nodeID = nodeID;
fEntries[fEntryCount].nameOffset = fNamesSize;
fEntries[fEntryCount].nameSize = nameSize;
fEntryCount++;
memcpy(fNames + fNamesSize, name, nameSize);
fNamesSize += nameSize;
return B_OK;
}
uint32 CountEntries() const
{
return fEntryCount;
}
size_t DirentLength(uint32 index) const
{
const Entry& entry = fEntries[index];
return sizeof(dirent) + entry.nameSize - 1;
}
bool ReadDirent(uint32 index, dev_t volumeID, bool align, dirent* buffer,
size_t bufferSize) const
{
if (index >= fEntryCount)
return false;
const Entry& entry = fEntries[index];
// get and check the size
size_t size = sizeof(dirent) + entry.nameSize - 1;
if (size > bufferSize)
return false;
// align the size, if requested
if (align)
size = std::min(bufferSize, (size + 7) / 8 * 8);
// fill in the dirent
buffer->d_dev = volumeID;
buffer->d_ino = entry.nodeID;
memcpy(buffer->d_name, fNames + entry.nameOffset, entry.nameSize);
buffer->d_reclen = size;
return true;
}
private:
struct Entry {
ino_t nodeID;
uint32 nameOffset;
uint32 nameSize;
};
private:
Entry* fEntries;
char* fNames;
uint32 fEntryCount;
uint32 fEntryCapacity;
size_t fNamesSize;
size_t fNamesCapacity;
};
struct FUSEVolume::DirCookie : fuse_file_info {
off_t currentEntryOffset;
union {
off_t currentEntryOffset;
uint32 currentEntryIndex;
};
DirEntryCache* entryCache;
bool getdirInterface;
DirCookie()
:
currentEntryOffset(0)
currentEntryOffset(0),
entryCache(NULL),
getdirInterface(false)
{
flags = 0;
fh_old = 0;
@ -41,6 +162,11 @@ struct FUSEVolume::DirCookie : fuse_file_info {
fh = 0;
lock_owner = 0;
}
~DirCookie()
{
delete entryCache;
}
};
@ -53,12 +179,12 @@ struct FUSEVolume::ReadDirBuffer {
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)
:
volume(volume),
directory(directory),
cookie(cookie),
buffer(buffer),
@ -66,7 +192,6 @@ struct FUSEVolume::ReadDirBuffer {
usedSize(0),
entriesRead(0),
maxEntries(maxEntries),
lastOffset(0),
error(B_OK)
{
}
@ -274,6 +399,9 @@ FUSEVolume::ReadVNode(ino_t vnid, bool reenter, void** _node, int* type,
RETURN_ERROR(B_ENTRY_NOT_FOUND);
node->refCount++;
*_node = node;
*type = node->type;
*flags = 0;
*_capabilities = _FileSystem()->GetNodeCapabilities();
return B_OK;
@ -563,10 +691,16 @@ PRINT(("FUSEVolume::OpenDir(%p (%lld), %p)\n", node, node->id, _cookie));
locker.Unlock();
// open the dir
int fuseError = fuse_fs_opendir(fFS, path, cookie);
if (fuseError != 0)
return from_fuse_error(fuseError);
if (fFS->ops.readdir == NULL && fFS->ops.getdir != NULL) {
// no open call -- the FS only supports the deprecated getdir()
// interface
cookie->getdirInterface = true;
} else {
// open the dir
int fuseError = fuse_fs_opendir(fFS, path, cookie);
if (fuseError != 0)
return from_fuse_error(fuseError);
}
cookieDeleter.Detach();
*_cookie = cookie;
@ -590,6 +724,9 @@ FUSEVolume::FreeDirCookie(void* _node, void* _cookie)
ObjectDeleter<DirCookie> cookieDeleter(cookie);
if (cookie->getdirInterface)
return B_OK;
AutoLocker<Locker> locker(fLock);
// get a path for the node
@ -612,47 +749,93 @@ FUSEVolume::FreeDirCookie(void* _node, void* _cookie)
status_t
FUSEVolume::ReadDir(void* _node, void* _cookie, void* buffer, size_t bufferSize,
uint32 count, uint32* countRead)
uint32 count, uint32* _countRead)
{
PRINT(("FUSEVolume::ReadDir(%p, %p, %p, %lu, %ld)\n", _node, _cookie, buffer,
bufferSize, count));
*_countRead = 0;
FUSENode* node = (FUSENode*)_node;
DirCookie* cookie = (DirCookie*)_cookie;
uint32 countRead = 0;
status_t readDirError = B_OK;
AutoLocker<Locker> locker(fLock);
ReadDirBuffer readDirBuffer(this, node, cookie, buffer, bufferSize, count);
if (cookie->entryCache == NULL) {
// We don't have an entry cache (yet), so we need to ask the client
// file system to read the directory.
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);
// 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;
off_t offset = cookie->currentEntryOffset;
locker.Unlock();
locker.Unlock();
// read the dir
int fuseError = fuse_fs_readdir(fFS, path, &readDirBuffer,
&_AddReadDirEntry, offset, cookie);
if (fuseError != 0)
return from_fuse_error(fuseError);
// read the dir
int fuseError;
if (cookie->getdirInterface) {
PRINT((" using getdir() interface\n"));
fuseError = fFS->ops.getdir(path, (fuse_dirh_t)&readDirBuffer,
&_AddReadDirEntryGetDir);
} else {
PRINT((" using readdir() interface\n"));
fuseError = fuse_fs_readdir(fFS, path, &readDirBuffer,
&_AddReadDirEntry, offset, cookie);
}
if (fuseError != 0)
return from_fuse_error(fuseError);
locker.Lock();
locker.Lock();
// everything went fine
cookie->currentEntryOffset = readDirBuffer.lastOffset;
*countRead = readDirBuffer.entriesRead;
countRead = readDirBuffer.entriesRead;
readDirError = readDirBuffer.error;
}
return B_OK;
if (cookie->entryCache != NULL) {
// we're using an entry cache -- read into the buffer what we can
dirent* entryBuffer = (dirent*)buffer;
while (countRead < count
&& cookie->entryCache->ReadDirent(cookie->currentEntryIndex, fID,
countRead + 1 < count, entryBuffer, bufferSize)) {
countRead++;
cookie->currentEntryIndex++;
bufferSize -= entryBuffer->d_reclen;
entryBuffer
= (dirent*)((uint8*)entryBuffer + entryBuffer->d_reclen);
}
}
*_countRead = countRead;
return countRead > 0 ? B_OK : readDirError;
}
status_t
FUSEVolume::RewindDir(void* node, void* cookie)
FUSEVolume::RewindDir(void* _node, void* _cookie)
{
// TODO: Implement!
return B_UNSUPPORTED;
PRINT(("FUSEVolume::RewindDir(%p, %p)\n", _node, _cookie));
DirCookie* cookie = (DirCookie*)_cookie;
AutoLocker<Locker> locker(fLock);
if (cookie->getdirInterface) {
delete cookie->entryCache;
cookie->entryCache = NULL;
cookie->currentEntryIndex = 0;
} else {
cookie->currentEntryOffset = 0;
}
return B_OK;
}
@ -699,6 +882,8 @@ FUSEVolume::_GetNode(FUSENode* dir, const char* entryName, FUSENode** _node)
if (privateNode != node) {
// weird, the node changed!
ERROR(("FUSEVolume::_GetNode(): cookie for node %lld changed: "
"expected: %p, got: %p\n", nodeID, node, privateNode));
UserlandFS::KernelEmu::put_vnode(fID, nodeID);
_PutNode(node);
continue;
@ -884,48 +1069,75 @@ FUSEVolume::_AddReadDirEntry(void* _buffer, const char* name,
const struct stat* st, off_t offset)
{
ReadDirBuffer* buffer = (ReadDirBuffer*)_buffer;
FUSEVolume* self = buffer->volume;
return buffer->volume->_AddReadDirEntry(buffer, name, st->st_mode & S_IFMT,
st->st_ino, offset);
}
AutoLocker<Locker> _(self->fLock);
if (offset == 0) {
ERROR(("FUSEVolume::_AddReadDirEntry(): Old interface not "
"supported!\n"));
buffer->error = B_ERROR;
return 1;
/*static*/ int
FUSEVolume::_AddReadDirEntryGetDir(fuse_dirh_t handle, const char* name,
int type, ino_t nodeID)
{
ReadDirBuffer* buffer = (ReadDirBuffer*)handle;
return buffer->volume->_AddReadDirEntry(buffer, name, type & S_IFMT, nodeID,
0);
}
int
FUSEVolume::_AddReadDirEntry(ReadDirBuffer* buffer, const char* name, int type,
ino_t nodeID, off_t offset)
{
PRINT(("FUSEVolume::_AddReadDirEntry(%p, \"%s\", %#x, %lld, %lld\n", buffer,
name, type, nodeID, offset));
AutoLocker<Locker> _(fLock);
size_t entryLen;
if (offset != 0) {
// does the caller want more entries?
if (buffer->entriesRead == buffer->maxEntries)
return 1;
// compute the entry length and check whether the entry still fits
entryLen = sizeof(dirent) + strlen(name);
if (buffer->usedSize + entryLen > buffer->bufferSize)
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) {
ino_t dirID = buffer->directory->id;
FUSEEntry* entry;
if (strcmp(name, ".") == 0) {
// current dir entry
nodeID = dirID;
} else if (strcmp(name, "..") == 0) {
// parent dir entry
FUSEEntry* parentEntry = buffer->directory->entries.Head();
if (parentEntry == NULL) {
ERROR(("FUSEVolume::_AddReadDirEntry(): dir %lld has no entry!\n",
dirID));
return 0;
}
nodeID = parentEntry->parent->id;
} else if ((entry = fEntries.Lookup(FUSEEntryRef(dirID, name))) == NULL) {
// get the node
ino_t nodeID = st->st_ino;
FUSENode* node = NULL;
if (self->fUseNodeIDs)
node = self->fNodes.Lookup(st->st_ino);
if (fUseNodeIDs)
node = fNodes.Lookup(nodeID);
else
nodeID = self->_GenerateNodeID();
nodeID = _GenerateNodeID();
if (node == NULL) {
// no node yet -- create one
node = new(std::nothrow) FUSENode(nodeID, st->st_mode & S_IFMT);
node = new(std::nothrow) FUSENode(nodeID, type);
if (node == NULL) {
buffer->error = B_NO_MEMORY;
return 1;
}
PRINT((" -> create node: %p, id: %lld\n", node, nodeID));
self->fNodes.Insert(node);
fNodes.Insert(node);
} else {
// get a node reference for the entry
node->refCount++;
@ -934,34 +1146,54 @@ FUSEVolume::_AddReadDirEntry(void* _buffer, const char* name,
// create the entry
entry = FUSEEntry::Create(buffer->directory, name, node);
if (entry == NULL) {
self->_PutNode(node);
_PutNode(node);
buffer->error = B_NO_MEMORY;
return 1;
}
self->fEntries.Insert(entry);
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 (offset == 0) {
// cache the entry
if (buffer->cookie->entryCache == NULL) {
// no cache yet -- create it
buffer->cookie->entryCache = new(std::nothrow) DirEntryCache;
if (buffer->cookie->entryCache == NULL) {
buffer->error = B_NO_MEMORY;
return 1;
}
}
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);
status_t error = buffer->cookie->entryCache->AddEntry(nodeID, name);
if (error != B_OK) {
buffer->error = error;
return 1;
}
} else {
// fill in the dirent
dirent* dirEntry = (dirent*)((uint8*)buffer->buffer + buffer->usedSize);
dirEntry->d_dev = fID;
dirEntry->d_ino = nodeID;
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->cookie->currentEntryOffset = offset;
}
dirEntry->d_reclen = entryLen;
// update the buffer
buffer->usedSize += entryLen;
buffer->entriesRead++;
buffer->lastOffset = offset;
return 0;
}

View File

@ -119,6 +119,7 @@ public:
virtual status_t RewindDir(void* node, void* cookie);
private:
struct DirEntryCache;
struct DirCookie;
struct ReadDirBuffer;
@ -141,6 +142,11 @@ private:
static int _AddReadDirEntry(void* buffer, const char* name,
const struct stat* st, off_t offset);
static int _AddReadDirEntryGetDir(fuse_dirh_t handle,
const char* name, int type, ino_t nodeID);
int _AddReadDirEntry(ReadDirBuffer* buffer,
const char* name, int type, ino_t nodeID,
off_t offset);
private:
Locker fLock;