diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cd793c0..1167c143 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,7 +210,7 @@ if (PHOTON_ENABLE_FSTACK_DPDK) target_include_directories(photon_obj PUBLIC ${FSTACK_INCLUDE_DIRS}) endif() if (PHOTON_ENABLE_EXTFS) - target_include_directories(photon_obj PUBLIC ${LIBE2FS_INCLUDE_DIRS}) + target_include_directories(photon_obj PUBLIC ${E2FS_INCLUDE_DIRS}) endif() if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0) # This option is enabled by default after -std=c++17 diff --git a/fs/extfs/extfs.cpp b/fs/extfs/extfs.cpp index 714184c6..e11068da 100644 --- a/fs/extfs/extfs.cpp +++ b/fs/extfs/extfs.cpp @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "extfs.h" +#include #include #include #include @@ -53,16 +54,14 @@ static ext2_filsys do_ext2fs_open(io_manager extfs_manager) { &fs // ret_fs ); if (ret) { - auto eno = errno = -parse_extfs_error(nullptr, 0, ret); - LOG_ERROR("failed ext2fs_open ", ERRNO(eno)); - return nullptr; + errno = -parse_extfs_error(nullptr, 0, ret); + LOG_ERRNO_RETURN(0, nullptr, "failed ext2fs_open"); } ret = ext2fs_read_bitmaps(fs); if (ret) { - auto eno = errno = -parse_extfs_error(fs, 0, ret); - LOG_ERROR("failed ext2fs_read_bitmaps ", ERRNO(eno)); + errno = -parse_extfs_error(fs, 0, ret); ext2fs_close(fs); - return nullptr; + LOG_ERRNO_RETURN(0, nullptr, "failed ext2fs_read_bitmaps"); } LOG_INFO("ext2fs opened"); return fs; @@ -938,21 +937,12 @@ static int ino_iter_blocks(ext2_filsys fs, ext2_ino_t ino, if (!ext2fs_inode_has_valid_blocks2(fs, &inode)) { LOG_DEBUG("ext2fs_inode_has_valid_blocks2"); return 0; - // return format->inline_data(&(inode.i_block[0]), - // format->private); } retval = ext2fs_extent_open(fs, ino, &extents); if (retval == EXT2_ET_INODE_NOT_EXTENT) { LOG_DEBUG("EXT2_ET_INODE_NOT_EXTENT"); return 0; - // retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY, - // NULL, walk_block, pdata); - // if (retval) { - // com_err(__func__, retval, _("listing blocks of ino \"%u\""), - // ino); - // } - // return retval; } auto ret = ino_iter_extents(fs, ino, extents, blocks); @@ -974,6 +964,183 @@ static int do_ext2fs_fiemap(ext2_filsys fs, ext2_ino_t ino, struct photon::fs::f return 0; } +static ssize_t do_ext2fs_getxattr(ext2_filsys fs, ext2_ino_t ino, const char *name, void *value, + size_t size) { + struct ext2_xattr_handle *h; + void *ptr; + size_t plen; + errcode_t ret = ext2fs_xattrs_open(fs, ino, &h); + if (ret) + return parse_extfs_error(fs, ino, ret); + DEFER(ext2fs_xattrs_close(&h)); + + ret = ext2fs_xattrs_read(h); + if (ret) + return parse_extfs_error(fs, ino, ret); + // get xattr + ret = ext2fs_xattr_get(h, name, &ptr, &plen); + if (ret) + return parse_extfs_error(fs, ino, ret); + + if (size == 0) { + ret = plen; + } else if (size < plen) { + ret = -ERANGE; + } else { + memcpy(value, ptr, plen); + ret = plen; + } + + ext2fs_free_mem(&ptr); + return ret; +} + +static ssize_t do_ext2fs_getxattr(ext2_filsys fs, const char *path, const char *name, + void *value, size_t size, int follow) { + LOG_DEBUG(VALUE(path)); + ext2_ino_t ino = string_to_inode(fs, path, follow); + if (!ino) return -ENOENT; + + return do_ext2fs_getxattr(fs, ino, name, value, size); +} + +static int count_buffer_space(char *name, char *value EXT2FS_ATTR((unused)), + size_t value_len EXT2FS_ATTR((unused)), void *data) { + auto cnt = (unsigned int *)data; + *cnt = *cnt + strlen(name) + 1; + return 0; +} + +static int copy_names(char *name, char *value EXT2FS_ATTR((unused)), + size_t value_len EXT2FS_ATTR((unused)), void *data) { + auto buf = (char **)data; + size_t name_len = strlen(name); + memcpy(*buf, name, name_len + 1); + *buf = *buf + name_len + 1; + return 0; +} + +static ssize_t do_ext2fs_listxattr(ext2_filsys fs, ext2_ino_t ino, char *list, size_t size) { + struct ext2_xattr_handle *h; + errcode_t ret = ext2fs_xattrs_open(fs, ino, &h); + if (ret) + return parse_extfs_error(fs, ino, ret); + DEFER(ext2fs_xattrs_close(&h)); + + ret = ext2fs_xattrs_read(h); + if (ret) + return parse_extfs_error(fs, ino, ret); + // count buffer space + unsigned int buf_size = 0; + ret = ext2fs_xattrs_iterate(h, count_buffer_space, &buf_size); + if (ret) + return parse_extfs_error(fs, ino, ret); + + if (size == 0) { + return buf_size; + } else if (size < buf_size) { + return -ERANGE; + } + // copy to list + memset(list, 0, size); + ret = ext2fs_xattrs_iterate(h, copy_names, &list); + if (ret) + return parse_extfs_error(fs, ino, ret); + + return buf_size; +} + +static ssize_t do_ext2fs_listxattr(ext2_filsys fs, const char *path, char *list, size_t size, int follow) { + LOG_DEBUG(VALUE(path)); + ext2_ino_t ino = string_to_inode(fs, path, follow); + if (!ino) return -ENOENT; + + return do_ext2fs_listxattr(fs, ino, list, size); +} + +static bool check_namespace(const char *name) { + static std::vector valid_names = { + "gnu.", + "system.posix_acl_default", + "system.posix_acl_access", + "system.richacl", + "security.", + "trusted.", + "system.", + "user.", + }; + + for (auto &n : valid_names) { + if (strncmp(name, n.c_str(), n.size()) == 0) { + return true; + } + } + return false; +} + +static int do_ext2fs_setxattr(ext2_filsys fs, ext2_ino_t ino, const char *name, + const void *value, size_t size, int flags) { + if (!check_namespace(name)) + LOG_ERROR_RETURN(ENOTSUP, -ENOTSUP, "the namespace prefix of '`' is not valid", name); + + struct ext2_xattr_handle *h; + errcode_t ret = ext2fs_xattrs_open(fs, ino, &h); + if (ret) + return parse_extfs_error(fs, ino, ret); + DEFER(ext2fs_xattrs_close(&h)); + + ret = ext2fs_xattrs_read(h); + if (ret) + return parse_extfs_error(fs, ino, ret); + // set xattr + ret = ext2fs_xattr_set(h, name, value, size); + if (ret) + return parse_extfs_error(fs, ino, ret); + // update_ctime + ret = update_xtime(fs, ino, nullptr, EXT_CTIME); + if (ret) return ret; + + return 0; +} + +static int do_ext2fs_setxattr(ext2_filsys fs, const char *path, const char *name, + const void *value, size_t size, int flags, int follow) { + LOG_DEBUG(VALUE(path)); + ext2_ino_t ino = string_to_inode(fs, path, follow); + if (!ino) return -ENOENT; + + return do_ext2fs_setxattr(fs, ino, name, value, size, flags); +} + +static int do_ext2fs_removexattr(ext2_filsys fs, ext2_ino_t ino, const char *name) { + struct ext2_xattr_handle *h; + errcode_t ret = ext2fs_xattrs_open(fs, ino, &h); + if (ret) + return parse_extfs_error(fs, ino, ret); + DEFER(ext2fs_xattrs_close(&h)); + + ret = ext2fs_xattrs_read(h); + if (ret) + return parse_extfs_error(fs, ino, ret); + // remove xattr + // no key found is treat as success + ret = ext2fs_xattr_remove(h, name); + if (ret) + return parse_extfs_error(fs, ino, ret); + // update_ctime + ret = update_xtime(fs, ino, nullptr, EXT_CTIME); + if (ret) return ret; + + return 0; +} + +static int do_ext2fs_removexattr(ext2_filsys fs, const char *path, const char *name, int follow) { + LOG_DEBUG(VALUE(path)); + ext2_ino_t ino = string_to_inode(fs, path, follow); + if (!ino) return -ENOENT; + + return do_ext2fs_removexattr(fs, ino, name); +} #define DO_EXT2FS(func) \ auto ret = func; \ @@ -986,8 +1153,10 @@ static int do_ext2fs_fiemap(ext2_filsys fs, ext2_ino_t ino, struct photon::fs::f namespace photon { namespace fs { +extern io_manager new_io_manager(photon::fs::IFile *file); + class ExtFileSystem; -class ExtFile : public photon::fs::VirtualFile { +class ExtFile : public photon::fs::VirtualFile, public photon::fs::IFileXAttr { public: ExtFile(ext2_file_t _file, int _flags, ExtFileSystem *_fs) : file(_file), flags(_flags), m_fs(_fs) { @@ -1045,6 +1214,18 @@ class ExtFile : public photon::fs::VirtualFile { virtual int fiemap(struct photon::fs::fiemap* map) override { DO_EXT2FS(do_ext2fs_fiemap(fs, ino, map)); } + virtual ssize_t fgetxattr(const char *name, void *value, size_t size) override { + DO_EXT2FS(do_ext2fs_getxattr(fs, ino, name, value, size)); + } + virtual ssize_t flistxattr(char *list, size_t size) override { + DO_EXT2FS(do_ext2fs_listxattr(fs, ino, list, size)); + } + virtual int fsetxattr(const char *name, const void *value, size_t size, int flags) override { + DO_EXT2FS(do_ext2fs_setxattr(fs, ino, name, value, size, flags)); + } + virtual int fremovexattr(const char *name) override { + DO_EXT2FS(do_ext2fs_removexattr(fs, ino, name)); + } virtual photon::fs::IFileSystem *filesystem() override; @@ -1103,7 +1284,7 @@ class ExtDIR : public photon::fs::DIR { }; static const uint64_t kMinimalInoLife = 1L * 1000 * 1000; // ino lives at least 1s -class ExtFileSystem : public photon::fs::IFileSystem { +class ExtFileSystem : public photon::fs::IFileSystem, public photon::fs::IFileSystemXAttr { public: ext2_filsys fs; io_manager extfs_io_manager; @@ -1216,6 +1397,30 @@ class ExtFileSystem : public photon::fs::IFileSystem { } return flush_buffer(); } + ssize_t getxattr(const char *path, const char *name, void *value, size_t size) override { + DO_EXT2FS(do_ext2fs_getxattr(fs, path, name, value, size, 1)); + } + ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size) override { + DO_EXT2FS(do_ext2fs_getxattr(fs, path, name, value, size, 0)); + } + ssize_t listxattr(const char *path, char *list, size_t size) override { + DO_EXT2FS(do_ext2fs_listxattr(fs, path, list, size, 1)); + } + ssize_t llistxattr(const char *path, char *list, size_t size) override { + DO_EXT2FS(do_ext2fs_listxattr(fs, path, list, size, 0)); + } + int setxattr(const char *path, const char *name, const void *value, size_t size, int flags) override { + DO_EXT2FS(do_ext2fs_setxattr(fs, path, name, value, size, flags, 1)); + } + int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) override { + DO_EXT2FS(do_ext2fs_setxattr(fs, path, name, value, size, flags, 0)); + } + int removexattr(const char *path, const char *name) override { + DO_EXT2FS(do_ext2fs_removexattr(fs, path, name, 1)); + } + int lremovexattr(const char *path, const char *name) override { + DO_EXT2FS(do_ext2fs_removexattr(fs, path, name, 0)); + } photon::fs::DIR *opendir(const char *path) override { std::vector<::dirent> dirs; diff --git a/fs/extfs/extfs.h b/fs/extfs/extfs.h index 9d170138..b04ee1c2 100644 --- a/fs/extfs/extfs.h +++ b/fs/extfs/extfs.h @@ -14,13 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ #pragma once -#include #include namespace photon { namespace fs { -io_manager new_io_manager(photon::fs::IFile *file); photon::fs::IFileSystem *new_extfs(photon::fs::IFile *file, bool buffer = true); // make extfs on an prezeroed IFile, @@ -28,4 +26,4 @@ photon::fs::IFileSystem *new_extfs(photon::fs::IFile *file, bool buffer = true); int make_extfs(photon::fs::IFile *file, char *uuid = nullptr); } -} \ No newline at end of file +} diff --git a/fs/extfs/extfs_io.cpp b/fs/extfs/extfs_io.cpp index 3166a455..b7aef906 100644 --- a/fs/extfs/extfs_io.cpp +++ b/fs/extfs/extfs_io.cpp @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "extfs.h" +#include #include #include #include diff --git a/fs/extfs/mkfs.cpp b/fs/extfs/mkfs.cpp index addd93f9..0195ae55 100644 --- a/fs/extfs/mkfs.cpp +++ b/fs/extfs/mkfs.cpp @@ -116,6 +116,7 @@ int do_mkfs(io_manager manager, size_t size, char *uuid) { return ret; } // reserve inodes + ext2fs_inode_alloc_stats2(fs, EXT2_BAD_INO, +1, 0); for (ext2_ino_t i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->super); i++) ext2fs_inode_alloc_stats2(fs, i, +1, 0); ext2fs_mark_ib_dirty(fs); @@ -135,6 +136,8 @@ int do_mkfs(io_manager manager, size_t size, char *uuid) { namespace photon { namespace fs { +extern io_manager new_io_manager(photon::fs::IFile *file); + int make_extfs(photon::fs::IFile *file, char *uuid) { struct stat st; auto ret = file->fstat(&st); @@ -148,4 +151,4 @@ int make_extfs(photon::fs::IFile *file, char *uuid) { } } -} \ No newline at end of file +} diff --git a/fs/extfs/test/test.cpp b/fs/extfs/test/test.cpp index e6ad6374..6731becb 100644 --- a/fs/extfs/test/test.cpp +++ b/fs/extfs/test/test.cpp @@ -1,3 +1,18 @@ +/* +Copyright 2023 The Photon Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ #include "../extfs.h" #include #include @@ -41,7 +56,7 @@ int write_file(photon::fs::IFile *file) { while (aa.size() < FILE_SIZE) aa.append(bb); auto ret = file->pwrite(aa.data(), aa.size(), 0); - if (ret != aa.size()) { + if (ret != (ssize_t)aa.size()) { LOG_ERRNO_RETURN(0, -1, "failed write file ", VALUE(aa.size()), VALUE(ret)) } LOG_DEBUG("write ` byte", ret); @@ -168,6 +183,86 @@ int lutimes(photon::fs::IFileSystem *fs, const char *path, const struct timeval return 0; } +ssize_t getxattr(photon::fs::IFileSystemXAttr *fs, const char *path, const char *name, + char *value, size_t size) { + auto ret = fs->getxattr(path, name, value, size); + if (ret < 0) { + LOG_ERRNO_RETURN(0, ret, "failed getxattr ", VALUE(path), VALUE(name), VALUE(value), + VALUE(size), VALUE(ret)); + } + return ret; +} + +ssize_t listxattr(photon::fs::IFileSystemXAttr *fs, const char *path, char *list, size_t size) { + auto ret = fs->listxattr(path, list, size); + if (ret < 0) { + LOG_ERRNO_RETURN(0, ret, "failed listxattr ", VALUE(path), VALUE(list), VALUE(size), VALUE(ret)); + } + return ret; +} + +int setxattr(photon::fs::IFileSystemXAttr *fs, const char *path, const char *name, + const char *value, size_t size) { + auto ret = fs->setxattr(path, name, value, size, 0); + if (ret) { + LOG_ERRNO_RETURN(0, ret, "failed setxattr ", VALUE(path), VALUE(name), VALUE(value), VALUE(size)); + } + return 0; +} + +int removexattr(photon::fs::IFileSystemXAttr *fs, const char *path, const char *name) { + auto ret = fs->removexattr(path, name); + if (ret) { + LOG_ERRNO_RETURN(0, ret, "failed removexattr ", VALUE(path), VALUE(name)); + } + return 0; +} + +int list_all_xattr(photon::fs::IFileSystemXAttr *fs, const char *path) { + char *list_buf, *value_buf, *key, *value; + auto list_sz = fs->listxattr(path, nullptr, 0); + if (list_sz <= 0) + return list_sz; + list_buf = (char *)malloc(list_sz); + if (!list_buf) + return -1; + list_sz = fs->listxattr(path, list_buf, list_sz); + if (list_sz < 0) + return list_sz; + key = list_buf; + int count = 0; + while (list_sz > 0) { + auto value_sz = fs->getxattr(path, key, nullptr, 0); + if (value_sz > 0) { + value_buf = (char *)malloc(value_sz + 1); + if (value_buf) { + value_sz = fs->getxattr(path, key, value_buf, value_sz); + if (value_sz > 0) { + value_buf[value_sz] = '\0'; + value = value_buf; + count++; + LOG_INFO(VALUE(key), VALUE(value)); + } else { + return -1; + } + free(value_buf); + } else { + return -1; + } + } else if (value_sz == 0) { + count++; + LOG_INFO(VALUE(key)); + } else { + return -1; + } + auto key_sz = strlen(key) + 1; + list_sz -= key_sz; + key += key_sz; + } + free(list_buf); + return count; +} + int opendir(photon::fs::IFileSystem *fs, const char *path) { auto ret = fs->opendir(path); if (ret == nullptr) { @@ -799,6 +894,93 @@ TEST_F(ExtfsTest, Sync) { EXPECT_EQ(0, ret); } +TEST_F(ExtfsTest, Xattr) { + auto file = fs->creat("/test_xattr", 0755); + EXPECT_NE(nullptr, file); + delete file; + file = fs->creat("/test_xattr1", 0755); + EXPECT_NE(nullptr, file); + delete file; + + auto xattr_fs = dynamic_cast(fs); + ASSERT_NE(nullptr, xattr_fs); + + // set xattr + auto ret = setxattr(xattr_fs, "/test_xattr", "user.test1", "test1", 5); + EXPECT_EQ(0, ret); + ret = setxattr(xattr_fs, "/test_xattr", "user.test2", "test2333", 5); + EXPECT_EQ(0, ret); + ret = setxattr(xattr_fs, "/test_xattr", "user.test3", nullptr, 0); + EXPECT_EQ(0, ret); + ret = setxattr(xattr_fs, "/test_xattr1", "ser.test1", "test1", 5); + EXPECT_EQ(-1, ret); + EXPECT_EQ(ENOTSUP, errno); + ret = setxattr(xattr_fs, "/test_xattr2", "user.test1", "test1", 5); + EXPECT_EQ(-1, ret); + EXPECT_EQ(ENOENT, errno); + // list xattr + char buf[128] = {0}; + auto list_sz = listxattr(xattr_fs, "/test_xattr", nullptr, 0); + EXPECT_LT(0, list_sz); + ret = listxattr(xattr_fs, "/test_xattr", buf, list_sz - 1); + EXPECT_EQ(-1, ret); + EXPECT_EQ(ERANGE, errno); + ret = listxattr(xattr_fs, "/test_xattr", buf, sizeof(buf)); + EXPECT_EQ(list_sz, ret); + ret = listxattr(xattr_fs, "/test_xattr1", buf, 0); + EXPECT_EQ(0, ret); + ret = listxattr(xattr_fs, "/test_xattr2", buf, 0); + EXPECT_EQ(-1, ret); + EXPECT_EQ(ENOENT, errno); + // get xattr + char vbuf[64] = {0}; + auto value_sz = getxattr(xattr_fs, "/test_xattr", "user.test1", nullptr, 0); + EXPECT_LT(0, value_sz); + ret = getxattr(xattr_fs, "/test_xattr", "user.test1", vbuf, value_sz - 1); + EXPECT_EQ(-1, ret); + EXPECT_EQ(ERANGE, errno); + ret = getxattr(xattr_fs, "/test_xattr", "user.test1", vbuf, sizeof(vbuf)); + EXPECT_EQ(value_sz, ret); + EXPECT_EQ(0, memcmp(vbuf, "test1", value_sz)); + memset(vbuf, 0, sizeof(vbuf)); + value_sz = getxattr(xattr_fs, "/test_xattr", "user.test2", nullptr, 0); + EXPECT_LT(0, value_sz); + ret = getxattr(xattr_fs, "/test_xattr", "user.test2", vbuf, sizeof(vbuf)); + EXPECT_EQ(value_sz, ret); + EXPECT_EQ(0, memcmp(vbuf, "test2", value_sz)); + ret = getxattr(xattr_fs, "/test_xattr", "ser.test1", vbuf, sizeof(vbuf)); + EXPECT_EQ(-1, ret); + EXPECT_EQ(ENODATA, errno); + ret = getxattr(xattr_fs, "/test_xattr1", "user.test1", vbuf, sizeof(vbuf)); + EXPECT_EQ(-1, ret); + EXPECT_EQ(ENODATA, errno); + ret = getxattr(xattr_fs, "/test_xattr2", "user.test1", vbuf, sizeof(vbuf)); + EXPECT_EQ(-1, ret); + EXPECT_EQ(ENOENT, errno); + // remove xattr + ret = list_all_xattr(xattr_fs, "/test_xattr"); + EXPECT_EQ(3, ret); + ret = removexattr(xattr_fs, "/test_xattr", "user.test1"); + EXPECT_EQ(0, ret); + ret = list_all_xattr(xattr_fs, "/test_xattr"); + EXPECT_EQ(2, ret); + ret = removexattr(xattr_fs, "/test_xattr", "user.test2"); + EXPECT_EQ(0, ret); + ret = list_all_xattr(xattr_fs, "/test_xattr"); + EXPECT_EQ(1, ret); + ret = removexattr(xattr_fs, "/test_xattr", "user.test3"); + EXPECT_EQ(0, ret); + ret = list_all_xattr(xattr_fs, "/test_xattr"); + EXPECT_EQ(0, ret); + ret = removexattr(xattr_fs, "/test_xattr", "ser.test1"); + EXPECT_EQ(0, ret); // no key found is treat as success + ret = removexattr(xattr_fs, "/test_xattr1", "user.test1"); + EXPECT_EQ(0, ret); // no key found is treat as success + ret = removexattr(xattr_fs, "/test_xattr2", "user.test1"); + EXPECT_EQ(-1, ret); + EXPECT_EQ(ENOENT, errno); +} + int main(int argc, char **argv) { photon::init(photon::INIT_EVENT_DEFAULT, photon::INIT_IO_DEFAULT); DEFER(photon::fini();); diff --git a/fs/subfs.cpp b/fs/subfs.cpp index 57847ea9..102037f0 100644 --- a/fs/subfs.cpp +++ b/fs/subfs.cpp @@ -31,10 +31,11 @@ namespace photon { namespace fs { // Sub tree of a file system - class SubFileSystem : public IFileSystem + class SubFileSystem : public IFileSystem, public IFileSystemXAttr { public: IFileSystem* underlayfs = nullptr; + IFileSystemXAttr* underlay_xattrfs = nullptr; char base_path[PATH_MAX]; uint base_path_len = 0; bool ownership = false; @@ -43,6 +44,7 @@ namespace fs { ownership = _ownership; underlayfs = _underlayfs; + underlay_xattrfs = dynamic_cast(_underlayfs); if (!_base_path || !_base_path[0]) // use default relative path { base_path[0] = '\0'; @@ -228,48 +230,63 @@ namespace fs PathCat __(this, path); return underlayfs->mknod(path, mode, dev); } - /* + virtual ssize_t getxattr(const char *path, const char *name, void *value, size_t size) { + if (!underlay_xattrfs) + LOG_ERROR_RETURN(ENOTSUP, -1, "xattr is not supported by underlay fs"); PathCat __(this, path); - return underlayfs->getxattr(path, name, value, size); + return underlay_xattrfs->getxattr(path, name, value, size); } virtual ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size) { + if (!underlay_xattrfs) + LOG_ERROR_RETURN(ENOTSUP, -1, "xattr is not supported by underlay fs"); PathCat __(this, path); - return underlayfs->lgetxattr(path, name, value, size); + return underlay_xattrfs->lgetxattr(path, name, value, size); } virtual ssize_t listxattr(const char *path, char *list, size_t size) { + if (!underlay_xattrfs) + LOG_ERROR_RETURN(ENOTSUP, -1, "xattr is not supported by underlay fs"); PathCat __(this, path); - return underlayfs->listxattr(path, list, size); + return underlay_xattrfs->listxattr(path, list, size); } virtual ssize_t llistxattr(const char *path, char *list, size_t size) { + if (!underlay_xattrfs) + LOG_ERROR_RETURN(ENOTSUP, -1, "xattr is not supported by underlay fs"); PathCat __(this, path); - return underlayfs->llistxattr(path, list, size); + return underlay_xattrfs->llistxattr(path, list, size); } virtual int setxattr(const char *path, const char *name, const void *value, size_t size, int flags) { + if (!underlay_xattrfs) + LOG_ERROR_RETURN(ENOTSUP, -1, "xattr is not supported by underlay fs"); PathCat __(this, path); - return underlayfs->setxattr(path, name, value, size, flags); + return underlay_xattrfs->setxattr(path, name, value, size, flags); } virtual int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) { + if (!underlay_xattrfs) + LOG_ERROR_RETURN(ENOTSUP, -1, "xattr is not supported by underlay fs"); PathCat __(this, path); - return underlayfs->lsetxattr(path, name, value, size, flags); + return underlay_xattrfs->lsetxattr(path, name, value, size, flags); } virtual int removexattr(const char *path, const char *name) { + if (!underlay_xattrfs) + LOG_ERROR_RETURN(ENOTSUP, -1, "xattr is not supported by underlay fs"); PathCat __(this, path); - return underlayfs->removexattr(path, name); + return underlay_xattrfs->removexattr(path, name); } virtual int lremovexattr(const char *path, const char *name) { + if (!underlay_xattrfs) + LOG_ERROR_RETURN(ENOTSUP, -1, "xattr is not supported by underlay fs"); PathCat __(this, path); - return underlayfs->lremovexattr(path, name); + return underlay_xattrfs->lremovexattr(path, name); } - */ }; class SubFile : public ForwardFile_Ownership {