Add zfs-tpm-list

This commit is contained in:
наб 2020-10-31 03:30:16 +01:00
parent b5cd91073e
commit 4da1dbf845
No known key found for this signature in database
GPG Key ID: BCFD0B018D2658F1
4 changed files with 208 additions and 31 deletions

114
src/bin/zfs-tpm-list.cpp Normal file
View File

@ -0,0 +1,114 @@
/* SPDX-License-Identifier: MIT */
#include "../main.hpp"
#include "../parse.hpp"
#include "../zfs.hpp"
#include <algorithm>
#include <sys/mman.h>
#define TZPFMS_BACKEND_MAX_LEN 16
/// zfs(8) uses struct zprop_get_cbdata_t, which is powerful, but inscrutable; we have a fixed format, which makes this easier
struct output_line {
char name[ZFS_MAX_DATASET_NAME_LEN + 1];
char backend[TZPFMS_BACKEND_MAX_LEN + 1];
bool key_available : 1;
bool coherent : 1;
};
int main(int argc, char ** argv) {
bool human = true;
bool print_nontzpfms = false;
size_t maxdepth = MAXDEPTH_UNSET;
return do_bare_main(
argc, argv, "Hrd:a", "[-H] [-r|-d max] [-a]",
[&](auto arg) {
switch(arg) {
case 'H':
human = false;
break;
case 'r':
maxdepth = SIZE_MAX;
break;
case 'd':
if(parse_int(optarg, maxdepth)) {
fprintf(stderr, "%s is not an integer\n", optarg);
return __LINE__;
}
break;
case 'a':
print_nontzpfms = true;
break;
}
return 0;
},
[&](auto libz) {
output_line * lines{};
size_t lines_len{};
quickscope_wrapper lines_deleter{[&] { free(lines); }};
TRY_MAIN(for_all_datasets(libz, argv + optind, maxdepth, [&](auto dataset) {
boolean_t dataset_is_root;
TRY("get encryption root", zfs_crypto_get_encryption_root(dataset, &dataset_is_root, nullptr));
if(!dataset_is_root)
return 0;
char *backend{}, *handle{};
TRY_MAIN(lookup_userprop(dataset, PROPNAME_BACKEND, backend));
TRY_MAIN(lookup_userprop(dataset, PROPNAME_KEY, handle));
++lines_len;
lines = TRY_PTR("allocate line buffer", reinterpret_cast<output_line *>(realloc(lines, sizeof(output_line) * lines_len)));
auto & cur_line = lines[lines_len - 1];
strncpy(cur_line.name, zfs_get_name(dataset), ZFS_MAX_DATASET_NAME_LEN);
strncpy(cur_line.backend, (backend && strlen(backend) <= TZPFMS_BACKEND_MAX_LEN) ? backend : "\0", TZPFMS_BACKEND_MAX_LEN);
// Tristate available/unavailable/none, but it's gonna be either available or unavailable on envryption roots, so
cur_line.key_available = zfs_prop_get_int(dataset, ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_AVAILABLE;
cur_line.coherent = !!backend == !!handle;
return 0;
}));
auto max_name_len = 0u;
auto max_backend_len = 0u;
auto max_key_available_len = 0u;
auto max_coherent_len = 0u;
auto separator = "\t";
if(human) {
max_name_len = strlen("NAME");
max_backend_len = strlen("BACK-END");
max_key_available_len = strlen("KEYSTATUS");
max_coherent_len = strlen("COHERENT");
separator = " ";
for(auto cur = lines; cur != lines + lines_len; ++cur)
if(print_nontzpfms || cur->backend[0] != '\0') {
max_name_len = std::max(max_name_len, strlen(cur->name));
max_backend_len = std::max(max_backend_len, (cur->backend[0] != '\0') ? strlen(cur->backend) : strlen("-"));
max_key_available_len = std::max(max_key_available_len, cur->key_available ? strlen("available") : strlen("unavailable"));
}
}
auto println = [&](auto name, auto backend, auto key_available, auto coherent) {
printf("%-*s%s%-*s%s%-*s%s%-*s\n", //
max_name_len, name, separator, //
max_backend_len, backend, separator, //
max_key_available_len, key_available, separator, //
max_coherent_len, coherent);
};
if(human)
println("NAME", "BACK-END", "KEYSTATUS", "COHERENT");
for(auto cur = lines; cur != lines + lines_len; ++cur)
if(print_nontzpfms || cur->backend[0] != '\0')
println(cur->name, (cur->backend[0] != '\0') ? cur->backend : "-", cur->key_available ? "available" : "unavailable", cur->coherent ? "yes" : "no");
return 0;
});
}

View File

@ -7,6 +7,7 @@
#include "common.hpp"
#include <libzfs.h>
#include <stdlib.h>
#include <type_traits>
#include <unistd.h>
@ -19,7 +20,7 @@
template <class G, class M>
int do_main(int argc, char ** argv, const char * getoptions, const char * usage, G && getoptfn, M && main) {
int do_bare_main(int argc, char ** argv, const char * getoptions, const char * usage, G && getoptfn, M && main) {
const auto libz = TRY_PTR("initialise libzfs", libzfs_init());
quickscope_wrapper libz_deleter{[=] { libzfs_fini(libz); }};
@ -40,35 +41,49 @@ int do_main(int argc, char ** argv, const char * getoptions, const char * usage,
printf("tzpfms version %s\n", TZPFMS_VERSION);
return 0;
default:
getoptfn(opt);
if constexpr(std::is_same_v<std::invoke_result_t<G, decltype(opt)>, void>)
getoptfn(opt);
else {
if(auto err = getoptfn(opt)) {
fprintf(stderr, "Usage: %s [-hV] %s%s<dataset>\n", argv[0], usage, strlen(usage) ? " " : "");
return err;
}
}
}
if(optind >= argc) {
fprintf(stderr,
"No dataset to act on?\n"
"Usage: %s [-hV] %s%s<dataset>\n",
argv[0], usage, strlen(usage) ? " " : "");
return __LINE__;
}
auto dataset = TRY_PTR(nullptr, zfs_open(libz, argv[optind], ZFS_TYPE_FILESYSTEM));
quickscope_wrapper dataset_deleter{[&] { zfs_close(dataset); }};
{
char encryption_root[MAXNAMELEN];
boolean_t dataset_is_root;
TRY("get encryption root", zfs_crypto_get_encryption_root(dataset, &dataset_is_root, encryption_root));
if(!dataset_is_root && !strlen(encryption_root)) {
fprintf(stderr, "Dataset %s not encrypted?\n", zfs_get_name(dataset));
return __LINE__;
} else if(!dataset_is_root) {
printf("Using dataset %s's encryption root %s instead.\n", zfs_get_name(dataset), encryption_root);
// TODO: disallow maybe? or require force option?
zfs_close(dataset);
dataset = TRY_PTR(nullptr, zfs_open(libz, encryption_root, ZFS_TYPE_FILESYSTEM));
}
}
return main(dataset);
return main(libz);
}
template <class G, class M>
int do_main(int argc, char ** argv, const char * getoptions, const char * usage, G && getoptfn, M && main) {
return do_bare_main(argc, argv, getoptions, usage, getoptfn, [&](auto libz) {
if(optind >= argc) {
fprintf(stderr,
"No dataset to act on?\n"
"Usage: %s [-hV] %s%s<dataset>\n",
argv[0], usage, strlen(usage) ? " " : "");
return __LINE__;
}
auto dataset = TRY_PTR(nullptr, zfs_open(libz, argv[optind], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME));
quickscope_wrapper dataset_deleter{[&] { zfs_close(dataset); }};
{
char encryption_root[MAXNAMELEN];
boolean_t dataset_is_root;
TRY("get encryption root", zfs_crypto_get_encryption_root(dataset, &dataset_is_root, encryption_root));
if(!dataset_is_root && !strlen(encryption_root)) {
fprintf(stderr, "Dataset %s not encrypted?\n", zfs_get_name(dataset));
return __LINE__;
} else if(!dataset_is_root) {
printf("Using dataset %s's encryption root %s instead.\n", zfs_get_name(dataset), encryption_root);
// TODO: disallow maybe? or require force option?
zfs_close(dataset);
dataset = TRY_PTR(nullptr, zfs_open(libz, encryption_root, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME));
}
}
return main(dataset);
});
}

View File

@ -11,7 +11,7 @@
template <class T>
int parse_int(const char * what, T & out) {
int base = 0;
int base = 10;
if(!strncmp(what, "0x", 2) || !strncmp(what, "0X", 2)) {
base = 16;
what += 2;

View File

@ -16,6 +16,8 @@
#define PROPNAME_BACKEND "xyz.nabijaczleweli:tzpfms.backend"
#define PROPNAME_KEY "xyz.nabijaczleweli:tzpfms.key"
#define MAXDEPTH_UNSET (SIZE_MAX - 1)
/// Mimic libzfs error output
#define REQUIRE_KEY_LOADED(dataset) \
@ -76,3 +78,49 @@ int verify_backend(zfs_handle_t * on, const char * this_backend, F && func) {
return 0;
}
template <class F>
struct for_all_datasets_iterator_data {
F & func;
zfs_iter_f iterator;
size_t depth;
};
/// Iterate over datasets like zfs(8) list
template <class F>
int for_all_datasets(libzfs_handle_t * libz, char ** datasets, size_t maxdepth, F && func) {
auto iterator = [](zfs_handle_t * dataset, void * dat_p) {
auto dat = reinterpret_cast<for_all_datasets_iterator_data<F> *>(dat_p);
TRY_MAIN(dat->func(dataset));
if(dat->depth) {
for_all_datasets_iterator_data<F> ndat{dat->func, dat->iterator, dat->depth - 1};
return zfs_iter_filesystems(dataset, dat->iterator, &ndat);
} else
return 0;
};
if(!*datasets) {
for_all_datasets_iterator_data<F> dat{func, iterator, (maxdepth == MAXDEPTH_UNSET) ? SIZE_MAX : maxdepth};
switch(auto err = zfs_iter_root(libz, dat.iterator, &dat)) {
case -1: // zfs_iter_filesystems() only bubbles errors from callback, but zfs_iter_root() might produce new ones and return -1
TRY("iterate root datasets", err);
__builtin_unreachable();
case 0:
return 0;
default:
return err;
}
} else {
for_all_datasets_iterator_data<F> dat{func, iterator, (maxdepth == MAXDEPTH_UNSET) ? 0 : maxdepth};
for(; *datasets; ++datasets) {
auto dataset = zfs_open(libz, *datasets, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
if(!dataset)
continue; // error printed by libzfs; mirror zfs(8) list behaviour here and continue despite any errors
TRY_MAIN(dat.iterator(dataset, &dat));
}
return 0;
}
}