1 /*
2 * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <stdarg.h>
36
37 #include "tcplay.h"
38 #include "tcplay_api.h"
39 #include "tcplay_api_internal.h"
40
41
42 int
tc_api_init(int verbose)43 tc_api_init(int verbose)
44 {
45 int error;
46
47 tc_internal_verbose = verbose;
48
49 if ((error = tc_play_init()) != 0)
50 return TC_ERR;
51 else
52 return TC_OK;
53 }
54
55 int
tc_api_uninit(void)56 tc_api_uninit(void)
57 {
58 check_and_purge_safe_mem();
59 return TC_OK;
60 }
61
62
63 static const char *_caps[] = {
64 "trim",
65 NULL
66 };
67
68 int
tc_api_has(const char * feature)69 tc_api_has(const char *feature)
70 {
71 const char *cap;
72 int i;
73
74 for (cap = _caps[0], i = 0; cap != NULL; cap = _caps[++i]) {
75 if ((strcmp(cap, feature)) == 0)
76 return TC_OK;
77 }
78
79 return TC_ERR_UNIMPL;
80 }
81
82 int
tc_api_cipher_iterate(tc_api_cipher_iterator_fn fn,void * priv)83 tc_api_cipher_iterate(tc_api_cipher_iterator_fn fn, void *priv)
84 {
85 int i;
86 struct tc_cipher_chain *chain;
87 int klen;
88 int length;
89 char buf[1024];
90
91 if (fn == NULL) {
92 errno = EFAULT;
93 return TC_ERR;
94 }
95
96 for (i = 0, chain = tc_cipher_chains[0]; chain != NULL;
97 chain = tc_cipher_chains[++i]) {
98 tc_cipher_chain_sprint(buf, sizeof(buf), chain);
99 klen = tc_cipher_chain_klen(chain);
100 length = tc_cipher_chain_length(chain);
101 if ((fn(priv, buf, klen, length)) < 0)
102 break;
103 }
104
105 return TC_OK;
106 }
107
108 int
tc_api_prf_iterate(tc_api_prf_iterator_fn fn,void * priv)109 tc_api_prf_iterate(tc_api_prf_iterator_fn fn, void *priv)
110 {
111 int i;
112
113 if (fn == NULL) {
114 errno = EFAULT;
115 return TC_ERR;
116 }
117
118 for (i = 0; pbkdf_prf_algos[i].name != NULL; i++) {
119 /* Skip over sys PRFs */
120 if (pbkdf_prf_algos[i].sys)
121 continue;
122
123 if ((fn(priv, pbkdf_prf_algos[i].name)) < 0)
124 break;
125 }
126
127 return TC_OK;
128 }
129
130
131 const char *
tc_api_task_get_error(tc_api_task task __unused)132 tc_api_task_get_error(tc_api_task task __unused)
133 {
134 return tc_internal_log_buffer;
135 }
136
137
138 #define _match(k, v) (strcmp(k, v) == 0)
139
140 tc_api_task
tc_api_task_init(const char * op)141 tc_api_task_init(const char *op)
142 {
143 tc_api_task task = NULL;
144 int fail = 1;
145
146 if ((task = alloc_safe_mem(sizeof(*task))) == NULL) {
147 errno = ENOMEM;
148 goto out;
149 }
150
151 if ((task->opts = opts_init()) == NULL) {
152 errno = ENOMEM;
153 goto out;
154 }
155
156 if (_match(op, "create")) {
157 task->op = TC_OP_CREATE;
158 } else if (_match(op, "map")) {
159 task->op = TC_OP_MAP;
160 } else if (_match(op, "unmap")) {
161 task->op = TC_OP_UNMAP;
162 } else if (_match(op, "info")) {
163 task->op = TC_OP_INFO;
164 } else if (_match(op, "info_mapped")) {
165 task->op = TC_OP_INFO_MAPPED;
166 } else if (_match(op, "modify")) {
167 task->op = TC_OP_MODIFY;
168 } else if (_match(op, "restore")) {
169 task->op = TC_OP_RESTORE;
170 } else {
171 errno = EINVAL;
172 goto out;
173 }
174
175 fail = 0;
176
177 out:
178 if (fail && task != NULL) {
179 if (task->opts != NULL)
180 opts_free(task->opts);
181 free_safe_mem(task);
182 }
183
184 return fail ? NULL : task;
185 }
186
187 int
tc_api_task_uninit(tc_api_task task)188 tc_api_task_uninit(tc_api_task task)
189 {
190 if (task->last_info != NULL)
191 free_info(task->last_info);
192 opts_free(task->opts);
193 free_safe_mem(task);
194
195 return TC_OK;
196 }
197
198
199 #define _set_str(k) \
200 do { \
201 if ((opts->k = strdup_safe_mem(s)) == NULL) { \
202 errno = ENOMEM; \
203 r = TC_ERR; \
204 goto out; \
205 } \
206 } while (0)
207
208 #define _clr_str(k) \
209 do { \
210 if (opts->k) \
211 free_safe_mem(opts->k); \
212 opts->k = NULL; \
213 } while (0)
214
215 int
tc_api_task_set(tc_api_task task,const char * key,...)216 tc_api_task_set(tc_api_task task, const char *key, ...)
217 {
218 struct tcplay_opts *opts;
219 va_list ap;
220 const char *s;
221 int64_t i64;
222 int i;
223 tc_api_state_change_fn sc_fn;
224 void *vp;
225 int r = TC_OK;
226
227 if (task == NULL || key == NULL || ((opts = task->opts) == NULL)) {
228 errno = EFAULT;
229 return TC_ERR;
230 }
231
232 va_start(ap, key);
233
234 if (_match(key, "interactive")) {
235 i = va_arg(ap, int);
236 opts->interactive = i;
237 } else if (_match(key, "weak_keys_and_salt")) {
238 i = va_arg(ap, int);
239 opts->weak_keys_and_salt = i;
240 } else if (_match(key, "secure_erase")) {
241 i = va_arg(ap, int);
242 opts->secure_erase = i;
243 } else if (_match(key, "protect_hidden")) {
244 i = va_arg(ap, int);
245 opts->protect_hidden = i;
246 } else if (_match(key, "fde")) {
247 i = va_arg(ap, int);
248 if (i)
249 opts->flags |= TC_FLAG_FDE;
250 else
251 opts->flags &= ~TC_FLAG_FDE;
252 } else if (_match(key, "use_backup_header")) {
253 i = va_arg(ap, int);
254 if (i)
255 opts->flags |= TC_FLAG_BACKUP;
256 else
257 opts->flags &= ~TC_FLAG_BACKUP;
258 } else if (_match(key, "allow_trim")) {
259 i = va_arg(ap, int);
260 if (i)
261 opts->flags |= TC_FLAG_ALLOW_TRIM;
262 else
263 opts->flags &= ~TC_FLAG_ALLOW_TRIM;
264 } else if (_match(key, "hidden_size_bytes")) {
265 i64 = va_arg(ap, int64_t);
266 opts->hidden_size_bytes = (disksz_t)i64;
267 opts->hidden = (i64 > 0);
268 } else if (_match(key, "retries")) {
269 i = va_arg(ap, int);
270 opts->retries = i;
271 } else if (_match(key, "timeout")) {
272 i = va_arg(ap, int);
273 opts->timeout = (time_t)i;
274 } else if (_match(key, "save_header_to_file")) {
275 s = va_arg(ap, const char *);
276 if (s != NULL) {
277 _set_str(hdr_file_out);
278 opts->flags |= TC_FLAG_SAVE_TO_FILE;
279 } else {
280 _clr_str(hdr_file_out);
281 opts->flags &= ~TC_FLAG_SAVE_TO_FILE;
282 }
283 } else if (_match(key, "header_from_file")) {
284 s = va_arg(ap, const char *);
285 if (s != NULL) {
286 _set_str(hdr_file_in);
287 opts->flags |= TC_FLAG_HDR_FROM_FILE;
288 } else {
289 _clr_str(hdr_file_in);
290 opts->flags &= ~TC_FLAG_HDR_FROM_FILE;
291 }
292 } else if (_match(key, "hidden_header_from_file")) {
293 s = va_arg(ap, const char *);
294 if (s != NULL) {
295 _set_str(h_hdr_file_in);
296 opts->flags |= TC_FLAG_H_HDR_FROM_FILE;
297 } else {
298 _clr_str(h_hdr_file_in);
299 opts->flags &= ~TC_FLAG_H_HDR_FROM_FILE;
300 }
301 } else if (_match(key, "sys")) {
302 s = va_arg(ap, const char *);
303 if (s != NULL) {
304 _set_str(sys_dev);
305 opts->flags |= TC_FLAG_SYS;
306 } else {
307 _clr_str(sys_dev);
308 opts->flags &= ~TC_FLAG_SYS;
309 }
310 } else if (_match(key, "passphrase")) {
311 s = va_arg(ap, const char *);
312 if (s != NULL) {
313 _set_str(passphrase);
314 } else {
315 _clr_str(passphrase);
316 }
317 } else if (_match(key, "h_passphrase")) {
318 s = va_arg(ap, const char *);
319 if (s != NULL) {
320 _set_str(h_passphrase);
321 } else {
322 _clr_str(h_passphrase);
323 }
324 } else if (_match(key, "new_passphrase")) {
325 s = va_arg(ap, const char *);
326 if (s != NULL) {
327 _set_str(new_passphrase);
328 } else {
329 _clr_str(new_passphrase);
330 }
331 } else if (_match(key, "dev")) {
332 s = va_arg(ap, const char *);
333 if (s != NULL) {
334 _set_str(dev);
335 } else {
336 _clr_str(dev);
337 }
338 } else if (_match(key, "map_name")) {
339 s = va_arg(ap, const char *);
340 if (s != NULL) {
341 _set_str(map_name);
342 } else {
343 _clr_str(map_name);
344 }
345 } else if (_match(key, "keyfiles")) {
346 s = va_arg(ap, const char *);
347 if (s != NULL) {
348 opts_add_keyfile(opts, s);
349 } else {
350 opts_clear_keyfile(opts);
351 }
352 } else if (_match(key, "h_keyfiles")) {
353 s = va_arg(ap, const char *);
354 if (s != NULL) {
355 opts_add_keyfile_hidden(opts, s);
356 } else {
357 opts_clear_keyfile_hidden(opts);
358 }
359 } else if (_match(key, "new_keyfiles")) {
360 s = va_arg(ap, const char *);
361 if (s != NULL) {
362 opts_add_keyfile_new(opts, s);
363 } else {
364 opts_clear_keyfile_new(opts);
365 }
366 } else if (_match(key, "prf_algo")) {
367 s = va_arg(ap, const char *);
368 if (s != NULL) {
369 if ((opts->prf_algo = check_prf_algo(s, 0, 1)) == NULL) {
370 errno = ENOENT;
371 r = TC_ERR;
372 goto out;
373 }
374 } else {
375 opts->prf_algo = NULL;
376 }
377 } else if (_match(key, "h_prf_algo")) {
378 s = va_arg(ap, const char *);
379 if (s != NULL) {
380 if ((opts->h_prf_algo = check_prf_algo(s, 0, 1)) == NULL) {
381 errno = ENOENT;
382 r = TC_ERR;
383 goto out;
384 }
385 } else {
386 opts->h_prf_algo = NULL;
387 }
388 } else if (_match(key, "new_prf_algo")) {
389 s = va_arg(ap, const char *);
390 if (s != NULL) {
391 if ((opts->new_prf_algo = check_prf_algo(s, 0, 1)) == NULL) {
392 errno = ENOENT;
393 r = TC_ERR;
394 goto out;
395 }
396 } else {
397 opts->new_prf_algo = NULL;
398 }
399 } else if (_match(key, "cipher_chain")) {
400 s = va_arg(ap, const char *);
401 if (s != NULL) {
402 if ((opts->cipher_chain = check_cipher_chain(s, 1)) == NULL) {
403 errno = ENOENT;
404 r = TC_ERR;
405 goto out;
406 }
407 } else {
408 opts->cipher_chain = NULL;
409 }
410 } else if (_match(key, "h_cipher_chain")) {
411 s = va_arg(ap, const char *);
412 if (s != NULL) {
413 if ((opts->h_cipher_chain = check_cipher_chain(s, 1)) == NULL) {
414 errno = ENOENT;
415 r = TC_ERR;
416 goto out;
417 }
418 } else {
419 opts->h_cipher_chain = NULL;
420 }
421 } else if (_match(key, "state_change_fn")) {
422 sc_fn = va_arg(ap, tc_api_state_change_fn);
423 opts->state_change_fn = sc_fn;
424 vp = va_arg(ap, void *);
425 opts->api_ctx = vp;
426 } else {
427 r = TC_ERR_UNIMPL;
428 }
429
430 out:
431 va_end(ap);
432
433 return r;
434 }
435
436 #define _not_null(x) \
437 if (opts->x == NULL) { \
438 return -1; \
439 }
440
441 #define _null(x) \
442 if (opts->x != NULL) { \
443 return -1; \
444 }
445
446 #define _zero(x) \
447 if (opts->x != 0) { \
448 return -1; \
449 }
450
451 #define _not_set(x) \
452 if (TC_FLAG_SET(opts->flags, x)) { \
453 return -1; \
454 }
455
456 static
457 int
_opts_check_create(struct tcplay_opts * opts)458 _opts_check_create(struct tcplay_opts *opts)
459 {
460 _not_null(dev);
461 _not_set(SYS);
462 _not_set(FDE);
463 _not_set(BACKUP);
464 _not_set(ONLY_RESTORE);
465 _not_set(ALLOW_TRIM);
466 _not_set(SAVE_TO_FILE);
467 _not_set(HDR_FROM_FILE);
468 _not_set(H_HDR_FROM_FILE);
469
470 _null(map_name);
471 _zero(protect_hidden);
472 _null(new_passphrase);
473 _null(new_prf_algo);
474 _zero(n_newkeyfiles);
475
476 if (opts->hidden_size_bytes && !opts->hidden) {
477 return -1;
478 }
479
480 return 0;
481 }
482
483 static
484 int
_opts_check_map(struct tcplay_opts * opts)485 _opts_check_map(struct tcplay_opts *opts)
486 {
487 _not_null(dev);
488 _not_null(map_name);
489 _not_set(ONLY_RESTORE);
490 _not_set(SAVE_TO_FILE);
491 _zero(hidden);
492 _zero(hidden_size_bytes);
493 _null(new_passphrase);
494 _null(new_prf_algo);
495 _zero(n_newkeyfiles);
496 _null(prf_algo);
497 _null(h_prf_algo);
498 _null(cipher_chain);
499 _null(h_cipher_chain);
500
501 if (!opts->protect_hidden) {
502 _zero(n_hkeyfiles);
503 //_null(h_passphrase);
504 }
505
506 return 0;
507 }
508
509 static
510 int
_opts_check_unmap(struct tcplay_opts * opts)511 _opts_check_unmap(struct tcplay_opts *opts)
512 {
513 _not_null(map_name);
514 /* XXX: _not_null(dev); ? */
515 _zero(nkeyfiles);
516 _zero(n_hkeyfiles);
517 _null(prf_algo);
518 _null(cipher_chain);
519 _null(h_prf_algo);
520 _null(h_cipher_chain);
521 _null(passphrase);
522 _null(h_passphrase);
523 _zero(hidden);
524 _zero(protect_hidden);
525 _null(new_prf_algo);
526 _null(new_passphrase);
527 _zero(n_newkeyfiles);
528 _not_set(SYS);
529 _not_set(FDE);
530 _not_set(BACKUP);
531 _not_set(ONLY_RESTORE);
532 _not_set(ALLOW_TRIM);
533 _not_set(SAVE_TO_FILE);
534 _not_set(HDR_FROM_FILE);
535 _not_set(H_HDR_FROM_FILE);
536
537 return 0;
538 }
539
540 static
541 int
_opts_check_info(struct tcplay_opts * opts)542 _opts_check_info(struct tcplay_opts *opts)
543 {
544 _not_null(dev);
545 _null(map_name);
546 _not_set(ONLY_RESTORE);
547 _not_set(SAVE_TO_FILE);
548 _zero(hidden);
549 _zero(hidden_size_bytes);
550 _null(new_passphrase);
551 _null(new_prf_algo);
552 _zero(n_newkeyfiles);
553 _null(prf_algo);
554 _null(h_prf_algo);
555 _null(cipher_chain);
556 _null(h_cipher_chain);
557
558 if (!opts->protect_hidden) {
559 _zero(n_hkeyfiles);
560 //_null(h_passphrase);
561 }
562
563 return 0;
564 }
565
566 static
567 int
_opts_check_info_mapped(struct tcplay_opts * opts)568 _opts_check_info_mapped(struct tcplay_opts *opts)
569 {
570 _not_null(map_name);
571 /* XXX: _not_null(dev); ? */
572 _zero(nkeyfiles);
573 _zero(n_hkeyfiles);
574 _null(prf_algo);
575 _null(cipher_chain);
576 _null(h_prf_algo);
577 _null(h_cipher_chain);
578 _null(passphrase);
579 _null(h_passphrase);
580 _zero(hidden);
581 _zero(protect_hidden);
582 _null(new_prf_algo);
583 _null(new_passphrase);
584 _zero(n_newkeyfiles);
585 _not_set(SYS);
586 _not_set(FDE);
587 _not_set(BACKUP);
588 _not_set(ONLY_RESTORE);
589 _not_set(ALLOW_TRIM);
590 _not_set(SAVE_TO_FILE);
591 _not_set(HDR_FROM_FILE);
592 _not_set(H_HDR_FROM_FILE);
593
594 return 0;
595 }
596
597 static
598 int
_opts_check_modify(struct tcplay_opts * opts)599 _opts_check_modify(struct tcplay_opts *opts)
600 {
601 _not_null(dev);
602 _null(map_name);
603 _zero(hidden);
604 _zero(hidden_size_bytes);
605 _null(prf_algo);
606 _null(h_prf_algo);
607 _null(cipher_chain);
608 _null(h_cipher_chain);
609
610 if (!opts->protect_hidden) {
611 _zero(n_hkeyfiles);
612 _null(h_passphrase);
613 }
614
615 return 0;
616 }
617
618
619 static
620 int
_opts_check_restore(struct tcplay_opts * opts)621 _opts_check_restore(struct tcplay_opts *opts)
622 {
623 if ((_opts_check_modify(opts)) < 0)
624 return -1;
625
626 _null(new_prf_algo);
627 _zero(n_newkeyfiles);
628 _null(new_passphrase);
629
630 return 0;
631 }
632
633 int
tc_api_task_do(tc_api_task task)634 tc_api_task_do(tc_api_task task)
635 {
636 struct tcplay_opts *opts;
637 int r = TC_OK;
638
639 if (task == NULL || ((opts = task->opts) == NULL)) {
640 errno = EFAULT;
641 return TC_ERR;
642 }
643
644 if (task->last_info != NULL) {
645 free_info(task->last_info);
646 }
647
648 switch (task->op) {
649 case TC_OP_CREATE:
650 if ((r = _opts_check_create(task->opts)) != 0) {
651 errno = EINVAL;
652 return r;
653 }
654 r = create_volume(opts);
655 break;
656
657 case TC_OP_MAP:
658 if ((r = _opts_check_map(task->opts)) != 0) {
659 errno = EINVAL;
660 return r;
661 }
662 r = map_volume(opts);
663 break;
664
665 case TC_OP_UNMAP:
666 if ((r = _opts_check_unmap(task->opts)) != 0) {
667 errno = EINVAL;
668 return r;
669 }
670 r = dm_teardown(opts->map_name, opts->dev);
671 break;
672
673 case TC_OP_INFO:
674 if ((r = _opts_check_info(task->opts)) != 0) {
675 errno = EINVAL;
676 return r;
677 }
678 if ((task->last_info = info_map_common(opts, NULL)) == NULL) {
679 r = TC_ERR;
680 }
681 break;
682
683 case TC_OP_INFO_MAPPED:
684 if ((r = _opts_check_info_mapped(task->opts)) != 0) {
685 errno = EINVAL;
686 return r;
687 }
688 if ((task->last_info = dm_info_map(opts->map_name)) == NULL) {
689 r = TC_ERR;
690 }
691 break;
692
693 case TC_OP_MODIFY:
694 if ((r = _opts_check_modify(task->opts)) != 0) {
695 errno = EINVAL;
696 return r;
697 }
698 r = modify_volume(opts);
699 break;
700
701 case TC_OP_RESTORE:
702 if ((r = _opts_check_restore(task->opts)) != 0) {
703 errno = EINVAL;
704 return r;
705 }
706 opts->flags |= TC_FLAG_ONLY_RESTORE;
707 r = modify_volume(opts);
708 opts->flags &= ~TC_FLAG_ONLY_RESTORE;
709 break;
710 }
711
712 return r;
713 }
714
715
716 int
tc_api_task_info_get(tc_api_task task,const char * key,...)717 tc_api_task_info_get(tc_api_task task, const char *key, ...)
718 {
719 char buf[1024];
720 va_list ap;
721 struct tcplay_info *info;
722 char *s;
723 int *ip;
724 int64_t *i64p;
725 int r = TC_OK;
726 size_t sz;
727
728 if (task == NULL || ((info = task->last_info) == NULL)) {
729 errno = EFAULT;
730 return TC_ERR;
731 }
732
733 va_start(ap, key);
734 sz = va_arg(ap, size_t);
735 if (sz < 1) {
736 errno = EINVAL;
737 r = TC_ERR;
738 goto out;
739 }
740
741 if (_match(key, "device")) {
742 s = va_arg(ap, char *);
743 strncpy(s, info->dev, sz);
744 s[sz-1] = '\0';
745 } else if (_match(key, "cipher")) {
746 s = va_arg(ap, char *);
747 tc_cipher_chain_sprint(buf, sizeof(buf), info->cipher_chain);
748 strncpy(s, buf, sz);
749 s[sz-1] = '\0';
750 } else if (_match(key, "prf")) {
751 s = va_arg(ap, char *);
752 if (info->pbkdf_prf)
753 strncpy(s, info->pbkdf_prf->name, sz);
754 else
755 strncpy(s, "(unknown)", sz);
756 s[sz-1] = '\0';
757 } else if (_match(key, "key_bits")) {
758 if (sz != sizeof(int)) {
759 errno = EFAULT;
760 r = TC_ERR;
761 goto out;
762 }
763 ip = va_arg(ap, int *);
764 *ip = 8*tc_cipher_chain_klen(info->cipher_chain);
765 } else if (_match(key, "size")) {
766 if (sz != sizeof(int64_t)) {
767 errno = EFAULT;
768 r = TC_ERR;
769 goto out;
770 }
771 i64p = va_arg(ap, int64_t *);
772 if (info->hdr)
773 *i64p = (int64_t)info->size * (int64_t)info->hdr->sec_sz;
774 else
775 *i64p = (int64_t)info->size * (int64_t)info->blk_sz;
776 } else if (_match(key, "iv_offset")) {
777 if (sz != sizeof(int64_t)) {
778 errno = EFAULT;
779 r = TC_ERR;
780 goto out;
781 }
782 i64p = va_arg(ap, int64_t *);
783 if (info->hdr)
784 *i64p = (int64_t)info->skip * (int64_t)info->hdr->sec_sz;
785 else
786 *i64p = (int64_t)info->skip * (int64_t)info->blk_sz;
787 } else if (_match(key, "block_offset")) {
788 if (sz != sizeof(int64_t)) {
789 errno = EFAULT;
790 r = TC_ERR;
791 goto out;
792 }
793 i64p = va_arg(ap, int64_t *);
794 if (info->hdr)
795 *i64p = (int64_t)info->offset * (int64_t)info->hdr->sec_sz;
796 else
797 *i64p = (int64_t)info->offset * (int64_t)info->blk_sz;
798 } else {
799 r = TC_ERR_UNIMPL;
800 }
801
802 out:
803 va_end(ap);
804
805 return r;
806 }
807