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:
Julian Harnath
2017-02-27 19:58:19 +00:00
parent 1b4f5f9b94
commit 5adf34b0d5
3 changed files with 184 additions and 6 deletions
@@ -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;