]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/hook_veritydev.c
Merge branch 'lsns--Q' of https://github.com/masatake/util-linux
[thirdparty/util-linux.git] / libmount / src / hook_veritydev.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /*
3 * This file is part of libmount from util-linux project.
4 *
5 * Copyright (C) 2019 Microsoft Corporation
6 * Copyright (C) 2022 Karel Zak <kzak@redhat.com>
7 *
8 * libmount is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 *
14 * Please, see comment in libmount/src/hooks.c to understand how hooks work.
15 */
16 #include "mountP.h"
17
18 #ifdef HAVE_CRYPTSETUP
19
20 #include <libcryptsetup.h>
21 #include "path.h"
22 #include "strutils.h"
23 #include "fileutils.h"
24 #include "pathnames.h"
25
26 #ifdef CRYPTSETUP_VIA_DLOPEN
27 # include <dlfcn.h>
28
29 /* Pointers to libcryptsetup functions (initiliazed by dlsym()) */
30 struct verity_opers {
31 void (*crypt_set_debug_level)(int);
32 void (*crypt_set_log_callback)(struct crypt_device *, void (*log)(int, const char *, void *), void *);
33 int (*crypt_init_data_device)(struct crypt_device **, const char *, const char *);
34 int (*crypt_load)(struct crypt_device *, const char *, void *);
35 int (*crypt_get_volume_key_size)(struct crypt_device *);
36 # ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
37 int (*crypt_activate_by_signed_key)(struct crypt_device *, const char *, const char *, size_t, const char *, size_t, uint32_t);
38 # endif
39 int (*crypt_activate_by_volume_key)(struct crypt_device *, const char *, const char *, size_t, uint32_t);
40 void (*crypt_free)(struct crypt_device *);
41 int (*crypt_init_by_name)(struct crypt_device **, const char *);
42 int (*crypt_get_verity_info)(struct crypt_device *, struct crypt_params_verity *);
43 int (*crypt_volume_key_get)(struct crypt_device *, int, char *, size_t *, const char *, size_t);
44
45 int (*crypt_deactivate_by_name)(struct crypt_device *, const char *, uint32_t);
46 };
47
48 /* libcryptsetup functions names and offsets in 'struct verity_opers' */
49 struct verity_sym {
50 const char *name;
51 size_t offset; /* offset of the symbol in verity_opers */
52 };
53
54 # define DEF_VERITY_SYM(_name) \
55 { \
56 .name = # _name, \
57 .offset = offsetof(struct verity_opers, _name), \
58 }
59
60 /* All required symbols */
61 static const struct verity_sym verity_symbols[] =
62 {
63 DEF_VERITY_SYM( crypt_set_debug_level ),
64 DEF_VERITY_SYM( crypt_set_log_callback ),
65 DEF_VERITY_SYM( crypt_init_data_device ),
66 DEF_VERITY_SYM( crypt_load ),
67 DEF_VERITY_SYM( crypt_get_volume_key_size ),
68 # ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
69 DEF_VERITY_SYM( crypt_activate_by_signed_key ),
70 # endif
71 DEF_VERITY_SYM( crypt_activate_by_volume_key ),
72 DEF_VERITY_SYM( crypt_free ),
73 DEF_VERITY_SYM( crypt_init_by_name ),
74 DEF_VERITY_SYM( crypt_get_verity_info ),
75 DEF_VERITY_SYM( crypt_volume_key_get ),
76
77 DEF_VERITY_SYM( crypt_deactivate_by_name ),
78 };
79 #endif /* CRYPTSETUP_VIA_DLOPEN */
80
81
82 /* Data used by all verity hooks */
83 struct hookset_data {
84 char *devname; /* the device */
85 #ifdef CRYPTSETUP_VIA_DLOPEN
86 void *dl; /* dlopen() */
87 struct verity_opers dl_funcs; /* dlsym() */
88 #endif
89 };
90
91 /* libcryptsetup call -- dlopen version requires 'struct hookset_data *hsd' */
92 #ifdef CRYPTSETUP_VIA_DLOPEN
93 # define verity_call(_func) (hsd->dl_funcs._func)
94 #else
95 # define verity_call(_func) (_func)
96 #endif
97
98
99 static void delete_veritydev(struct libmnt_context *cxt,
100 const struct libmnt_hookset *hs,
101 struct hookset_data *hsd);
102
103
104 #ifdef CRYPTSETUP_VIA_DLOPEN
105 static int load_libcryptsetup_symbols(struct libmnt_context *cxt,
106 const struct libmnt_hookset *hs,
107 struct hookset_data *hsd)
108 {
109 size_t i;
110 int flags = RTLD_LAZY | RTLD_LOCAL;
111
112 assert(cxt);
113 assert(hsd);
114 assert(hsd->dl == NULL);
115
116 /* glibc extension: mnt_context_deferred_delete_veritydev is called immediately after, don't unload on dl_close */
117 #ifdef RTLD_NODELETE
118 flags |= RTLD_NODELETE;
119 #endif
120 /* glibc extension: might help to avoid further symbols clashes */
121 #ifdef RTLD_DEEPBIND
122 flags |= RTLD_DEEPBIND;
123 #endif
124 hsd->dl = dlopen("libcryptsetup.so.12", flags);
125 if (!hsd->dl) {
126 DBG(HOOK, ul_debugobj(hs, "cannot dlopen libcryptsetup"));
127 return -ENOTSUP;
128 }
129
130 /* clear errors first, then load all the libcryptsetup symbols */
131 dlerror();
132
133 /* dlsym() */
134 for (i = 0; i < ARRAY_SIZE(verity_symbols); i++) {
135 char *errmsg;
136 const struct verity_sym *def = &verity_symbols[i];
137 void **sym;
138
139 sym = (void **) ((char *) (&hsd->dl_funcs) + def->offset);
140 *sym = dlsym(hsd->dl, def->name);
141
142 errmsg = dlerror();
143 if (errmsg) {
144 DBG(HOOK, ul_debugobj(hs, "dlsym failed %s: %s", def->name, errmsg));
145 return -ENOTSUP;
146 }
147 }
148 return 0;
149 }
150 #endif
151
152 /* libcryptsetup callback */
153 static void libcryptsetup_log(int level __attribute__((__unused__)),
154 const char *msg, void *data)
155 {
156 const struct libmnt_hookset *hs = (struct libmnt_hookset *) data;
157 DBG(HOOK, ul_debugobj(hs, "cryptsetup: %s", msg));
158 }
159
160 /* free global data */
161 static void free_hookset_data( struct libmnt_context *cxt,
162 const struct libmnt_hookset *hs)
163 {
164 struct hookset_data *hsd = mnt_context_get_hookset_data(cxt, hs);
165
166 if (!hsd)
167 return;
168 if (hsd->devname)
169 delete_veritydev(cxt, hs, hsd);
170 #ifdef CRYPTSETUP_VIA_DLOPEN
171 if (hsd->dl)
172 dlclose(hsd->dl);
173 #endif
174 free(hsd);
175 mnt_context_set_hookset_data(cxt, hs, NULL);
176 }
177
178 /* global data, used by all callbacks */
179 static struct hookset_data *new_hookset_data(
180 struct libmnt_context *cxt,
181 const struct libmnt_hookset *hs)
182 {
183 struct hookset_data *hsd = calloc(1, sizeof(struct hookset_data));
184
185 if (hsd && mnt_context_set_hookset_data(cxt, hs, hsd) != 0)
186 goto failed;
187
188 #ifdef CRYPTSETUP_VIA_DLOPEN
189 if (load_libcryptsetup_symbols(cxt, hs, hsd) != 0)
190 goto failed;
191 #endif
192 if (mnt_context_is_verbose(cxt))
193 verity_call( crypt_set_debug_level(CRYPT_DEBUG_ALL) );
194
195 verity_call( crypt_set_log_callback(NULL, libcryptsetup_log, (void *) hs) );
196
197 return hsd;
198 failed:
199 free(hsd);
200 return NULL;
201 }
202
203 /* libmount callback -- cleanup all */
204 static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs)
205 {
206 DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name));
207
208 /* remove all our hooks */
209 while (mnt_context_remove_hook(cxt, hs, 0, NULL) == 0);
210
211 /* free and remove global hookset data */
212 free_hookset_data(cxt, hs);
213
214 return 0;
215 }
216
217 /* check mount options for verity stuff */
218 static int is_veritydev_required(struct libmnt_context *cxt,
219 const struct libmnt_hookset *hs,
220 struct libmnt_optlist *ol)
221 {
222 const char *src;
223 unsigned long flags = 0;
224
225 assert(cxt);
226 assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
227
228 if (cxt->action != MNT_ACT_MOUNT)
229 return 0;
230 if (!cxt->fs)
231 return 0;
232 src = mnt_fs_get_srcpath(cxt->fs);
233 if (!src)
234 return 0; /* backing file not set */
235
236 ol = mnt_context_get_optlist(cxt);
237 if (!ol)
238 return 0;
239 if (mnt_optlist_is_bind(ol)
240 || mnt_optlist_is_move(ol)
241 || mnt_context_propagation_only(cxt))
242 return 0;
243
244 if (mnt_context_get_user_mflags(cxt, &flags))
245 return 0;
246
247 if (flags & (MNT_MS_HASH_DEVICE | MNT_MS_ROOT_HASH | MNT_MS_HASH_OFFSET)) {
248 DBG(HOOK, ul_debugobj(hs, "verity options detected"));
249 return 1;
250 }
251
252 return 0;
253 }
254
255 static void delete_veritydev(struct libmnt_context *cxt,
256 const struct libmnt_hookset *hs,
257 struct hookset_data *hsd)
258 {
259 uint32_t flags = 0;
260 int rc;
261
262 if (!hsd || !hsd->devname)
263 return;
264
265 if (mnt_context_get_status(cxt) != 0)
266 /*
267 * mount(2) success, use deferred deactivation
268 */
269 flags |= CRYPT_DEACTIVATE_DEFERRED;
270
271 rc = verity_call( crypt_deactivate_by_name(NULL, hsd->devname, flags) );
272
273 DBG(HOOK, ul_debugobj(hs, "deleted %s [rc=%d%s]",
274 hsd->devname, rc,
275 flags & CRYPT_DEACTIVATE_DEFERRED ? " deferred" : "" ));
276 if (rc == 0) {
277 free(hsd->devname);
278 hsd->devname = NULL;
279 }
280
281 return;
282 }
283
284 /* Taken from https://gitlab.com/cryptsetup/cryptsetup/blob/master/lib/utils_crypt.c#L225 */
285 static size_t crypt_hex_to_bytes(const char *hex, char **result)
286 {
287 char buf[3] = "xx\0", *endp, *bytes;
288 size_t i, len;
289
290 len = strlen(hex);
291 if (len % 2)
292 return -EINVAL;
293 len /= 2;
294
295 bytes = malloc(len);
296 if (!bytes)
297 return -ENOMEM;
298
299 for (i = 0; i < len; i++) {
300 memcpy(buf, &hex[i * 2], 2);
301 errno = 0;
302 bytes[i] = strtoul(buf, &endp, 16);
303 if (errno || endp != &buf[2]) {
304 free(bytes);
305 return -EINVAL;
306 }
307 }
308 *result = bytes;
309 return i;
310 }
311
312
313 static int setup_veritydev( struct libmnt_context *cxt,
314 const struct libmnt_hookset *hs,
315 struct hookset_data *hsd,
316 struct libmnt_optlist *ol)
317 {
318 struct libmnt_opt *opt;
319 const char *backing_file,
320 *hash_device = NULL,
321 *root_hash_file = NULL,
322 *fec_device = NULL,
323 *root_hash_sig_file = NULL;
324 char *key = NULL,
325 *root_hash_binary = NULL,
326 *mapper_device = NULL,
327 *root_hash = NULL,
328 *hash_sig = NULL;
329
330 size_t hash_size, hash_sig_size = 0, keysize = 0;
331 struct crypt_params_verity crypt_params = {};
332 struct crypt_device *crypt_dev = NULL;
333
334 int rc = 0;
335 /* Use the same default for FEC parity bytes as cryptsetup uses */
336 uint64_t offset = 0, fec_offset = 0, fec_roots = 2;
337 uint32_t crypt_activate_flags = CRYPT_ACTIVATE_READONLY;
338
339 struct stat hash_sig_st;
340
341 assert(cxt);
342 assert(cxt->fs);
343 assert(hsd);
344 assert(hsd->devname == NULL);
345
346 /* dm-verity volumes are read-only, and mount will fail if not set */
347 mnt_optlist_append_flags(ol, MS_RDONLY, cxt->map_linux);
348
349 backing_file = mnt_fs_get_srcpath(cxt->fs);
350 if (!backing_file)
351 return -EINVAL;
352
353 DBG(HOOK, ul_debugobj(hs, "verity: setup for %s", backing_file));
354
355 /* verity.hashdevice= */
356 if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_HASH_DEVICE, cxt->map_userspace)))
357 hash_device = mnt_opt_get_value(opt);
358
359 /* verity.roothash= */
360 if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_ROOT_HASH, cxt->map_userspace))) {
361 root_hash = strdup(mnt_opt_get_value(opt));
362 rc = root_hash ? 0 : -ENOMEM;
363 }
364
365 /* verity.hashoffset= */
366 if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_HASH_OFFSET, cxt->map_userspace))
367 && mnt_opt_has_value(opt)) {
368 if (strtosize(mnt_opt_get_value(opt), &offset)) {
369 DBG(HOOK, ul_debugobj(hs, "failed to parse verity.hashoffset="));
370 rc = -MNT_ERR_MOUNTOPT;
371 }
372 }
373
374 /* verity.roothashfile= */
375 if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_ROOT_HASH_FILE, cxt->map_userspace)))
376 root_hash_file = mnt_opt_get_value(opt);
377
378 /* verity.fecdevice= */
379 if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_FEC_DEVICE, cxt->map_userspace)))
380 fec_device = mnt_opt_get_value(opt);
381
382 /* verity.fecoffset= */
383 if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_FEC_OFFSET, cxt->map_userspace))
384 && mnt_opt_has_value(opt)
385 && strtosize(mnt_opt_get_value(opt), &fec_offset)) {
386 DBG(HOOK, ul_debugobj(hs, "failed to parse verity.fecoffset="));
387 rc = -MNT_ERR_MOUNTOPT;
388 }
389
390 /* verity.fecroots= */
391 if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_FEC_ROOTS, cxt->map_userspace))
392 && mnt_opt_has_value(opt)
393 && strtosize(mnt_opt_get_value(opt), &fec_roots)) {
394 DBG(HOOK, ul_debugobj(hs, "failed to parse verity.fecroots="));
395 rc = -MNT_ERR_MOUNTOPT;
396 }
397
398 /* verity.roothashsig= */
399 if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_ROOT_HASH_SIG, cxt->map_userspace))
400 && mnt_opt_has_value(opt)) {
401 root_hash_sig_file = mnt_opt_get_value(opt);
402
403 DBG(HOOK, ul_debugobj(hs, "verity: checking %s", root_hash_sig_file));
404
405 rc = ul_path_stat(NULL, &hash_sig_st, 0, root_hash_sig_file);
406 if (rc == 0)
407 rc = S_ISREG(hash_sig_st.st_mode) && hash_sig_st.st_size ? 0 : -EINVAL;
408 if (rc == 0) {
409 hash_sig_size = hash_sig_st.st_size;
410 hash_sig = malloc(hash_sig_size);
411 rc = hash_sig ? 0 : -ENOMEM;
412 }
413 if (rc == 0) {
414 rc = ul_path_read(NULL, hash_sig, hash_sig_size, root_hash_sig_file);
415 rc = rc < (int)hash_sig_size ? -1 : 0;
416 }
417 }
418
419 /* verity.oncorruption= */
420 if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_VERITY_ON_CORRUPTION, cxt->map_userspace))
421 && mnt_opt_has_value(opt)) {
422 const char *val = mnt_opt_get_value(opt);
423 if (!strcmp(val, "ignore"))
424 crypt_activate_flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION;
425 else if (!strcmp(val, "restart"))
426 crypt_activate_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION;
427 else if (!strcmp(val, "panic"))
428 /* Added by libcryptsetup v2.3.4 - ignore on lower versions, as with other optional features */
429 #ifdef CRYPT_ACTIVATE_PANIC_ON_CORRUPTION
430 crypt_activate_flags |= CRYPT_ACTIVATE_PANIC_ON_CORRUPTION;
431 #else
432 DBG(HOOK, ul_debugobj(hs, "verity.oncorruption=panic not supported by libcryptsetup, ignoring"));
433 #endif
434 else {
435 DBG(HOOK, ul_debugobj(hs, "failed to parse verity.oncorruption="));
436 rc = -MNT_ERR_MOUNTOPT;
437 }
438 }
439
440 if (!rc && root_hash && root_hash_file) {
441 DBG(HOOK, ul_debugobj(hs, "verity.roothash and verity.roothashfile are mutually exclusive"));
442 rc = -EINVAL;
443 } else if (!rc && root_hash_file) {
444 rc = ul_path_read_string(NULL, &root_hash, root_hash_file);
445 rc = rc < 1 ? rc : 0;
446 }
447
448 if (!rc && (!hash_device || !root_hash)) {
449 DBG(HOOK, ul_debugobj(hs, "verity.hashdevice and one of verity.roothash or verity.roothashfile are mandatory"));
450 rc = -EINVAL;
451 }
452
453 /* To avoid clashes, use the roothash as the device name. This allows us to reuse already open devices, saving
454 * a lot of time and resources when there are duplicated mounts. If the roothash is the same, then the volumes
455 * are also guaranteed to be identical. This is what systemd also does, so we can deduplicate across the whole
456 * system. */
457 if (asprintf(&mapper_device, "%s-verity", root_hash) < 0)
458 rc = -ENOMEM;
459
460 if (!rc)
461 rc = verity_call( crypt_init_data_device(&crypt_dev, hash_device, backing_file) );
462 if (rc)
463 goto done;
464
465 memset(&crypt_params, 0, sizeof(struct crypt_params_verity));
466 crypt_params.hash_area_offset = offset;
467 crypt_params.fec_area_offset = fec_offset;
468 crypt_params.fec_roots = fec_roots;
469 crypt_params.fec_device = fec_device;
470 crypt_params.flags = 0;
471
472 rc = verity_call( crypt_load(crypt_dev, CRYPT_VERITY, &crypt_params) );
473 if (rc < 0)
474 goto done;
475
476 hash_size = verity_call( crypt_get_volume_key_size(crypt_dev) );
477 if (crypt_hex_to_bytes(root_hash, &root_hash_binary) != hash_size) {
478 DBG(HOOK, ul_debugobj(hs, "root hash %s is not of length %zu", root_hash, hash_size));
479 rc = -EINVAL;
480 goto done;
481 }
482
483 if (hash_sig) {
484 #ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
485 rc = verity_call( crypt_activate_by_signed_key(crypt_dev, mapper_device, root_hash_binary, hash_size,
486 hash_sig, hash_sig_size, crypt_activate_flags) );
487 #else
488 rc = -EINVAL;
489 DBG(HOOK, ul_debugobj(hs, "verity.roothashsig=%s passed but libcryptsetup does not provide crypt_activate_by_signed_key()", hash_sig));
490 #endif
491 } else
492 rc = verity_call( crypt_activate_by_volume_key(crypt_dev, mapper_device, root_hash_binary, hash_size,
493 crypt_activate_flags) );
494
495 /*
496 * If the mapper device already exists, and if libcryptsetup supports it, get the root
497 * hash associated with the existing one and compare it with the parameter passed by
498 * the user. If they match, then we can be sure the user intended to mount the exact
499 * same device, and simply reuse it and return success. Although we use the roothash
500 * as the device mapper name, and root privileges are required to open them, better be
501 * safe than sorry, so double check that the actual root hash used matches.
502 * The kernel does the refcounting for us.
503 * If libcryptsetup does not support getting the root hash out of an existing device,
504 * then return an error and tell the user that the device is already in use.
505 * Pass through only OOM errors or mismatching root hash errors.
506 */
507 if (rc == -EEXIST) {
508 DBG(HOOK, ul_debugobj(hs, "%s already in use as /dev/mapper/%s", backing_file, mapper_device));
509
510 verity_call( crypt_free(crypt_dev) );
511
512 rc = verity_call( crypt_init_by_name(&crypt_dev, mapper_device) );
513 if (!rc) {
514 rc = verity_call( crypt_get_verity_info(crypt_dev, &crypt_params) );
515 if (!rc) {
516 key = calloc(hash_size, 1);
517 if (!key) {
518 rc = -ENOMEM;
519 goto done;
520 }
521 }
522 if (!rc) {
523 keysize = hash_size;
524 rc = verity_call( crypt_volume_key_get(crypt_dev, CRYPT_ANY_SLOT, key, &keysize, NULL, 0) );
525 }
526 if (!rc) {
527 DBG(HOOK, ul_debugobj(hs, "comparing root hash of existing device with %s", root_hash));
528 if (memcmp(key, root_hash_binary, hash_size)) {
529 DBG(HOOK, ul_debugobj(hs, "existing device's hash does not match with %s", root_hash));
530 rc = -EINVAL;
531 goto done;
532 }
533 } else {
534 DBG(HOOK, ul_debugobj(hs, "libcryptsetup does not support extracting root hash of existing device"));
535 }
536 }
537 if (rc) {
538 rc = -EEXIST;
539 } else {
540 /*
541 * Ensure that, if signatures are supported, we only reuse the device if the previous mount
542 * used the same settings, so that a previous unsigned mount will not be reused if the user
543 * asks to use signing for the new one, and viceversa.
544 */
545 #ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
546 if (!!hash_sig != !!(crypt_params.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE)) {
547 rc = -EINVAL;
548 DBG(HOOK, ul_debugobj(hs, "existing device and new mount have to either be both opened with signature or both without"));
549 goto done;
550 }
551 #endif
552 DBG(HOOK, ul_debugobj(hs, "root hash of %s matches %s, reusing device", mapper_device, root_hash));
553 }
554 }
555
556 if (!rc) {
557 if (asprintf(&hsd->devname, _PATH_DEV_MAPPER "/%s", mapper_device) == -1)
558 rc = -ENOMEM;
559 else
560 rc = mnt_fs_set_source(cxt->fs, hsd->devname);
561 }
562
563 done:
564 verity_call( crypt_free(crypt_dev) );
565
566 free(root_hash_binary);
567 free(mapper_device);
568 free(root_hash);
569 free(hash_sig);
570 free(key);
571 return rc;
572 }
573 /* call after mount(2) */
574 static int hook_mount_post(
575 struct libmnt_context *cxt,
576 const struct libmnt_hookset *hs,
577 void *data __attribute__((__unused__)))
578 {
579
580 delete_veritydev(cxt, hs, mnt_context_get_hookset_data(cxt, hs));
581 return 0;
582 }
583
584 /*
585 * first call (first callback in this hookset)
586 */
587 static int hook_prepare_source(
588 struct libmnt_context *cxt,
589 const struct libmnt_hookset *hs,
590 void *data __attribute__((__unused__)))
591 {
592 struct libmnt_optlist *ol;
593 struct hookset_data *hsd;
594 int rc;
595
596 assert(cxt);
597
598 ol = mnt_context_get_optlist(cxt);
599 if (!ol)
600 return -ENOMEM;
601
602 if (!is_veritydev_required(cxt, hs, ol))
603 return 0;
604
605 hsd = new_hookset_data(cxt, hs);
606 if (!hsd)
607 return -ENOMEM;
608
609 rc = setup_veritydev(cxt, hs, hsd, ol);
610 if (!rc) {
611 rc = mnt_context_append_hook(cxt, hs,
612 MNT_STAGE_MOUNT_POST,
613 NULL, hook_mount_post);
614 if (rc)
615 delete_veritydev(cxt, hs, hsd);
616 }
617 return rc;
618 }
619
620
621 const struct libmnt_hookset hookset_veritydev =
622 {
623 .name = "__veritydev",
624
625 .firststage = MNT_STAGE_PREP_SOURCE,
626 .firstcall = hook_prepare_source,
627
628 .deinit = hookset_deinit
629 };
630 #endif /*HAVE_CRYPTSETUP*/
631
632
633