Update tcplay from 2.0 to 3.3
[dragonfly.git] / lib / libtcplay / tcplay_api.c
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
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
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
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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 }