697 lines
24 KiB
C++
697 lines
24 KiB
C++
// Copyright (c) Vitaliy Filippov, 2019+
|
|
// License: VNPL-1.1 (see README.md for details)
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
#include <ctype.h>
|
|
#include "cli.h"
|
|
#include "http_client.h"
|
|
#include "cluster_client.h"
|
|
#include "str_util.h"
|
|
|
|
// Create an image, snapshot or clone
|
|
//
|
|
// Snapshot creation performs a etcd transaction which:
|
|
// - Checks that the image exists
|
|
// - Checks that the snapshot doesn't exist
|
|
// - Renames the inode to a new name with snapshot (say, testimg -> testimg@0)
|
|
// - Sets the readonly flag for the old inode
|
|
// - Creates a new inode with the same name pointing to the old inode as parent
|
|
// - Adjusts /index/image/*
|
|
//
|
|
// The same algorithm can be easily implemented in any other language or even via etcdctl,
|
|
// however we have it here for completeness
|
|
struct image_creator_t
|
|
{
|
|
cli_tool_t *parent;
|
|
|
|
pool_id_t new_pool_id = 0;
|
|
std::string new_pool_name;
|
|
std::string image_name, new_snap, new_parent;
|
|
json11::Json new_meta;
|
|
uint64_t size = 0;
|
|
bool force = false;
|
|
bool force_size = false;
|
|
std::string enc_key;
|
|
bool set_key = false;
|
|
std::string new_owner, new_owner_group, new_reader_group;
|
|
|
|
pool_id_t old_pool_id = 0;
|
|
inode_t new_parent_id = 0;
|
|
inode_t new_id = 0, old_id = 0;
|
|
uint64_t max_id_mod_rev = 0, idx_mod_rev = 0;
|
|
inode_config_t cur_cfg;
|
|
inode_config_t new_cfg;
|
|
|
|
int state = 0;
|
|
cli_result_t result;
|
|
|
|
bool is_done()
|
|
{
|
|
return state == 100;
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
auto & pools = parent->cli->st_cli.pool_config;
|
|
if (state >= 1)
|
|
goto resume_1;
|
|
if (image_name == "")
|
|
{
|
|
// FIXME: EINVAL -> specific codes for every error
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Image name is missing" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (image_name.find('@') != std::string::npos)
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Image name can't contain @ character" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (new_pool_id)
|
|
{
|
|
auto pool_it = pools.find(new_pool_id);
|
|
if (pool_it == pools.end())
|
|
{
|
|
result = (cli_result_t){ .err = ENOENT, .text = "Pool "+std::to_string(new_pool_id)+" does not exist" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
}
|
|
else if (new_pool_name != "")
|
|
{
|
|
for (auto & ic: pools)
|
|
{
|
|
if (ic.second.name == new_pool_name)
|
|
{
|
|
new_pool_id = ic.first;
|
|
break;
|
|
}
|
|
}
|
|
if (!new_pool_id)
|
|
{
|
|
result = (cli_result_t){ .err = ENOENT, .text = "Pool "+new_pool_name+" does not exist" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
}
|
|
else if (pools.size() == 1)
|
|
{
|
|
new_pool_id = pools.begin()->first;
|
|
}
|
|
if (new_pool_id && !pools.at(new_pool_id).used_for_app.empty() && !force)
|
|
{
|
|
result = (cli_result_t){
|
|
.err = EINVAL,
|
|
.text = "Pool "+pools.at(new_pool_id).name+
|
|
" is used for application "+pools.at(new_pool_id).used_for_app+
|
|
". Use --force if you really know what you are doing",
|
|
};
|
|
state = 100;
|
|
return;
|
|
}
|
|
state = 1;
|
|
resume_1:
|
|
if (new_snap == "")
|
|
create_image();
|
|
else
|
|
create_snapshot();
|
|
}
|
|
|
|
bool check_pool_permission()
|
|
{
|
|
if (!parent->user || parent->user->type == user_type_t::ADMIN)
|
|
{
|
|
return true;
|
|
}
|
|
auto pool_it = parent->cli->st_cli.pool_config.find(new_pool_id);
|
|
if (pool_it == parent->cli->st_cli.pool_config.end() ||
|
|
(pool_it->second.creator_group == "" || parent->user->groups.find(pool_it->second.creator_group) == parent->user->groups.end()))
|
|
{
|
|
result = (cli_result_t){ .err = EACCES, .text = "Pool image create permission denied" };
|
|
state = 100;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void create_image()
|
|
{
|
|
if (state == 2)
|
|
goto resume_2;
|
|
else if (state == 3)
|
|
goto resume_3;
|
|
for (auto & ic: parent->cli->st_cli.inode_config)
|
|
{
|
|
if (ic.second.name == image_name)
|
|
{
|
|
result = (cli_result_t){ .err = EEXIST, .text = "Image "+image_name+" already exists" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (ic.second.name == new_parent)
|
|
{
|
|
new_parent_id = ic.second.num;
|
|
if (!new_pool_id)
|
|
{
|
|
new_pool_id = INODE_POOL(ic.second.num);
|
|
}
|
|
if (!size)
|
|
{
|
|
size = ic.second.size;
|
|
}
|
|
}
|
|
}
|
|
if (new_parent != "" && !new_parent_id)
|
|
{
|
|
result = (cli_result_t){ .err = ENOENT, .text = "Parent image "+new_parent+" not found" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (!new_pool_id)
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Pool name or ID is missing" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (!check_pool_permission())
|
|
{
|
|
return;
|
|
}
|
|
if (!size && !force_size)
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Image size is missing" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
do
|
|
{
|
|
parent->etcd_txn(json11::Json::object {
|
|
{ "success", json11::Json::array { get_next_id() } }
|
|
});
|
|
state = 2;
|
|
resume_2:
|
|
if (parent->waiting > 0)
|
|
return;
|
|
if (parent->etcd_err.err)
|
|
{
|
|
result = parent->etcd_err;
|
|
state = 100;
|
|
return;
|
|
}
|
|
extract_next_id(parent->etcd_result["responses"][0]);
|
|
attempt_create();
|
|
state = 3;
|
|
resume_3:
|
|
if (parent->waiting > 0)
|
|
return;
|
|
if (parent->etcd_err.err)
|
|
{
|
|
result = parent->etcd_err;
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (!parent->etcd_result["succeeded"].bool_value() &&
|
|
parent->etcd_result["responses"][0]["response_range"]["kvs"].array_items().size() > 0)
|
|
{
|
|
result = (cli_result_t){ .err = EEXIST, .text = "Image "+image_name+" already exists" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
} while (!parent->etcd_result["succeeded"].bool_value());
|
|
// Save into inode_config for library users to be able to take it from there immediately
|
|
new_cfg.mod_revision = parent->etcd_result["header"]["revision"].uint64_value();
|
|
parent->cli->st_cli.insert_inode_config(new_cfg);
|
|
auto img = parent->format_image(new_cfg);
|
|
result = (cli_result_t){
|
|
.err = 0,
|
|
.text = "Image "+image_name+" created",
|
|
.data = img,
|
|
};
|
|
state = 100;
|
|
}
|
|
|
|
void create_snapshot()
|
|
{
|
|
if (state == 2)
|
|
goto resume_2;
|
|
else if (state == 3)
|
|
goto resume_3;
|
|
else if (state == 4)
|
|
goto resume_4;
|
|
// FIXME: take all info from etcd requests, not mixed with st_cli.inode_config
|
|
for (auto & ic: parent->cli->st_cli.inode_config)
|
|
{
|
|
if (ic.second.name == image_name+"@"+new_snap)
|
|
{
|
|
result = (cli_result_t){ .err = EEXIST, .text = "Snapshot "+image_name+"@"+new_snap+" already exists" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
}
|
|
if (new_parent != "")
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Parent can't be specified for snapshots" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
do
|
|
{
|
|
// In addition to next_id, get: cur_cfg, old_id, old_pool_id, size, idx_mod_rev
|
|
resume_2:
|
|
resume_3:
|
|
get_image_details();
|
|
if (parent->waiting > 0)
|
|
return;
|
|
if (!old_id)
|
|
{
|
|
result = (cli_result_t){ .err = ENOENT, .text = "Image "+image_name+" does not exist" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (!parent->check_image_perm(cur_cfg, true))
|
|
{
|
|
result = (cli_result_t){ .err = EACCES, .text = "Image permission denied" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (!new_pool_id)
|
|
{
|
|
// Create snapshot in the same pool by default
|
|
new_pool_id = old_pool_id;
|
|
}
|
|
// Verify pool permissions if the pool is different from the original
|
|
if (new_pool_id != old_pool_id && !check_pool_permission())
|
|
{
|
|
return;
|
|
}
|
|
attempt_create();
|
|
state = 4;
|
|
resume_4:
|
|
if (parent->waiting > 0)
|
|
return;
|
|
if (parent->etcd_err.err)
|
|
{
|
|
result = parent->etcd_err;
|
|
state = 100;
|
|
return;
|
|
}
|
|
if (!parent->etcd_result["succeeded"].bool_value() &&
|
|
parent->etcd_result["responses"][0]["response_range"]["kvs"].array_items().size() > 0)
|
|
{
|
|
result = (cli_result_t){ .err = EEXIST, .text = "Snapshot "+image_name+"@"+new_snap+" already exists" };
|
|
state = 100;
|
|
return;
|
|
}
|
|
} while (!parent->etcd_result["succeeded"].bool_value());
|
|
// Save into inode_config for library users to be able to take it from there immediately
|
|
new_cfg.mod_revision = parent->etcd_result["header"]["revision"].uint64_value();
|
|
parent->cli->st_cli.insert_inode_config(new_cfg);
|
|
{
|
|
auto new_pool_it = parent->cli->st_cli.pool_config.find(new_pool_id);
|
|
new_pool_name = new_pool_it != parent->cli->st_cli.pool_config.end() ? new_pool_it->second.name : "";
|
|
}
|
|
result = (cli_result_t){
|
|
.err = 0,
|
|
.text = "Snapshot "+image_name+"@"+new_snap+" created",
|
|
.data = json11::Json::object {
|
|
{ "inode_id", INODE_WITH_POOL(new_pool_id, new_id) },
|
|
{ "inode_num", new_id },
|
|
{ "name", image_name },
|
|
{ "pool_id", (uint64_t)new_pool_id },
|
|
{ "pool_name", new_pool_name },
|
|
{ "parent_name", image_name+"@"+new_snap },
|
|
{ "parent_inode_id", INODE_WITH_POOL(old_pool_id, old_id) },
|
|
{ "parent_inode_num", old_id },
|
|
{ "parent_pool_id", (uint64_t)old_pool_id },
|
|
{ "size", size },
|
|
}
|
|
};
|
|
state = 100;
|
|
}
|
|
|
|
json11::Json::object get_next_id()
|
|
{
|
|
assert(new_pool_id);
|
|
return json11::Json::object {
|
|
{ "request_range", json11::Json::object {
|
|
{ "key", base64_encode(
|
|
parent->cli->st_cli.etcd_prefix+"/index/maxid/"+std::to_string(new_pool_id)
|
|
) },
|
|
} },
|
|
};
|
|
}
|
|
|
|
void extract_next_id(json11::Json response)
|
|
{
|
|
new_id = 1;
|
|
max_id_mod_rev = 0;
|
|
if (response["response_range"]["kvs"].array_items().size() > 0)
|
|
{
|
|
auto kv = parent->cli->st_cli.parse_etcd_kv(response["response_range"]["kvs"][0]);
|
|
new_id = 1+INODE_NO_POOL(kv.value.uint64_value());
|
|
max_id_mod_rev = kv.mod_revision;
|
|
}
|
|
// Also check existing inodes - for the case when some inodes are created without changing /index/maxid
|
|
auto ino_it = parent->cli->st_cli.inode_config.lower_bound(INODE_WITH_POOL(new_pool_id+1, 0));
|
|
if (ino_it != parent->cli->st_cli.inode_config.begin())
|
|
{
|
|
ino_it--;
|
|
if (INODE_POOL(ino_it->first) == new_pool_id && new_id < 1+INODE_NO_POOL(ino_it->first))
|
|
new_id = 1+INODE_NO_POOL(ino_it->first);
|
|
}
|
|
}
|
|
|
|
void get_image_details()
|
|
{
|
|
if (state == 2)
|
|
goto resume_2;
|
|
else if (state == 3)
|
|
goto resume_3;
|
|
parent->etcd_txn(json11::Json::object { { "success", json11::Json::array {
|
|
get_next_id(),
|
|
json11::Json::object {
|
|
{ "request_range", json11::Json::object {
|
|
{ "key", base64_encode(
|
|
parent->cli->st_cli.etcd_prefix+"/index/image/"+image_name
|
|
) },
|
|
} },
|
|
},
|
|
} } });
|
|
state = 2;
|
|
resume_2:
|
|
if (parent->waiting > 0)
|
|
return;
|
|
if (parent->etcd_err.err)
|
|
{
|
|
result = parent->etcd_err;
|
|
state = 100;
|
|
return;
|
|
}
|
|
extract_next_id(parent->etcd_result["responses"][0]);
|
|
old_id = 0;
|
|
old_pool_id = 0;
|
|
idx_mod_rev = 0;
|
|
if (parent->etcd_result["responses"][1]["response_range"]["kvs"].array_items().size() == 0)
|
|
{
|
|
for (auto & ic: parent->cli->st_cli.inode_config)
|
|
{
|
|
if (ic.second.name == image_name)
|
|
{
|
|
old_id = INODE_NO_POOL(ic.first);
|
|
old_pool_id = INODE_POOL(ic.first);
|
|
cur_cfg = ic.second;
|
|
size = ic.second.size;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// FIXME: Parse kvs in etcd_state_client automatically
|
|
{
|
|
auto kv = parent->cli->st_cli.parse_etcd_kv(parent->etcd_result["responses"][1]["response_range"]["kvs"][0]);
|
|
old_id = INODE_NO_POOL(kv.value["id"].uint64_value());
|
|
old_pool_id = (pool_id_t)kv.value["pool_id"].uint64_value();
|
|
idx_mod_rev = kv.mod_revision;
|
|
if (!old_id || !old_pool_id || old_pool_id >= POOL_ID_MAX)
|
|
{
|
|
result = (cli_result_t){ .err = ENOENT, .text = "Invalid pool or inode ID in etcd key "+kv.key };
|
|
state = 100;
|
|
return;
|
|
}
|
|
}
|
|
parent->etcd_txn(json11::Json::object {
|
|
{ "success", json11::Json::array {
|
|
json11::Json::object {
|
|
{ "request_range", json11::Json::object {
|
|
{ "key", base64_encode(
|
|
parent->cli->st_cli.etcd_prefix+"/config/inode/"+
|
|
std::to_string(old_pool_id)+"/"+std::to_string(old_id)
|
|
) },
|
|
} },
|
|
},
|
|
} },
|
|
});
|
|
state = 3;
|
|
resume_3:
|
|
if (parent->waiting > 0)
|
|
return;
|
|
if (parent->etcd_err.err)
|
|
{
|
|
result = parent->etcd_err;
|
|
state = 100;
|
|
return;
|
|
}
|
|
{
|
|
auto kv = parent->cli->st_cli.parse_etcd_kv(parent->etcd_result["responses"][0]["response_range"]["kvs"][0]);
|
|
cur_cfg = parent->cli->st_cli.deserialize_inode_cfg(INODE_WITH_POOL(old_pool_id, old_id), kv.value, kv.mod_revision);
|
|
size = cur_cfg.size;
|
|
}
|
|
}
|
|
if (!new_pool_id)
|
|
{
|
|
new_pool_id = old_pool_id;
|
|
}
|
|
}
|
|
|
|
void attempt_create()
|
|
{
|
|
new_cfg = (inode_config_t){
|
|
.num = INODE_WITH_POOL(new_pool_id, new_id),
|
|
.name = image_name,
|
|
.size = size,
|
|
.parent_id = (new_snap != "" ? INODE_WITH_POOL(old_pool_id, old_id) : new_parent_id),
|
|
.readonly = false,
|
|
.meta = new_meta,
|
|
};
|
|
if (set_key)
|
|
{
|
|
new_cfg.enc_key = enc_key;
|
|
}
|
|
else if (new_snap != "")
|
|
{
|
|
new_cfg.enc_key = cur_cfg.enc_key;
|
|
}
|
|
new_cfg.owner = http_context_get_ssl_cn(parent->cli->st_cli.get_http_ctx());
|
|
if (!new_owner.empty())
|
|
{
|
|
new_cfg.owner = new_owner;
|
|
}
|
|
if (!new_owner_group.empty())
|
|
{
|
|
new_cfg.owner_group = new_owner_group;
|
|
}
|
|
if (!new_reader_group.empty())
|
|
{
|
|
new_cfg.reader_group = new_reader_group;
|
|
}
|
|
json11::Json::array checks = json11::Json::array {
|
|
json11::Json::object {
|
|
{ "target", "VERSION" },
|
|
{ "version", 0 },
|
|
{ "key", base64_encode(
|
|
parent->cli->st_cli.etcd_prefix+"/config/inode/"+
|
|
std::to_string(new_pool_id)+"/"+std::to_string(new_id)
|
|
) },
|
|
},
|
|
json11::Json::object {
|
|
{ "target", "VERSION" },
|
|
{ "version", 0 },
|
|
{ "key", base64_encode(
|
|
parent->cli->st_cli.etcd_prefix+"/index/image/"+image_name+
|
|
(new_snap != "" ? "@"+new_snap : "")
|
|
) },
|
|
},
|
|
json11::Json::object {
|
|
{ "target", "MOD" },
|
|
{ "mod_revision", max_id_mod_rev },
|
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/index/maxid/"+std::to_string(new_pool_id)) },
|
|
},
|
|
};
|
|
json11::Json::array success = json11::Json::array {
|
|
json11::Json::object {
|
|
{ "request_put", json11::Json::object {
|
|
{ "key", base64_encode(
|
|
parent->cli->st_cli.etcd_prefix+"/config/inode/"+
|
|
std::to_string(new_pool_id)+"/"+std::to_string(new_id)
|
|
) },
|
|
{ "value", base64_encode(
|
|
json11::Json(parent->cli->st_cli.serialize_inode_cfg(&new_cfg)).dump()
|
|
) },
|
|
} },
|
|
},
|
|
json11::Json::object {
|
|
{ "request_put", json11::Json::object {
|
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/index/image/"+image_name) },
|
|
{ "value", base64_encode(json11::Json(json11::Json::object{
|
|
{ "id", new_id },
|
|
{ "pool_id", (uint64_t)new_pool_id },
|
|
}).dump()) },
|
|
} },
|
|
},
|
|
json11::Json::object {
|
|
{ "request_put", json11::Json::object {
|
|
{ "key", base64_encode(
|
|
parent->cli->st_cli.etcd_prefix+"/index/maxid/"+
|
|
std::to_string(new_pool_id)
|
|
) },
|
|
{ "value", base64_encode(std::to_string(new_id)) }
|
|
} },
|
|
},
|
|
};
|
|
json11::Json::array failure = json11::Json::array {
|
|
json11::Json::object {
|
|
{ "request_range", json11::Json::object {
|
|
{ "key", base64_encode(
|
|
parent->cli->st_cli.etcd_prefix+"/index/image/"+
|
|
image_name+(new_snap != "" ? "@"+new_snap : "")
|
|
) },
|
|
} },
|
|
},
|
|
};
|
|
if (new_snap != "")
|
|
{
|
|
inode_config_t snap_cfg = cur_cfg;
|
|
snap_cfg.name = image_name+"@"+new_snap;
|
|
snap_cfg.readonly = true;
|
|
checks.push_back(json11::Json::object {
|
|
{ "target", "MOD" },
|
|
{ "mod_revision", cur_cfg.mod_revision },
|
|
{ "key", base64_encode(
|
|
parent->cli->st_cli.etcd_prefix+"/config/inode/"+
|
|
std::to_string(old_pool_id)+"/"+std::to_string(old_id)
|
|
) },
|
|
});
|
|
checks.push_back(json11::Json::object {
|
|
{ "target", "MOD" },
|
|
{ "mod_revision", idx_mod_rev },
|
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/index/image/"+image_name) }
|
|
});
|
|
success.push_back(json11::Json::object {
|
|
{ "request_put", json11::Json::object {
|
|
{ "key", base64_encode(
|
|
parent->cli->st_cli.etcd_prefix+"/config/inode/"+
|
|
std::to_string(old_pool_id)+"/"+std::to_string(old_id)
|
|
) },
|
|
{ "value", base64_encode(
|
|
json11::Json(parent->cli->st_cli.serialize_inode_cfg(&snap_cfg)).dump()
|
|
) },
|
|
} },
|
|
});
|
|
success.push_back(json11::Json::object {
|
|
{ "request_put", json11::Json::object {
|
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/index/image/"+image_name+"@"+new_snap) },
|
|
{ "value", base64_encode(json11::Json(json11::Json::object{
|
|
{ "id", old_id },
|
|
{ "pool_id", (uint64_t)old_pool_id },
|
|
}).dump()) },
|
|
} },
|
|
});
|
|
};
|
|
parent->etcd_txn(json11::Json::object {
|
|
{ "compare", checks },
|
|
{ "success", success },
|
|
{ "failure", failure },
|
|
});
|
|
}
|
|
};
|
|
|
|
std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
|
|
{
|
|
auto image_creator = new image_creator_t();
|
|
image_creator->parent = this;
|
|
image_creator->image_name = cfg["image"].string_value();
|
|
if (!cfg["pool"].is_null())
|
|
{
|
|
image_creator->new_pool_id = cfg["pool"].uint64_value();
|
|
image_creator->new_pool_name = cfg["pool"].string_value();
|
|
}
|
|
else
|
|
{
|
|
image_creator->new_pool_id = cfg["pool_id"].uint64_value();
|
|
image_creator->new_pool_name = cfg["pool_name"].string_value();
|
|
}
|
|
image_creator->force = cfg["force"].bool_value();
|
|
image_creator->force_size = cfg["force_size"].bool_value();
|
|
if (cfg["image_meta"].is_object())
|
|
{
|
|
image_creator->new_meta = cfg["image_meta"];
|
|
}
|
|
if (cfg["snapshot"].string_value() != "")
|
|
{
|
|
image_creator->new_snap = cfg["snapshot"].string_value();
|
|
}
|
|
if (!cfg["enc_key"].is_null())
|
|
{
|
|
image_creator->set_key = true;
|
|
if (image_creator->enc_key == "random")
|
|
{
|
|
uint8_t newkey[64];
|
|
RAND_bytes(newkey, 64);
|
|
image_creator->enc_key = tohexstr(newkey, 64);
|
|
}
|
|
else
|
|
{
|
|
image_creator->enc_key = cfg["enc_key"].string_value();
|
|
if (image_creator->enc_key != "" &&
|
|
image_creator->enc_key.substr(0, strlen(VAULT_KEY_PREFIX)) != VAULT_KEY_PREFIX &&
|
|
(!ishexstr(image_creator->enc_key) || image_creator->enc_key.size() != 128))
|
|
{
|
|
return [](cli_result_t & result)
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Encryption key is not a 512-bit hex string, not \"\" and not \"random\"" };
|
|
return true;
|
|
};
|
|
}
|
|
}
|
|
}
|
|
image_creator->new_owner = cfg["owner"].string_value();
|
|
image_creator->new_owner_group = cfg["owner_group"].string_value();
|
|
image_creator->new_reader_group = cfg["reader_group"].string_value();
|
|
image_creator->new_parent = cfg["parent"].string_value();
|
|
if (!cfg["size"].is_null())
|
|
{
|
|
bool ok;
|
|
image_creator->size = parse_size(cfg["size"].as_string(), &ok);
|
|
if (!ok)
|
|
{
|
|
return [size = cfg["size"].string_value()](cli_result_t & result)
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Invalid syntax for size: "+size };
|
|
return true;
|
|
};
|
|
}
|
|
if ((image_creator->size % 4096) && !cfg["force_size"].bool_value())
|
|
{
|
|
delete image_creator;
|
|
return [](cli_result_t & result)
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Size should be a multiple of 4096" };
|
|
return true;
|
|
};
|
|
}
|
|
if (image_creator->new_snap != "")
|
|
{
|
|
delete image_creator;
|
|
return [](cli_result_t & result)
|
|
{
|
|
result = (cli_result_t){ .err = EINVAL, .text = "Size can't be specified for snapshots" };
|
|
return true;
|
|
};
|
|
}
|
|
}
|
|
return [image_creator](cli_result_t & result)
|
|
{
|
|
image_creator->loop();
|
|
if (image_creator->is_done())
|
|
{
|
|
result = image_creator->result;
|
|
delete image_creator;
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
}
|