summaryrefslogtreecommitdiffstats
path: root/libmount/src/hook_veritydev.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmount/src/hook_veritydev.c')
-rw-r--r--libmount/src/hook_veritydev.c646
1 files changed, 646 insertions, 0 deletions
diff --git a/libmount/src/hook_veritydev.c b/libmount/src/hook_veritydev.c
new file mode 100644
index 0000000..f91778a
--- /dev/null
+++ b/libmount/src/hook_veritydev.c
@@ -0,0 +1,646 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * This file is part of libmount from util-linux project.
+ *
+ * Copyright (C) 2019 Microsoft Corporation
+ * Copyright (C) 2022 Karel Zak <kzak@redhat.com>
+ *
+ * libmount is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ * Please, see comment in libmount/src/hooks.c to understand how hooks work.
+ */
+#include "mountP.h"
+
+#ifdef HAVE_CRYPTSETUP
+
+#include <libcryptsetup.h>
+#include "path.h"
+#include "strutils.h"
+#include "fileutils.h"
+#include "pathnames.h"
+
+#ifdef CRYPTSETUP_VIA_DLOPEN
+# include <dlfcn.h>
+
+/* Pointers to libcryptsetup functions (initiliazed by dlsym()) */
+struct verity_opers {
+ void (*crypt_set_debug_level)(int);
+ void (*crypt_set_log_callback)(struct crypt_device *, void (*log)(int, const char *, void *), void *);
+ int (*crypt_init_data_device)(struct crypt_device **, const char *, const char *);
+ int (*crypt_load)(struct crypt_device *, const char *, void *);
+ int (*crypt_get_volume_key_size)(struct crypt_device *);
+# ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
+ int (*crypt_activate_by_signed_key)(struct crypt_device *, const char *, const char *, size_t, const char *, size_t, uint32_t);
+# endif
+ int (*crypt_activate_by_volume_key)(struct crypt_device *, const char *, const char *, size_t, uint32_t);
+ void (*crypt_free)(struct crypt_device *);
+ int (*crypt_init_by_name)(struct crypt_device **, const char *);
+ int (*crypt_get_verity_info)(struct crypt_device *, struct crypt_params_verity *);
+ int (*crypt_volume_key_get)(struct crypt_device *, int, char *, size_t *, const char *, size_t);
+
+ int (*crypt_deactivate_by_name)(struct crypt_device *, const char *, uint32_t);
+};
+
+/* libcryptsetup functions names and offsets in 'struct verity_opers' */
+struct verity_sym {
+ const char *name;
+ size_t offset; /* offset of the symbol in verity_opers */
+};
+
+# define DEF_VERITY_SYM(_name) \
+ { \
+ .name = # _name, \
+ .offset = offsetof(struct verity_opers, _name), \
+ }
+
+/* All required symbols */
+static const struct verity_sym verity_symbols[] =
+{
+ DEF_VERITY_SYM( crypt_set_debug_level ),
+ DEF_VERITY_SYM( crypt_set_log_callback ),
+ DEF_VERITY_SYM( crypt_init_data_device ),
+ DEF_VERITY_SYM( crypt_load ),
+ DEF_VERITY_SYM( crypt_get_volume_key_size ),
+# ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
+ DEF_VERITY_SYM( crypt_activate_by_signed_key ),
+# endif
+ DEF_VERITY_SYM( crypt_activate_by_volume_key ),
+ DEF_VERITY_SYM( crypt_free ),
+ DEF_VERITY_SYM( crypt_init_by_name ),
+ DEF_VERITY_SYM( crypt_get_verity_info ),
+ DEF_VERITY_SYM( crypt_volume_key_get ),
+
+ DEF_VERITY_SYM( crypt_deactivate_by_name ),
+};
+#endif /* CRYPTSETUP_VIA_DLOPEN */
+
+
+/* Data used by all verity hooks */
+struct hookset_data {
+ char *devname; /* the device */
+#ifdef CRYPTSETUP_VIA_DLOPEN
+ void *dl; /* dlopen() */
+ struct verity_opers dl_funcs; /* dlsym() */
+#endif
+};
+
+/* libcryptsetup call -- dlopen version requires 'struct hookset_data *hsd' */
+#ifdef CRYPTSETUP_VIA_DLOPEN
+# define verity_call(_func) (hsd->dl_funcs._func)
+#else
+# define verity_call(_func) (_func)
+#endif
+
+
+static void delete_veritydev(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ struct hookset_data *hsd);
+
+
+#ifdef CRYPTSETUP_VIA_DLOPEN
+static int load_libcryptsetup_symbols(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ struct hookset_data *hsd)
+{
+ size_t i;
+ int flags = RTLD_LAZY | RTLD_LOCAL;
+
+ assert(cxt);
+ assert(hsd);
+ assert(hsd->dl == NULL);
+
+ /* glibc extension: mnt_context_deferred_delete_veritydev is called immediately after, don't unload on dl_close */
+#ifdef RTLD_NODELETE
+ flags |= RTLD_NODELETE;
+#endif
+ /* glibc extension: might help to avoid further symbols clashes */
+#ifdef RTLD_DEEPBIND
+ flags |= RTLD_DEEPBIND;
+#endif
+ hsd->dl = dlopen("libcryptsetup.so.12", flags);
+ if (!hsd->dl) {
+ DBG(HOOK, ul_debugobj(hs, "cannot dlopen libcryptsetup"));
+ return -ENOTSUP;
+ }
+
+ /* clear errors first, then load all the libcryptsetup symbols */
+ dlerror();
+
+ /* dlsym() */
+ for (i = 0; i < ARRAY_SIZE(verity_symbols); i++) {
+ char *errmsg;
+ const struct verity_sym *def = &verity_symbols[i];
+ void **sym;
+
+ sym = (void **) ((char *) (&hsd->dl_funcs) + def->offset);
+ *sym = dlsym(hsd->dl, def->name);
+
+ errmsg = dlerror();
+ if (errmsg) {
+ DBG(HOOK, ul_debugobj(hs, "dlsym failed %s: %s", def->name, errmsg));
+ return -ENOTSUP;
+ }
+ }
+ return 0;
+}
+#endif
+
+/* libcryptsetup callback */
+static void libcryptsetup_log(int level __attribute__((__unused__)),
+ const char *msg, void *data)
+{
+ const struct libmnt_hookset *hs = (struct libmnt_hookset *) data;
+ DBG(HOOK, ul_debugobj(hs, "cryptsetup: %s", msg));
+}
+
+/* free global data */
+static void free_hookset_data( struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs)
+{
+ struct hookset_data *hsd = mnt_context_get_hookset_data(cxt, hs);
+
+ if (!hsd)
+ return;
+ if (hsd->devname)
+ delete_veritydev(cxt, hs, hsd);
+#ifdef CRYPTSETUP_VIA_DLOPEN
+ if (hsd->dl)
+ dlclose(hsd->dl);
+#endif
+ free(hsd);
+ mnt_context_set_hookset_data(cxt, hs, NULL);
+}
+
+/* global data, used by all callbacks */
+static struct hookset_data *new_hookset_data(
+ struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs)
+{
+ struct hookset_data *hsd = calloc(1, sizeof(struct hookset_data));
+
+ if (hsd && mnt_context_set_hookset_data(cxt, hs, hsd) != 0)
+ goto failed;
+
+#ifdef CRYPTSETUP_VIA_DLOPEN
+ if (load_libcryptsetup_symbols(cxt, hs, hsd) != 0)
+ goto failed;
+#endif
+ if (mnt_context_is_verbose(cxt))
+ verity_call( crypt_set_debug_level(CRYPT_DEBUG_ALL) );
+
+ verity_call( crypt_set_log_callback(NULL, libcryptsetup_log, (void *) hs) );
+
+ return hsd;
+failed:
+ free(hsd);
+ return NULL;
+}
+
+/* libmount callback -- cleanup all */
+static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
+{
+ DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name));
+
+ /* remove all our hooks */
+ while (mnt_context_remove_hook(cxt, hs, 0, NULL) == 0);
+
+ /* free and remove global hookset data */
+ free_hookset_data(cxt, hs);
+
+ return 0;
+}
+
+/* check mount options for verity stuff */
+static int is_veritydev_required(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ struct libmnt_optlist *ol)
+{
+ const char *src;
+ unsigned long flags = 0;
+
+ assert(cxt);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (cxt->action != MNT_ACT_MOUNT)
+ return 0;
+ if (!cxt->fs)
+ return 0;
+ src = mnt_fs_get_srcpath(cxt->fs);
+ if (!src)
+ return 0; /* backing file not set */
+
+ ol = mnt_context_get_optlist(cxt);
+ if (!ol)
+ return 0;
+ if (mnt_optlist_is_bind(ol)
+ || mnt_optlist_is_move(ol)
+ || mnt_context_propagation_only(cxt))
+ return 0;
+
+ if (mnt_context_get_user_mflags(cxt, &flags))
+ return 0;
+
+ if (flags & (MNT_MS_HASH_DEVICE | MNT_MS_ROOT_HASH | MNT_MS_HASH_OFFSET)) {
+ DBG(HOOK, ul_debugobj(hs, "verity options detected"));
+ return 1;
+ }
+
+ return 0;
+}
+
+static void delete_veritydev(struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ struct hookset_data *hsd)
+{
+ uint32_t flags = 0;
+ int rc;
+
+ if (!hsd || !hsd->devname)
+ return;
+
+ if (mnt_context_get_status(cxt) != 0)
+ /*
+ * mount(2) success, use deferred deactivation
+ */
+ flags |= CRYPT_DEACTIVATE_DEFERRED;
+
+ rc = verity_call( crypt_deactivate_by_name(NULL, hsd->devname, flags) );
+
+ DBG(HOOK, ul_debugobj(hs, "deleted %s [rc=%d%s]",
+ hsd->devname, rc,
+ flags & CRYPT_DEACTIVATE_DEFERRED ? " deferred" : "" ));
+ if (rc == 0) {
+ free(hsd->devname);
+ hsd->devname = NULL;
+ }
+
+ return;
+}
+
+/* Taken from https://gitlab.com/cryptsetup/cryptsetup/blob/master/lib/utils_crypt.c#L225 */
+static size_t crypt_hex_to_bytes(const char *hex, char **result)
+{
+ char buf[3] = "xx\0", *endp, *bytes;
+ size_t i, len;
+
+ len = strlen(hex);
+ if (len % 2)
+ return -EINVAL;
+ len /= 2;
+
+ bytes = malloc(len);
+ if (!bytes)
+ return -ENOMEM;
+
+ for (i = 0; i < len; i++) {
+ memcpy(buf, &hex[i * 2], 2);
+ errno = 0;
+ bytes[i] = strtoul(buf, &endp, 16);
+ if (errno || endp != &buf[2]) {
+ free(bytes);
+ return -EINVAL;
+ }
+ }
+ *result = bytes;
+ return i;
+}
+
+
+static int setup_veritydev( struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ struct hookset_data *hsd,
+ struct libmnt_optlist *ol)
+{
+ struct libmnt_opt *opt;
+ const char *backing_file,
+ *hash_device = NULL,
+ *root_hash_file = NULL,
+ *fec_device = NULL,
+ *root_hash_sig_file = NULL;
+ char *key = NULL,
+ *root_hash_binary = NULL,
+ *mapper_device = NULL,
+ *root_hash = NULL,
+ *hash_sig = NULL;
+
+ size_t hash_size, hash_sig_size = 0, keysize = 0;
+ struct crypt_params_verity crypt_params = {};
+ struct crypt_device *crypt_dev = NULL;
+
+ int rc = 0;
+ /* Use the same default for FEC parity bytes as cryptsetup uses */
+ uint64_t offset = 0, fec_offset = 0, fec_roots = 2;
+ uint32_t crypt_activate_flags = CRYPT_ACTIVATE_READONLY;
+
+ struct stat hash_sig_st;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(hsd);
+ assert(hsd->devname == NULL);
+
+ /* dm-verity volumes are read-only, and mount will fail if not set */
+ mnt_optlist_append_flags(ol, MS_RDONLY, cxt->map_linux);
+
+ backing_file = mnt_fs_get_srcpath(cxt->fs);
+ if (!backing_file)
+ return -EINVAL;
+ else {
+ /* To avoid clashes, prefix libmnt_ to all mapper devices */
+ char *p, *path = strdup(backing_file);
+ if (!path)
+ return -ENOMEM;
+
+ p = stripoff_last_component(path);
+ if (p)
+ mapper_device = calloc(strlen(p) + sizeof("libmnt_"), sizeof(char));
+ if (mapper_device) {
+ strcat(mapper_device, "libmnt_");
+ strcat(mapper_device, p);
+ }
+ free(path);
+ if (!mapper_device)
+ return -ENOMEM;
+ }
+
+ DBG(HOOK, ul_debugobj(hs, "verity: setup for %s [%s]", backing_file, mapper_device));
+
+ /* verity.hashdevice= */
+ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_HASH_DEVICE, cxt->map_userspace)))
+ hash_device = mnt_opt_get_value(opt);
+
+ /* verity.roothash= */
+ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_ROOT_HASH, cxt->map_userspace))) {
+ root_hash = strdup(mnt_opt_get_value(opt));
+ rc = root_hash ? 0 : -ENOMEM;
+ }
+
+ /* verity.hashoffset= */
+ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_HASH_OFFSET, cxt->map_userspace))
+ && mnt_opt_has_value(opt)) {
+ if (strtosize(mnt_opt_get_value(opt), &offset)) {
+ DBG(HOOK, ul_debugobj(hs, "failed to parse verity.hashoffset="));
+ rc = -MNT_ERR_MOUNTOPT;
+ }
+ }
+
+ /* verity.roothashfile= */
+ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_ROOT_HASH_FILE, cxt->map_userspace)))
+ root_hash_file = mnt_opt_get_value(opt);
+
+ /* verity.fecdevice= */
+ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_FEC_DEVICE, cxt->map_userspace)))
+ fec_device = mnt_opt_get_value(opt);
+
+ /* verity.fecoffset= */
+ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_FEC_OFFSET, cxt->map_userspace))
+ && mnt_opt_has_value(opt)
+ && strtosize(mnt_opt_get_value(opt), &fec_offset)) {
+ DBG(HOOK, ul_debugobj(hs, "failed to parse verity.fecoffset="));
+ rc = -MNT_ERR_MOUNTOPT;
+ }
+
+ /* verity.fecroots= */
+ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_FEC_ROOTS, cxt->map_userspace))
+ && mnt_opt_has_value(opt)
+ && strtosize(mnt_opt_get_value(opt), &fec_roots)) {
+ DBG(HOOK, ul_debugobj(hs, "failed to parse verity.fecroots="));
+ rc = -MNT_ERR_MOUNTOPT;
+ }
+
+ /* verity.roothashsig= */
+ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_ROOT_HASH_SIG, cxt->map_userspace))
+ && mnt_opt_has_value(opt)) {
+ root_hash_sig_file = mnt_opt_get_value(opt);
+
+ DBG(HOOK, ul_debugobj(hs, "verity: checking %s", root_hash_sig_file));
+
+ rc = ul_path_stat(NULL, &hash_sig_st, 0, root_hash_sig_file);
+ if (rc == 0)
+ rc = S_ISREG(hash_sig_st.st_mode) && hash_sig_st.st_size ? 0 : -EINVAL;
+ if (rc == 0) {
+ hash_sig_size = hash_sig_st.st_size;
+ hash_sig = malloc(hash_sig_size);
+ rc = hash_sig ? 0 : -ENOMEM;
+ }
+ if (rc == 0) {
+ rc = ul_path_read(NULL, hash_sig, hash_sig_size, root_hash_sig_file);
+ rc = rc < (int)hash_sig_size ? -1 : 0;
+ }
+ }
+
+ /* verity.oncorruption= */
+ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_VERITY_ON_CORRUPTION, cxt->map_userspace))
+ && mnt_opt_has_value(opt)) {
+ const char *val = mnt_opt_get_value(opt);
+ if (!strcmp(val, "ignore"))
+ crypt_activate_flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION;
+ else if (!strcmp(val, "restart"))
+ crypt_activate_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION;
+ else if (!strcmp(val, "panic"))
+ /* Added by libcryptsetup v2.3.4 - ignore on lower versions, as with other optional features */
+#ifdef CRYPT_ACTIVATE_PANIC_ON_CORRUPTION
+ crypt_activate_flags |= CRYPT_ACTIVATE_PANIC_ON_CORRUPTION;
+#else
+ DBG(HOOK, ul_debugobj(hs, "verity.oncorruption=panic not supported by libcryptsetup, ignoring"));
+#endif
+ else {
+ DBG(HOOK, ul_debugobj(hs, "failed to parse verity.oncorruption="));
+ rc = -MNT_ERR_MOUNTOPT;
+ }
+ }
+
+ if (!rc && root_hash && root_hash_file) {
+ DBG(HOOK, ul_debugobj(hs, "verity.roothash and verity.roothashfile are mutually exclusive"));
+ rc = -EINVAL;
+ } else if (!rc && root_hash_file) {
+ rc = ul_path_read_string(NULL, &root_hash, root_hash_file);
+ rc = rc < 1 ? rc : 0;
+ }
+
+ if (!rc && (!hash_device || !root_hash)) {
+ DBG(HOOK, ul_debugobj(hs, "verity.hashdevice and one of verity.roothash or verity.roothashfile are mandatory"));
+ rc = -EINVAL;
+ }
+
+ if (!rc)
+ rc = verity_call( crypt_init_data_device(&crypt_dev, hash_device, backing_file) );
+ if (rc)
+ goto done;
+
+ memset(&crypt_params, 0, sizeof(struct crypt_params_verity));
+ crypt_params.hash_area_offset = offset;
+ crypt_params.fec_area_offset = fec_offset;
+ crypt_params.fec_roots = fec_roots;
+ crypt_params.fec_device = fec_device;
+ crypt_params.flags = 0;
+
+ rc = verity_call( crypt_load(crypt_dev, CRYPT_VERITY, &crypt_params) );
+ if (rc < 0)
+ goto done;
+
+ hash_size = verity_call( crypt_get_volume_key_size(crypt_dev) );
+ if (crypt_hex_to_bytes(root_hash, &root_hash_binary) != hash_size) {
+ DBG(HOOK, ul_debugobj(hs, "root hash %s is not of length %zu", root_hash, hash_size));
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (hash_sig) {
+#ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
+ rc = verity_call( crypt_activate_by_signed_key(crypt_dev, mapper_device, root_hash_binary, hash_size,
+ hash_sig, hash_sig_size, crypt_activate_flags) );
+#else
+ rc = -EINVAL;
+ DBG(HOOK, ul_debugobj(hs, "verity.roothashsig=%s passed but libcryptsetup does not provide crypt_activate_by_signed_key()", hash_sig));
+#endif
+ } else
+ rc = verity_call( crypt_activate_by_volume_key(crypt_dev, mapper_device, root_hash_binary, hash_size,
+ crypt_activate_flags) );
+
+ /*
+ * If the mapper device already exists, and if libcryptsetup supports it, get the root
+ * hash associated with the existing one and compare it with the parameter passed by
+ * the user. If they match, then we can be sure the user intended to mount the exact
+ * same device, and simply reuse it and return success.
+ * The kernel does the refcounting for us.
+ * If libcryptsetup does not support getting the root hash out of an existing device,
+ * then return an error and tell the user that the device is already in use.
+ * Pass through only OOM errors or mismatching root hash errors.
+ */
+ if (rc == -EEXIST) {
+ DBG(HOOK, ul_debugobj(hs, "%s already in use as /dev/mapper/%s", backing_file, mapper_device));
+
+ verity_call( crypt_free(crypt_dev) );
+
+ rc = verity_call( crypt_init_by_name(&crypt_dev, mapper_device) );
+ if (!rc) {
+ rc = verity_call( crypt_get_verity_info(crypt_dev, &crypt_params) );
+ if (!rc) {
+ key = calloc(hash_size, 1);
+ if (!key) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+ if (!rc) {
+ keysize = hash_size;
+ rc = verity_call( crypt_volume_key_get(crypt_dev, CRYPT_ANY_SLOT, key, &keysize, NULL, 0) );
+ }
+ if (!rc) {
+ DBG(HOOK, ul_debugobj(hs, "comparing root hash of existing device with %s", root_hash));
+ if (memcmp(key, root_hash_binary, hash_size)) {
+ DBG(HOOK, ul_debugobj(hs, "existing device's hash does not match with %s", root_hash));
+ rc = -EINVAL;
+ goto done;
+ }
+ } else {
+ DBG(HOOK, ul_debugobj(hs, "libcryptsetup does not support extracting root hash of existing device"));
+ }
+ }
+ if (rc) {
+ rc = -EEXIST;
+ } else {
+ /*
+ * Ensure that, if signatures are supported, we only reuse the device if the previous mount
+ * used the same settings, so that a previous unsigned mount will not be reused if the user
+ * asks to use signing for the new one, and viceversa.
+ */
+#ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
+ if (!!hash_sig != !!(crypt_params.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE)) {
+ rc = -EINVAL;
+ DBG(HOOK, ul_debugobj(hs, "existing device and new mount have to either be both opened with signature or both without"));
+ goto done;
+ }
+#endif
+ DBG(HOOK, ul_debugobj(hs, "root hash of %s matches %s, reusing device", mapper_device, root_hash));
+ }
+ }
+
+ if (!rc) {
+ hsd->devname = calloc(strlen(mapper_device)
+ + sizeof(_PATH_DEV_MAPPER) + 2, sizeof(char));
+ if (!hsd->devname)
+ rc = -ENOMEM;
+ else {
+ strcat(hsd->devname, _PATH_DEV_MAPPER "/");
+ strcat(hsd->devname, mapper_device);
+ rc = mnt_fs_set_source(cxt->fs, hsd->devname);
+ }
+ }
+
+done:
+ verity_call( crypt_free(crypt_dev) );
+
+ free(root_hash_binary);
+ free(mapper_device);
+ free(root_hash);
+ free(hash_sig);
+ free(key);
+ return rc;
+}
+/* call after mount(2) */
+static int hook_mount_post(
+ struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+
+ delete_veritydev(cxt, hs, mnt_context_get_hookset_data(cxt, hs));
+ return 0;
+}
+
+/*
+ * first call (first callback in this hookset)
+ */
+static int hook_prepare_source(
+ struct libmnt_context *cxt,
+ const struct libmnt_hookset *hs,
+ void *data __attribute__((__unused__)))
+{
+ struct libmnt_optlist *ol;
+ struct hookset_data *hsd;
+ int rc;
+
+ assert(cxt);
+
+ ol = mnt_context_get_optlist(cxt);
+ if (!ol)
+ return -ENOMEM;
+
+ if (!is_veritydev_required(cxt, hs, ol))
+ return 0;
+
+ hsd = new_hookset_data(cxt, hs);
+ if (!hsd)
+ return -ENOMEM;
+
+ rc = setup_veritydev(cxt, hs, hsd, ol);
+ if (!rc) {
+ rc = mnt_context_append_hook(cxt, hs,
+ MNT_STAGE_MOUNT_POST,
+ NULL, hook_mount_post);
+ if (rc)
+ delete_veritydev(cxt, hs, hsd);
+ }
+ return rc;
+}
+
+
+const struct libmnt_hookset hookset_veritydev =
+{
+ .name = "__veritydev",
+
+ .firststage = MNT_STAGE_PREP_SOURCE,
+ .firstcall = hook_prepare_source,
+
+ .deinit = hookset_deinit
+};
+#endif /*HAVE_CRYPTSETUP*/
+
+
+