mirror of
https://git.sr.ht/~nabijaczleweli/tzpfms
synced 2025-04-15 09:40:32 +03:00
Add zfs-tpm-list
This commit is contained in:
parent
b5cd91073e
commit
4da1dbf845
114
src/bin/zfs-tpm-list.cpp
Normal file
114
src/bin/zfs-tpm-list.cpp
Normal 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;
|
||||
});
|
||||
}
|
75
src/main.hpp
75
src/main.hpp
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
|
48
src/zfs.hpp
48
src/zfs.hpp
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user