#include "include/int_types.h"
#include "include/scope_guard.h"
+#include <boost/endian/conversion.hpp>
+
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <libnl3/netlink/genl/ctrl.h>
#include <libnl3/netlink/genl/mngt.h>
-#if __has_include(<filesystem>)
#include <filesystem>
-namespace fs = std::filesystem;
-#else
-#include <experimental/filesystem>
-namespace fs = std::experimental::filesystem;
-#endif
#include <fstream>
#include <iostream>
#include <memory>
#undef dout_prefix
#define dout_prefix *_dout << "rbd-nbd: "
+using namespace std;
+namespace fs = std::filesystem;
+
+using boost::endian::big_to_native;
+using boost::endian::native_to_big;
+
enum Command {
None,
Map,
int reattach_timeout = 30;
bool exclusive = false;
+ bool notrim = false;
bool quiesce = false;
bool readonly = false;
bool set_max_part = false;
bool try_netlink = false;
+ bool show_cookie = false;
std::string poolname;
std::string nsname;
Command command = None;
int pid = 0;
+ std::string cookie;
std::string image_spec() const {
std::string spec = poolname + "/";
if (!nsname.empty()) {
- spec += "/" + nsname;
+ spec += nsname + "/";
}
spec += imgname;
<< " (possible values: luks1, luks2)\n"
<< " --encryption-passphrase-file Path of file containing passphrase for unlocking image encryption\n"
<< " --exclusive Forbid writes by other clients\n"
+ << " --notrim Turn off trim/discard\n"
<< " --io-timeout <sec> Set nbd IO timeout\n"
<< " --max_part <limit> Override for module param max_part\n"
<< " --nbds_max <limit> Override for module param nbds_max\n"
<< " --reattach-timeout <sec> Set nbd re-attach timeout\n"
<< " (default: " << Config().reattach_timeout << ")\n"
<< " --try-netlink Use the nbd netlink interface\n"
+ << " --show-cookie Show device cookie\n"
+ << " --cookie Specify device cookie\n"
<< "\n"
<< "List options:\n"
<< " --format plain|json|xml Output format (default: plain)\n"
#define HELP_INFO 1
#define VERSION_INFO 2
-#ifdef CEPH_BIG_ENDIAN
-#define ntohll(a) (a)
-#elif defined(CEPH_LITTLE_ENDIAN)
-#define ntohll(a) swab(a)
-#else
-#error "Could not determine endianess"
-#endif
-#define htonll(a) ntohll(a)
-
static int parse_args(vector<const char*>& args, std::ostream *err_msg,
Config *cfg);
static int netlink_disconnect(int index);
const std::string &devpath,
const std::string &command);
+static std::string get_cookie(const std::string &devpath);
+
class NBDServer
{
public:
}
if (ret < 0) {
- ctx->reply.error = htonl(-ret);
+ ctx->reply.error = native_to_big<uint32_t>(-ret);
} else if ((ctx->command == NBD_CMD_READ) &&
ret < static_cast<int>(ctx->request.len)) {
int pad_byte_count = static_cast<int> (ctx->request.len) - ret;
ctx->data.append_zero(pad_byte_count);
dout(20) << __func__ << ": " << *ctx << ": Pad byte count: "
<< pad_byte_count << dendl;
- ctx->reply.error = htonl(0);
+ ctx->reply.error = native_to_big<uint32_t>(0);
} else {
- ctx->reply.error = htonl(0);
+ ctx->reply.error = native_to_big<uint32_t>(0);
}
ctx->server->io_finish(ctx);
goto signal;
}
- ctx->request.from = ntohll(ctx->request.from);
- ctx->request.type = ntohl(ctx->request.type);
- ctx->request.len = ntohl(ctx->request.len);
+ ctx->request.from = big_to_native(ctx->request.from);
+ ctx->request.type = big_to_native(ctx->request.type);
+ ctx->request.len = big_to_native(ctx->request.len);
- ctx->reply.magic = htonl(NBD_REPLY_MAGIC);
+ ctx->reply.magic = native_to_big<uint32_t>(NBD_REPLY_MAGIC);
memcpy(ctx->reply.handle, ctx->request.handle, sizeof(ctx->reply.handle));
ctx->command = ctx->request.type & 0x0000ffff;
std::ostream &operator<<(std::ostream &os, const NBDServer::IOContext &ctx) {
- os << "[" << std::hex << ntohll(*((uint64_t *)ctx.request.handle));
+ os << "[" << std::hex << big_to_native(*((uint64_t *)ctx.request.handle));
switch (ctx.command)
{
}
os << ctx.request.from << "~" << ctx.request.len << " "
- << std::dec << ntohl(ctx.reply.error) << "]";
+ << std::dec << big_to_native(ctx.reply.error) << "]";
return os;
}
continue;
}
ifs >> pid;
+ ifs.close();
// If the rbd-nbd is re-attached the pid may store garbage
// here. We are sure this is the case when it is negative or
c.devpath = cfg->devpath;
}
+ c.cookie = get_cookie(cfg->devpath);
*cfg = c;
return 0;
}
}
};
+static std::string get_cookie(const std::string &devpath)
+{
+ std::string cookie;
+ std::ifstream ifs;
+ std::string path = "/sys/block/" + devpath.substr(sizeof("/dev/") - 1) + "/backend";
+
+ ifs.open(path, std::ifstream::in);
+ if (ifs.is_open()) {
+ std::getline(ifs, cookie);
+ ifs.close();
+ }
+ return cookie;
+}
+
static int load_module(Config *cfg)
{
ostringstream param;
NLA_PUT_U64(msg, NBD_ATTR_BLOCK_SIZE_BYTES, RBD_NBD_BLKSIZE);
NLA_PUT_U64(msg, NBD_ATTR_SERVER_FLAGS, flags);
NLA_PUT_U64(msg, NBD_ATTR_DEAD_CONN_TIMEOUT, cfg->reattach_timeout);
+ if (!cfg->cookie.empty())
+ NLA_PUT_STRING(msg, NBD_ATTR_BACKEND_IDENTIFIER, cfg->cookie.c_str());
sock_attr = nla_nest_start(msg, NBD_ATTR_SOCKETS);
if (!sock_attr) {
Preforker forker;
NBDServer *server;
- vector<const char*> args;
- argv_to_vec(argc, argv, args);
+ auto args = argv_to_vec(argc, argv);
if (args.empty()) {
cerr << argv[0] << ": -h or --help for usage" << std::endl;
exit(1);
if (r < 0)
goto close_fd;
- flags = NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_TRIM | NBD_FLAG_HAS_FLAGS;
+ flags = NBD_FLAG_SEND_FLUSH | NBD_FLAG_HAS_FLAGS;
+ if (!cfg->notrim) {
+ flags |= NBD_FLAG_SEND_TRIM;
+ }
if (!cfg->snapname.empty() || cfg->readonly) {
flags |= NBD_FLAG_READ_ONLY;
read_only = 1;
use_netlink = cfg->try_netlink || reconnect;
if (use_netlink) {
+ // generate when the cookie is not supplied at CLI
+ if (!reconnect && cfg->cookie.empty()) {
+ uuid_d uuid_gen;
+ uuid_gen.generate_random();
+ cfg->cookie = uuid_gen.to_string();
+ }
r = try_netlink_setup(cfg, fd[0], size, flags, reconnect);
if (r < 0) {
goto free_server;
if (r < 0)
goto close_nbd;
- cout << cfg->devpath << std::endl;
+ std::string cookie;
+ if (use_netlink) {
+ cookie = get_cookie(cfg->devpath);
+ ceph_assert(cookie == cfg->cookie || cookie.empty());
+ }
+ if (cfg->show_cookie && !cookie.empty()) {
+ cout << cfg->devpath << " " << cookie << std::endl;
+ } else {
+ cout << cfg->devpath << std::endl;
+ }
run_server(forker, server, use_netlink);
tbl.define_column("image", TextTable::LEFT, TextTable::LEFT);
tbl.define_column("snap", TextTable::LEFT, TextTable::LEFT);
tbl.define_column("device", TextTable::LEFT, TextTable::LEFT);
+ tbl.define_column("cookie", TextTable::LEFT, TextTable::LEFT);
}
Config cfg;
f->dump_string("image", cfg.imgname);
f->dump_string("snap", cfg.snapname);
f->dump_string("device", cfg.devpath);
+ f->dump_string("cookie", cfg.cookie);
f->close_section();
} else {
should_print = true;
cfg.snapname = "-";
}
tbl << cfg.pid << cfg.poolname << cfg.nsname << cfg.imgname
- << cfg.snapname << cfg.devpath << TextTable::endrow;
+ << cfg.snapname << cfg.devpath << cfg.cookie << TextTable::endrow;
}
}
}
} else if (ceph_argparse_flag(args, i, "--exclusive", (char *)NULL)) {
cfg->exclusive = true;
+ } else if (ceph_argparse_flag(args, i, "--notrim", (char *)NULL)) {
+ cfg->notrim = true;
} else if (ceph_argparse_witharg(args, i, &cfg->io_timeout, err,
"--timeout", (char *)NULL)) {
if (!err.str().empty()) {
cfg->pretty_format = true;
} else if (ceph_argparse_flag(args, i, "--try-netlink", (char *)NULL)) {
cfg->try_netlink = true;
+ } else if (ceph_argparse_flag(args, i, "--show-cookie", (char *)NULL)) {
+ cfg->show_cookie = true;
+ } else if (ceph_argparse_witharg(args, i, &cfg->cookie, "--cookie", (char *)NULL)) {
} else if (ceph_argparse_witharg(args, i, &arg_value,
"--encryption-format", (char *)NULL)) {
if (arg_value == "luks1") {
return -EINVAL;
}
+ std::string cookie;
switch (cmd) {
case Attach:
if (cfg->devpath.empty()) {
*err_msg << "rbd-nbd: must specify device to attach";
return -EINVAL;
}
+ // Allowing attach without --cookie option for kernel without
+ // NBD_ATTR_BACKEND_IDENTIFIER support for compatibility
+ cookie = get_cookie(cfg->devpath);
+ if (!cookie.empty()) {
+ if (cfg->cookie.empty()) {
+ *err_msg << "rbd-nbd: must specify cookie to attach";
+ return -EINVAL;
+ } else if (cookie != cfg->cookie) {
+ *err_msg << "rbd-nbd: cookie mismatch";
+ return -EINVAL;
+ }
+ } else if (!cfg->cookie.empty()) {
+ *err_msg << "rbd-nbd: kernel does not have cookie support";
+ return -EINVAL;
+ }
[[fallthrough]];
case Map:
if (args.begin() == args.end()) {
{
int r;
Config cfg;
- vector<const char*> args;
- argv_to_vec(argc, argv, args);
-
+ auto args = argv_to_vec(argc, argv);
std::ostringstream err_msg;
r = parse_args(args, &err_msg, &cfg);
if (r == HELP_INFO) {