From 767b3fb6904c9a4287199aa1fca980abccbfb8ef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 09:46:55 +0800 Subject: [PATCH] Auto PR from release/0.7 to main (#306) * Mute an UBSan check error for SCOPED_LOCK macro (#283) * io: support reset photon at fork (#282) In photon context, event engine based module need reset after fork, if exec will not be called after fork. This is implicitly done by pthread_atfork hook. Signed-off-by: yuchen.cc * RPC support graceful shutdown * fs: support xattr for extfs and subfs (#298) Signed-off-by: yuchen.cc * Sequential mutex (#294) seq_mutex * exit(0) requires stdlib.h (#303) * fix e2fs include dir name (#301) Signed-off-by: yuchen.cc * [feat] http client with cert Signed-off-by: zhuangbowei.zbw --------- Signed-off-by: yuchen.cc Signed-off-by: zhuangbowei.zbw Co-authored-by: Bob Chen Co-authored-by: yuchen0cc <91253032+yuchen0cc@users.noreply.github.com> Co-authored-by: Huiba Li Co-authored-by: zhuangbowei.zbw Co-authored-by: Lanzheng Liu --- CMakeLists.txt | 2 +- fs/extfs/extfs.cpp | 239 +++++++++++++++++++++++++++--- fs/extfs/extfs.h | 4 +- fs/extfs/extfs_io.cpp | 1 + fs/extfs/mkfs.cpp | 4 +- fs/extfs/test/test.cpp | 184 ++++++++++++++++++++++- fs/subfs.cpp | 39 +++-- io/signal.cpp | 1 + net/http/client.cpp | 21 ++- net/http/client.h | 3 +- net/http/test/CMakeLists.txt | 4 + net/http/test/client_tls_test.cpp | 89 +++++++++++ thread/thread.cpp | 8 +- thread/thread.h | 14 +- 14 files changed, 565 insertions(+), 48 deletions(-) create mode 100644 net/http/test/client_tls_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6050477c..ca287024 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,7 +212,7 @@ if (PHOTON_ENABLE_FSTACK_DPDK) target_include_directories(photon_obj PRIVATE ${FSTACK_INCLUDE_DIRS}) endif() if (PHOTON_ENABLE_EXTFS) - target_include_directories(photon_obj PRIVATE ${LIBE2FS_INCLUDE_DIRS}) + target_include_directories(photon_obj PRIVATE ${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 42cc9bd1..0195ae55 100644 --- a/fs/extfs/mkfs.cpp +++ b/fs/extfs/mkfs.cpp @@ -136,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); @@ -149,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 { diff --git a/io/signal.cpp b/io/signal.cpp index 63b1a751..7208b811 100644 --- a/io/signal.cpp +++ b/io/signal.cpp @@ -15,6 +15,7 @@ limitations under the License. */ #include "signal.h" +#include #include #include #ifdef __APPLE__ diff --git a/net/http/client.cpp b/net/http/client.cpp index 4feee924..59f08faf 100644 --- a/net/http/client.cpp +++ b/net/http/client.cpp @@ -35,13 +35,15 @@ static constexpr char USERAGENT[] = "PhotonLibOS_HTTP"; class PooledDialer { public: net::TLSContext* tls_ctx = nullptr; + bool tls_ctx_ownership; std::unique_ptr tcpsock; std::unique_ptr tlssock; std::unique_ptr resolver; //etsocket seems not support multi thread very well, use tcp_socket now. need to find out why - PooledDialer(): - tls_ctx(new_tls_context(nullptr, nullptr, nullptr)), + PooledDialer(TLSContext *_tls_ctx) : + tls_ctx(_tls_ctx ? _tls_ctx : new_tls_context(nullptr, nullptr, nullptr)), + tls_ctx_ownership(_tls_ctx == nullptr), resolver(new_default_resolver(kDNSCacheLife)) { auto tcp_cli = new_tcp_socket_client(); auto tls_cli = new_tls_client(tls_ctx, new_tcp_socket_client(), true); @@ -49,7 +51,10 @@ class PooledDialer { tlssock.reset(new_tcp_socket_pool(tls_cli, -1, true)); } - ~PooledDialer() { delete tls_ctx; } + ~PooledDialer() { + if (tls_ctx_ownership) + delete tls_ctx; + } ISocketStream* dial(std::string_view host, uint16_t port, bool secure, uint64_t timeout = -1UL); @@ -114,8 +119,10 @@ class ClientImpl : public Client { PooledDialer m_dialer; CommonHeaders<> m_common_headers; ICookieJar *m_cookie_jar; - ClientImpl(ICookieJar *cookie_jar) : - m_cookie_jar(cookie_jar) {} + ClientImpl(ICookieJar *cookie_jar, TLSContext *tls_ctx) : + m_cookie_jar(cookie_jar), + m_dialer(tls_ctx) { + } using SocketStream_ptr = std::unique_ptr; int redirect(Operation* op) { @@ -273,7 +280,9 @@ class ClientImpl : public Client { } }; -Client* new_http_client(ICookieJar *cookie_jar) { return new ClientImpl(cookie_jar); } +Client* new_http_client(ICookieJar *cookie_jar, TLSContext *tls_ctx) { + return new ClientImpl(cookie_jar, tls_ctx); +} } // namespace http } // namespace net diff --git a/net/http/client.h b/net/http/client.h index 7a767786..350766e3 100644 --- a/net/http/client.h +++ b/net/http/client.h @@ -29,6 +29,7 @@ limitations under the License. namespace photon { namespace net { +class TLSContext; namespace http { class ICookieJar : public Object { @@ -133,7 +134,7 @@ class Client : public Object { }; //A Client without cookie_jar would ignore all response-header "Set-Cookies" -Client* new_http_client(ICookieJar *cookie_jar = nullptr); +Client* new_http_client(ICookieJar *cookie_jar = nullptr, TLSContext *tls_ctx = nullptr); ICookieJar* new_simple_cookie_jar(); diff --git a/net/http/test/CMakeLists.txt b/net/http/test/CMakeLists.txt index 8ac5c878..810990f1 100644 --- a/net/http/test/CMakeLists.txt +++ b/net/http/test/CMakeLists.txt @@ -24,3 +24,7 @@ add_test(NAME cookie_jar_test COMMAND $) add_executable(headers_test headers_test.cpp) target_link_libraries(headers_test PRIVATE photon_shared) add_test(NAME headers_test COMMAND $) + +add_executable(client_tls_test client_tls_test.cpp) +target_link_libraries(client_tls_test PRIVATE photon_shared ${testing_libs}) +add_test(NAME client_tls_test COMMAND $) diff --git a/net/http/test/client_tls_test.cpp b/net/http/test/client_tls_test.cpp new file mode 100644 index 00000000..fce96908 --- /dev/null +++ b/net/http/test/client_tls_test.cpp @@ -0,0 +1,89 @@ +/* +Copyright 2022 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../test/cert-key.cpp" + +using namespace photon; + +int idiot_handler(void*, net::http::Request &req, net::http::Response &resp, std::string_view) { + std::string str; + auto r = req.headers.range(); + auto cl = r.second - r.first + 1; + if (cl > 4096) { + LOG_ERROR_RETURN(0, -1, "RetType failed test"); + } + resp.set_result(200); + resp.headers.content_length(cl); + resp.headers.insert("Test_Handle", "test"); + + str.resize(cl); + memset((void*)str.data(), '0', cl); + resp.write((void*)str.data(), str.size()); + return 0; +} + +TEST(client_tls, basic) { + auto ctx = net::new_tls_context(cert_str, key_str, passphrase_str); + DEFER(delete ctx); + auto tcpserver = net::new_tls_server(ctx, net::new_tcp_socket_server(), true); + DEFER(delete tcpserver); + tcpserver->timeout(1000UL*1000); + tcpserver->setsockopt(SOL_SOCKET, SO_REUSEPORT, 1); + tcpserver->bind(19876, net::IPAddr("127.0.0.1")); + tcpserver->listen(); + + auto server = net::http::new_http_server(); + DEFER(delete server); + server->add_handler({nullptr, &idiot_handler}); + + tcpserver->set_handler(server->get_connection_handler()); + tcpserver->start_loop(); + + auto client = net::http::new_http_client(nullptr, ctx); + DEFER(delete client); + auto op = client->new_operation(net::http::Verb::GET, "https://localhost:19876/test"); + DEFER(delete op); + auto exp_len = 20; + op->req.headers.range(0, exp_len - 1); + op->call(); + EXPECT_EQ(200, op->resp.status_code()); + char buf[4096]; + auto ret = op->resp.read(buf, 4096); + EXPECT_EQ(exp_len, ret); + EXPECT_EQ(true, "test" == op->resp.headers["Test_Handle"]); +} + +int main(int argc, char** arg) { + if (photon::init(photon::INIT_EVENT_DEFAULT, photon::INIT_IO_NONE)) + return -1; + DEFER(photon::fini()); + set_log_output_level(ALOG_DEBUG); + ::testing::InitGoogleTest(&argc, arg); + LOG_DEBUG("test result:`", RUN_ALL_TESTS()); + return 0; +} diff --git a/thread/thread.cpp b/thread/thread.cpp index bde8ab21..f9f1a67e 100644 --- a/thread/thread.cpp +++ b/thread/thread.cpp @@ -1544,12 +1544,12 @@ R"( auto splock = (spinlock*)s_; splock->unlock(); } - int mutex::lock(uint64_t timeout) - { - for (int tries = 0; tries < MaxTries; ++tries) { + int mutex::lock(uint64_t timeout) { + if (try_lock() == 0) return 0; + for (auto re = retries; re; --re) { + thread_yield(); if (try_lock() == 0) return 0; - thread_yield(); } splock.lock(); if (try_lock() == 0) { diff --git a/thread/thread.h b/thread/thread.h index 69f64dce..9bb35347 100644 --- a/thread/thread.h +++ b/thread/thread.h @@ -226,8 +226,9 @@ namespace photon class mutex : protected waitq { public: - int lock(uint64_t timeout = -1); // threads are guaranteed to get the lock - int try_lock(); // in FIFO order, when there's contention + mutex(uint16_t max_retries = 100) : retries(max_retries) { } + int lock(uint64_t timeout = -1); + int try_lock(); void unlock(); ~mutex() { @@ -235,13 +236,20 @@ namespace photon } protected: - static constexpr const int MaxTries = 100; std::atomic owner{nullptr}; + uint16_t retries; spinlock splock; }; + class seq_mutex : protected mutex { + public: + // threads are guaranteed to get the lock in sequental order (FIFO) + seq_mutex() : mutex(0) { } + }; + class recursive_mutex : protected mutex { public: + using mutex::mutex; int lock(uint64_t timeout = -1); int try_lock(); void unlock();