diff --git a/src/image_file.h b/src/image_file.h index c4eea1c3..786f3347 100644 --- a/src/image_file.h +++ b/src/image_file.h @@ -41,10 +41,15 @@ static std::string SEALED_FILE_NAME = "overlaybd.sealed"; class ImageFile : public photon::fs::ForwardFile { public: - ImageFile(ImageConfigNS::ImageConfig &_conf, ImageService &is) + ImageFile(ImageConfigNS::ImageConfig &_conf, ImageService &is, const std::string &dev_id) : ForwardFile(nullptr), image_service(is), m_lower_file(nullptr) { conf.CopyFrom(_conf, conf.GetAllocator()); m_exception = ""; + if(image_service.register_image_file(dev_id, this) != 0) { // register itself + set_failed("duplicated dev id: " + dev_id); + return; + } + m_dev_id = dev_id; m_status = init_image_file(); if (m_status == 1) { struct stat st; @@ -55,6 +60,7 @@ class ImageFile : public photon::fs::ForwardFile { ~ImageFile() { m_status = -1; + image_service.unregister_image_file(m_dev_id); // unregister itself if (dl_thread_jh != nullptr) photon::thread_join(dl_thread_jh); delete m_prefetcher; @@ -117,6 +123,7 @@ class ImageFile : public photon::fs::ForwardFile { // load new config file to get the snapshot layer path // open new upper layer // restack() current RW layer as snapshot layer + return 0; } private: @@ -127,6 +134,7 @@ class ImageFile : public photon::fs::ForwardFile { ImageService &image_service; photon::fs::IFile *m_lower_file = nullptr; photon::fs::IFile *m_upper_file = nullptr; + std::string m_dev_id = ""; int init_image_file(); template void set_failed(const Ts&...xs); diff --git a/src/image_service.cpp b/src/image_service.cpp index 2c124e44..df918b07 100644 --- a/src/image_service.cpp +++ b/src/image_service.cpp @@ -470,7 +470,7 @@ bool ImageService::enable_acceleration() { } } -ImageFile *ImageService::create_image_file(const char *config_path) { +ImageFile *ImageService::create_image_file(const char *config_path, const std::string &dev_id) { ImageConfigNS::GlobalConfig defaultDlCfg; if (!defaultDlCfg.ParseJSON(m_config_path)) { LOG_WARN("default download config parse failed, ignore"); @@ -493,7 +493,7 @@ ImageFile *ImageService::create_image_file(const char *config_path) { } auto resFile = cfg.resultFile(); - ImageFile *ret = new ImageFile(cfg, *this); + ImageFile *ret = new ImageFile(cfg, *this, dev_id); if (ret->m_status <= 0) { std::string data = "failed:" + ret->m_exception; set_result_file(resFile, data); @@ -505,6 +505,29 @@ ImageFile *ImageService::create_image_file(const char *config_path) { return ret; } +int ImageService::register_image_file(const std::string& dev_id, ImageFile* file) { + if (dev_id.empty()) + return 0; + if(find_image_file(dev_id) != nullptr) + LOG_ERROR_RETURN(0, -1, "dev id exists: `", dev_id); + m_image_files[dev_id] = file; + LOG_INFO("Registered image file for dev_id: `", dev_id); + return 0; +} + +int ImageService::unregister_image_file(const std::string& dev_id) { + if (dev_id.empty()) + return 0; + m_image_files.erase(dev_id); + LOG_INFO("Unregistered image file for dev_id: `", dev_id); + return 0; +} + +ImageFile* ImageService::find_image_file(const std::string& dev_id) { + auto it = m_image_files.find(dev_id); + return (it != m_image_files.end()) ? it->second : nullptr; +} + ImageService::ImageService(const char *config_path) { m_config_path = config_path ? config_path : DEFAULT_CONFIG_PATH; } diff --git a/src/image_service.h b/src/image_service.h index 0bbbd2c5..cf9d39dd 100644 --- a/src/image_service.h +++ b/src/image_service.h @@ -22,6 +22,7 @@ #include "overlaybd/cache/gzip_cache/cached_fs.h" #include #include +#include using namespace photon::fs; @@ -54,9 +55,12 @@ class ImageService { ImageService(const char *config_path = nullptr); ~ImageService(); int init(); - ImageFile *create_image_file(const char *image_config_path); + ImageFile *create_image_file(const char *image_config_path, const std::string &dev_id); // bool enable_acceleration(GlobalFs *global_fs, ImageConfigNS::P2PConfig conf); bool enable_acceleration(); + int register_image_file(const std::string& dev_id, ImageFile* file); + int unregister_image_file(const std::string& dev_id); + ImageFile* find_image_file(const std::string& dev_id); ImageConfigNS::GlobalConfig global_conf; @@ -69,6 +73,7 @@ class ImageService { std::pair reload_auth(const char *remote_path); void set_result_file(std::string &filename, std::string &data); std::string m_config_path; + std::unordered_map m_image_files; // dev_id -> ImageFile* }; ImageService *create_image_service(const char *config_path = nullptr); diff --git a/src/main.cpp b/src/main.cpp index 0498e4d1..9b0fe680 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ #include "version.h" #include "image_file.h" #include "image_service.h" +#include "tools/comm_func.h" #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include class TCMUDevLoop; @@ -46,6 +48,7 @@ struct obd_dev { uint32_t inflight; std::thread *work; photon::semaphore start, end; + std::string dev_id; }; struct handle_args { @@ -303,7 +306,7 @@ class TCMUDevLoop { }; static char *tcmu_get_path(struct tcmu_device *dev) { - char *config = strchr(tcmu_dev_get_cfgstring(dev), '/'); + char *config = strchr(tcmu_dev_get_cfgstring(dev), '/'); // dev_config=overlaybd/[;] if (!config) { LOG_ERROR("no configuration found in cfgstring"); return NULL; @@ -314,16 +317,18 @@ static char *tcmu_get_path(struct tcmu_device *dev) { } static int dev_open(struct tcmu_device *dev) { - char *config = tcmu_get_path(dev); + char *config = tcmu_get_path(dev); // [;] LOG_INFO("dev open `", config); if (!config) { LOG_ERROR_RETURN(0, -EPERM, "get image config path failed"); } + std::string config_path, dev_id; + parse_config_and_dev_id(config, config_path, dev_id); struct timeval start; gettimeofday(&start, NULL); - ImageFile *file = imgservice->create_image_file(config); + ImageFile *file = imgservice->create_image_file(config_path.c_str(), dev_id); if (file == nullptr) { LOG_ERROR_RETURN(0, -EPERM, "create image file failed"); } @@ -332,6 +337,7 @@ static int dev_open(struct tcmu_device *dev) { odev->aio_pending_wakeups = 0; odev->inflight = 0; odev->file = file; + odev->dev_id = dev_id; tcmu_dev_set_private(dev, odev); tcmu_dev_set_block_size(dev, file->block_size); @@ -366,7 +372,7 @@ static int dev_open(struct tcmu_device *dev) { gettimeofday(&end, NULL); uint64_t elapsed = 1000000UL * (end.tv_sec - start.tv_sec) + end.tv_usec - start.tv_usec; - LOG_INFO("dev opened `, time cost ` ms", config, elapsed / 1000); + LOG_INFO("dev opened `, time cost ` ms", config_path.c_str(), elapsed / 1000); return 0; } diff --git a/src/overlaybd/lsmt/file.cpp b/src/overlaybd/lsmt/file.cpp index 1cacc05e..8b0b728d 100644 --- a/src/overlaybd/lsmt/file.cpp +++ b/src/overlaybd/lsmt/file.cpp @@ -948,7 +948,7 @@ class LSMTFile : public LSMTReadOnlyFile { } auto p = new LSMTReadOnlyFile; p->m_index = new_index; - p->m_files = {nullptr, m_files.back()}; + p->m_files = {m_files.back()};//p->m_files = {nullptr, m_files.back()}; p->m_vsize = m_vsize; p->m_file_ownership = m_file_ownership; m_file_ownership = false; @@ -1025,8 +1025,8 @@ class LSMTFile : public LSMTReadOnlyFile { auto u = top_layer; LOG_INFO("m_files.insert new layer: file ptr: 0x`", u->m_files[0]); - // m_files[m_rw_tag] = ((LSMTReadOnlyFile*)gc_layer)->m_files[1]; - m_files.insert(m_files.begin(), ((LSMTReadOnlyFile*)gc_layer)->m_files[1]); + // m_files[m_rw_tag] = ((LSMTReadOnlyFile*)gc_layer)->m_files[0]; + m_files.insert(m_files.begin(), ((LSMTReadOnlyFile*)gc_layer)->m_files[0]); m_rw_tag++; m_files[m_rw_tag] = u->m_files[0]; // fnew_layer; if (m_file_ownership){ diff --git a/src/test/image_service_test.cpp b/src/test/image_service_test.cpp index a5b81a17..392f94b1 100644 --- a/src/test/image_service_test.cpp +++ b/src/test/image_service_test.cpp @@ -26,11 +26,15 @@ #include "photon/net/curl.h" #include "../version.h" #include +#include - +#include #include #include "../image_service.cpp" +#include "../image_service.h" +#include "../image_file.h" +#include "../tools/comm_func.h" char *test_ua = nullptr; @@ -160,7 +164,98 @@ TEST(http_client, user_agent) { EXPECT_EQ(true, buf == "success"); } +class DevIDTest : public ::testing::Test { +public: + ImageService *imgservice; + const std::string test_dir = "/tmp/overlaybd"; + const std::string global_config_path = test_dir + "/global_config.json"; + const std::string image_config_path = test_dir + "/image_config.json"; + const std::string global_config_content = R"delimiter({ + "enableAudit": false, + "logPath": "", + "p2pConfig": { + "enable": false, + "address": "localhost:64210" + } +})delimiter"; + const std::string image_config_content = R"delimiter({ + "lowers" : [ + { + "file" : "/opt/overlaybd/baselayers/ext4_64" + } + ] +})delimiter"; + + virtual void SetUp() override { + // set_log_output_level(0); + system(("mkdir -p " + test_dir).c_str()); + + system(("echo \'" + global_config_content + "\' > " + global_config_path).c_str()); + LOG_INFO("Global config file: "); + system(("cat " + global_config_path).c_str()); + + system(("echo \'" + image_config_content + "\' > " + image_config_path).c_str()); + LOG_INFO("Image config file: "); + system(("cat " + image_config_path).c_str()); + + imgservice = create_image_service(global_config_path.c_str()); + if(imgservice == nullptr) { + LOG_ERROR("failed to create image service"); + exit(-1); + } + } + virtual void TearDown() override { + delete imgservice; + system(("rm -rf " + test_dir).c_str()); + } +}; + +TEST_F(DevIDTest, parse_config_with_dev_id) { + std::string config_path, dev_id; + parse_config_and_dev_id("path/to/config.v1.json;123", config_path, dev_id); + EXPECT_EQ(config_path, "path/to/config.v1.json"); + EXPECT_EQ(dev_id, "123"); +} + +TEST_F(DevIDTest, parse_config_without_dev_id) { + std::string config_path, dev_id; + parse_config_and_dev_id("path/to/config.v1.json", config_path, dev_id); + EXPECT_EQ(config_path, "path/to/config.v1.json"); + EXPECT_EQ(dev_id, ""); +} + +TEST_F(DevIDTest, registers) { + ImageFile* imagefile0 = imgservice->create_image_file(image_config_path.c_str(), ""); + ImageFile* imagefile1 = imgservice->create_image_file(image_config_path.c_str(), "111"); + ImageFile* imagefile2 = imgservice->create_image_file(image_config_path.c_str(), "222"); + ImageFile* imagefile3 = imgservice->create_image_file(image_config_path.c_str(), "333"); + EXPECT_NE(imagefile0, nullptr); + EXPECT_NE(imagefile1, nullptr); + EXPECT_NE(imagefile2, nullptr); + EXPECT_NE(imagefile3, nullptr); + + EXPECT_EQ(imgservice->find_image_file(""), nullptr); + EXPECT_EQ(imgservice->find_image_file("111"), imagefile1); + EXPECT_EQ(imgservice->find_image_file("222"), imagefile2); + EXPECT_EQ(imgservice->find_image_file("333"), imagefile3); + + delete imagefile2; + + EXPECT_EQ(imgservice->find_image_file(""), nullptr); + EXPECT_EQ(imgservice->find_image_file("111"), imagefile1); + EXPECT_EQ(imgservice->find_image_file("222"), nullptr); + EXPECT_EQ(imgservice->find_image_file("333"), imagefile3); + + ImageFile* dup = imgservice->create_image_file(image_config_path.c_str(), "111"); + + EXPECT_EQ(dup, nullptr); + EXPECT_EQ(imgservice->find_image_file("111"), imagefile1); + + delete imagefile0; + delete imagefile1; + delete imagefile3; +} int main(int argc, char** argv) { photon::init(photon::INIT_EVENT_DEFAULT, photon::INIT_IO_DEFAULT); diff --git a/src/tools/comm_func.cpp b/src/tools/comm_func.cpp index 6e362661..3c44ccc3 100644 --- a/src/tools/comm_func.cpp +++ b/src/tools/comm_func.cpp @@ -24,6 +24,8 @@ #include "../image_service.h" #include "../image_file.h" #include "../overlaybd/tar/erofs/erofs_fs.h" +#include +#include using namespace std; @@ -51,7 +53,7 @@ int create_overlaybd(const std::string &srv_config, const std::string &dev_confi fprintf(stderr, "failed to create image service\n"); exit(-1); } - imgfile = imgservice->create_image_file(dev_config.c_str()); + imgfile = imgservice->create_image_file(dev_config.c_str(), ""); if (imgfile == nullptr) { fprintf(stderr, "failed to create image file\n"); exit(-1); @@ -112,3 +114,15 @@ IFile *create_uploader(ZFile::CompressArgs *zfile_args, IFile *src, } return upload_builder; } + +void parse_config_and_dev_id(const char *raw_config, std::string &config_path, std::string &dev_id) { + const char *semicolon = strchr(raw_config, ';'); + if(!semicolon) { + config_path = std::string(raw_config); + dev_id = std::string(""); + } + else { + config_path = std::string(raw_config, semicolon - raw_config); + dev_id = std::string(semicolon + 1); + } +} diff --git a/src/tools/comm_func.h b/src/tools/comm_func.h index 7c6618f0..d4ac2783 100644 --- a/src/tools/comm_func.h +++ b/src/tools/comm_func.h @@ -50,3 +50,5 @@ photon::fs::IFileSystem *create_ext4fs(photon::fs::IFile *imgfile, bool mkfs, bool is_erofs_fs(const photon::fs::IFile *imgfile); photon::fs::IFileSystem *create_erofs_fs(photon::fs::IFile *imgfile, uint64_t blksz); + +void parse_config_and_dev_id(const char *raw_config, std::string &config_path, std::string &dev_id);