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
|
||||
// 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_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.getxattr);
|
||||
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR, fFS->ops.write_attr);
|
||||
|
||||
// fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT,
|
||||
// fFS->ops.stat_attr);
|
||||
fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT,
|
||||
fFS->ops.getxattr);
|
||||
// // 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);
|
||||
|
||||
@@ -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 {
|
||||
FUSEVolume* volume;
|
||||
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 -
|
||||
|
||||
|
||||
|
||||
@@ -108,11 +108,23 @@ public:
|
||||
uint32 count, uint32* countRead);
|
||||
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:
|
||||
struct DirEntryCache;
|
||||
struct DirCookie;
|
||||
struct FileCookie;
|
||||
struct AttrDirCookie;
|
||||
struct AttrCookie;
|
||||
struct ReadDirBuffer;
|
||||
struct LockIterator;
|
||||
struct RWLockableReadLocking;
|
||||
|
||||
Reference in New Issue
Block a user