FUSE compat: add support for attribute reading
* Implement reading extended file attributes in FUSE modules, using getxattr() getxattr() is a quite limited API, not allowing to specify a read offset. So we read in the entire attribute value into a buffer and store in the cookie. This shouldn't be a problem memory-wise, since xattr implementions usually have limitations regarding attribute size anyway, so it'll rarely be more than a few kilobytes. * Writing, renaming, and removing attributes is not yet implemented
This commit is contained in:
@@ -472,14 +472,14 @@ FUSEFileSystem::_InitCapabilities()
|
|||||||
// // or write_attr() is present
|
// // or write_attr() is present
|
||||||
// bool hasAttributes = (fFS->ops.read_attr || fFS->ops.write_attr);
|
// bool hasAttributes = (fFS->ops.read_attr || fFS->ops.write_attr);
|
||||||
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_ATTR, hasAttributes);
|
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_ATTR, hasAttributes);
|
||||||
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, hasAttributes);
|
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, hasAttributes);
|
||||||
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE_ATTR, false);
|
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE_ATTR, false);
|
||||||
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE, hasAttributes);
|
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_READ_ATTR, fFS->ops.getxattr);
|
||||||
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR, fFS->ops.write_attr);
|
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR, fFS->ops.write_attr);
|
||||||
|
|
||||||
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT,
|
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT,
|
||||||
// fFS->ops.stat_attr);
|
fFS->ops.getxattr);
|
||||||
// // missing: FS_VNODE_CAPABILITY_WRITE_ATTR_STAT
|
// // missing: FS_VNODE_CAPABILITY_WRITE_ATTR_STAT
|
||||||
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME_ATTR, fFS->ops.rename_attr);
|
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME_ATTR, fFS->ops.rename_attr);
|
||||||
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_ATTR, fFS->ops.remove_attr);
|
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_ATTR, fFS->ops.remove_attr);
|
||||||
|
|||||||
@@ -280,6 +280,73 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct FUSEVolume::AttrCookie : RWLockable {
|
||||||
|
public:
|
||||||
|
AttrCookie()
|
||||||
|
:
|
||||||
|
fValue(NULL),
|
||||||
|
fSize(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AttrCookie(const char* value)
|
||||||
|
:
|
||||||
|
fValue(strdup(value)),
|
||||||
|
fSize(strlen(value) + 1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~AttrCookie()
|
||||||
|
{
|
||||||
|
free(fValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValid() const
|
||||||
|
{
|
||||||
|
return fValue != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t Allocate(size_t size)
|
||||||
|
{
|
||||||
|
fValue = (char*)malloc(size);
|
||||||
|
if (fValue == NULL) {
|
||||||
|
fSize = 0;
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
fSize = size;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Read(void* buffer, size_t bufferSize, off_t pos,
|
||||||
|
size_t* bytesRead) const
|
||||||
|
{
|
||||||
|
if (pos < 0 || (uint64)pos > SIZE_MAX || (size_t)pos > fSize - 1) {
|
||||||
|
*bytesRead = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t copySize = fSize - pos;
|
||||||
|
if (copySize > bufferSize)
|
||||||
|
copySize = bufferSize;
|
||||||
|
strlcpy((char*)buffer, &fValue[pos], bufferSize);
|
||||||
|
*bytesRead = copySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* Buffer()
|
||||||
|
{
|
||||||
|
return fValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Size() const
|
||||||
|
{
|
||||||
|
return fSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* fValue;
|
||||||
|
size_t fSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct FUSEVolume::ReadDirBuffer {
|
struct FUSEVolume::ReadDirBuffer {
|
||||||
FUSEVolume* volume;
|
FUSEVolume* volume;
|
||||||
FUSENode* directory;
|
FUSENode* directory;
|
||||||
@@ -2067,6 +2134,105 @@ FUSEVolume::RewindAttrDir(void* _node, void* _cookie)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - attributes
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
FUSEVolume::OpenAttr(void* _node, const char* name, int openMode,
|
||||||
|
void** _cookie)
|
||||||
|
{
|
||||||
|
FUSENode* node = (FUSENode*)_node;
|
||||||
|
|
||||||
|
// lock the node
|
||||||
|
NodeReadLocker nodeLocker(this, node, true);
|
||||||
|
if (nodeLocker.Status() != B_OK)
|
||||||
|
RETURN_ERROR(nodeLocker.Status());
|
||||||
|
|
||||||
|
if (openMode != O_RDONLY) {
|
||||||
|
// Write support currently not implemented
|
||||||
|
RETURN_ERROR(B_UNSUPPORTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
int attrSize = fuse_fs_getxattr(fFS, path, name, NULL, 0);
|
||||||
|
if (attrSize < 0)
|
||||||
|
return attrSize;
|
||||||
|
|
||||||
|
AttrCookie* cookie = new(std::nothrow)AttrCookie();
|
||||||
|
error = cookie->Allocate(attrSize);
|
||||||
|
if (error != B_OK)
|
||||||
|
RETURN_ERROR(error);
|
||||||
|
|
||||||
|
int bytesRead = fuse_fs_getxattr(fFS, path, name, cookie->Buffer(),
|
||||||
|
attrSize);
|
||||||
|
if (bytesRead < 0)
|
||||||
|
return bytesRead;
|
||||||
|
|
||||||
|
*_cookie = cookie;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
FUSEVolume::CloseAttr(void* _node, void* _cookie)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
FUSEVolume::FreeAttrCookie(void* _node, void* _cookie)
|
||||||
|
{
|
||||||
|
delete (AttrCookie*)_cookie;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
FUSEVolume::ReadAttr(void* _node, void* _cookie, off_t pos, void* buffer,
|
||||||
|
size_t bufferSize, size_t* bytesRead)
|
||||||
|
{
|
||||||
|
AttrCookie* cookie = (AttrCookie*)_cookie;
|
||||||
|
|
||||||
|
RWLockableWriteLocker cookieLocker(this, cookie);
|
||||||
|
|
||||||
|
if (!cookie->IsValid())
|
||||||
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
|
cookie->Read(buffer, bufferSize, pos, bytesRead);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
FUSEVolume::ReadAttrStat(void* _node, void* _cookie, struct stat* st)
|
||||||
|
{
|
||||||
|
AttrCookie* cookie = (AttrCookie*)_cookie;
|
||||||
|
|
||||||
|
RWLockableWriteLocker cookieLocker(this, cookie);
|
||||||
|
|
||||||
|
if (!cookie->IsValid())
|
||||||
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
|
st->st_size = cookie->Size();
|
||||||
|
st->st_type = B_RAW_TYPE;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -108,11 +108,23 @@ public:
|
|||||||
uint32 count, uint32* countRead);
|
uint32 count, uint32* countRead);
|
||||||
virtual status_t RewindAttrDir(void* node, void* cookie);
|
virtual status_t RewindAttrDir(void* node, void* cookie);
|
||||||
|
|
||||||
|
// attributes
|
||||||
|
virtual status_t OpenAttr(void* node, const char* name,
|
||||||
|
int openMode, void** cookie);
|
||||||
|
virtual status_t CloseAttr(void* node, void* cookie);
|
||||||
|
virtual status_t FreeAttrCookie(void* node, void* cookie);
|
||||||
|
virtual status_t ReadAttr(void* node, void* cookie,
|
||||||
|
off_t pos, void* buffer, size_t bufferSize,
|
||||||
|
size_t* bytesRead);
|
||||||
|
virtual status_t ReadAttrStat(void* node, void* cookie,
|
||||||
|
struct stat* st);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct DirEntryCache;
|
struct DirEntryCache;
|
||||||
struct DirCookie;
|
struct DirCookie;
|
||||||
struct FileCookie;
|
struct FileCookie;
|
||||||
struct AttrDirCookie;
|
struct AttrDirCookie;
|
||||||
|
struct AttrCookie;
|
||||||
struct ReadDirBuffer;
|
struct ReadDirBuffer;
|
||||||
struct LockIterator;
|
struct LockIterator;
|
||||||
struct RWLockableReadLocking;
|
struct RWLockableReadLocking;
|
||||||
|
|||||||
Reference in New Issue
Block a user