Merge branch 'vendor/BINUTILS225'
[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         /* start at 1 due to RIPEMD weirdness... */
119         for (i = 1; pbkdf_prf_algos[i].name != NULL; i++) {
120                 if ((fn(priv, pbkdf_prf_algos[i].name)) < 0)
121                         break;
122         }
123
124         return TC_OK;
125 }
126
127
128 const char *
129 tc_api_task_get_error(tc_api_task task __unused)
130 {
131         return tc_internal_log_buffer;
132 }
133
134
135 #define _match(k, v) (strcmp(k, v) == 0)
136
137 tc_api_task
138 tc_api_task_init(const char *op)
139 {
140         tc_api_task task = NULL;
141         int fail = 1;
142
143         if ((task = alloc_safe_mem(sizeof(*task))) == NULL) {
144                 errno = ENOMEM;
145                 goto out;
146         }
147
148         if ((task->opts = opts_init()) == NULL) {
149                 errno = ENOMEM;
150                 goto out;
151         }
152
153         if (_match(op, "create")) {
154                 task->op = TC_OP_CREATE;
155         } else if (_match(op, "map")) {
156                 task->op = TC_OP_MAP;
157         } else if (_match(op, "unmap")) {
158                 task->op = TC_OP_UNMAP;
159         } else if (_match(op, "info")) {
160                 task->op = TC_OP_INFO;
161         } else if (_match(op, "info_mapped")) {
162                 task->op = TC_OP_INFO_MAPPED;
163         } else if (_match(op, "modify")) {
164                 task->op = TC_OP_MODIFY;
165         } else if (_match(op, "restore")) {
166                 task->op = TC_OP_RESTORE;
167         } else {
168                 errno = EINVAL;
169                 goto out;
170         }
171
172         fail = 0;
173
174 out:
175         if (fail && task != NULL) {
176                 if (task->opts != NULL)
177                         opts_free(task->opts);
178                 free_safe_mem(task);
179         }
180
181         return fail ? NULL : task;
182 }
183
184 int
185 tc_api_task_uninit(tc_api_task task)
186 {
187         if (task->last_info != NULL)
188                 free_info(task->last_info);
189         opts_free(task->opts);
190         free_safe_mem(task);
191
192         return TC_OK;
193 }
194
195
196 #define _set_str(k) \
197         do {                                                    \
198                 if ((opts->k = strdup_safe_mem(s)) == NULL) {   \
199                         errno = ENOMEM;                         \
200                         r = TC_ERR;                             \
201                         goto out;                               \
202                 }                                               \
203         } while (0)
204
205 #define _clr_str(k) \
206         do {                                                    \
207                 if (opts->k)                                    \
208                         free_safe_mem(opts->k);                 \
209                 opts->k = NULL;                                 \
210         } while (0)
211
212 int
213 tc_api_task_set(tc_api_task task, const char *key, ...)
214 {
215         struct tcplay_opts *opts;
216         va_list ap;
217         const char *s;
218         int64_t i64;
219         int i;
220         tc_api_state_change_fn sc_fn;
221         void *vp;
222         int r = TC_OK;
223
224         if (task == NULL || ((opts = task->opts) == NULL)) {
225                 errno = EFAULT;
226                 return TC_ERR;
227         }
228
229         va_start(ap, key);
230
231         if (_match(key, "interactive")) {
232                 i = va_arg(ap, int);
233                 opts->interactive = i;
234         } else if (_match(key, "weak_keys_and_salt")) {
235                 i = va_arg(ap, int);
236                 opts->weak_keys_and_salt = i;
237         } else if (_match(key, "secure_erase")) {
238                 i = va_arg(ap, int);
239                 opts->secure_erase = i;
240         } else if (_match(key, "protect_hidden")) {
241                 i = va_arg(ap, int);
242                 opts->protect_hidden = i;
243         } else if (_match(key, "fde")) {
244                 i = va_arg(ap, int);
245                 if (i)
246                         opts->flags |= TC_FLAG_FDE;
247                 else
248                         opts->flags &= ~TC_FLAG_FDE;
249         } else if (_match(key, "use_backup_header")) {
250                 i = va_arg(ap, int);
251                 if (i)
252                         opts->flags |= TC_FLAG_BACKUP;
253                 else
254                         opts->flags &= ~TC_FLAG_BACKUP;
255         } else if (_match(key, "allow_trim")) {
256                 i = va_arg(ap, int);
257                 if (i)
258                         opts->flags |= TC_FLAG_ALLOW_TRIM;
259                 else
260                         opts->flags &= ~TC_FLAG_ALLOW_TRIM;
261         } else if (_match(key, "hidden_size_bytes")) {
262                 i64 = va_arg(ap, int64_t);
263                 opts->hidden_size_bytes = (disksz_t)i64;
264                 opts->hidden = (i64 > 0);
265         } else if (_match(key, "retries")) {
266                 i = va_arg(ap, int);
267                 opts->retries = i;
268         } else if (_match(key, "timeout")) {
269                 i = va_arg(ap, int);
270                 opts->timeout = (time_t)i;
271         } else if (_match(key, "save_header_to_file")) {
272                 s = va_arg(ap, const char *);
273                 if (s != NULL) {
274                         _set_str(hdr_file_out);
275                         opts->flags |= TC_FLAG_SAVE_TO_FILE;
276                 } else {
277                         _clr_str(hdr_file_out);
278                         opts->flags &= ~TC_FLAG_SAVE_TO_FILE;
279                 }
280         } else if (_match(key, "header_from_file")) {
281                 s = va_arg(ap, const char *);
282                 if (s != NULL) {
283                         _set_str(hdr_file_in);
284                         opts->flags |= TC_FLAG_HDR_FROM_FILE;
285                 } else {
286                         _clr_str(hdr_file_in);
287                         opts->flags &= ~TC_FLAG_HDR_FROM_FILE;
288                 }
289         } else if (_match(key, "hidden_header_from_file")) {
290                 s = va_arg(ap, const char *);
291                 if (s != NULL) {
292                         _set_str(h_hdr_file_in);
293                         opts->flags |= TC_FLAG_H_HDR_FROM_FILE;
294                 } else {
295                         _clr_str(h_hdr_file_in);
296                         opts->flags &= ~TC_FLAG_H_HDR_FROM_FILE;
297                 }
298         } else if (_match(key, "sys")) {
299                 s = va_arg(ap, const char *);
300                 if (s != NULL) {
301                         _set_str(sys_dev);
302                         opts->flags |= TC_FLAG_SYS;
303                 } else {
304                         _clr_str(sys_dev);
305                         opts->flags &= ~TC_FLAG_SYS;
306                 }
307         } else if (_match(key, "passphrase")) {
308                 s = va_arg(ap, const char *);
309                 if (s != NULL) {
310                         _set_str(passphrase);
311                 } else {
312                         _clr_str(passphrase);
313                 }
314         } else if (_match(key, "h_passphrase")) {
315                 s = va_arg(ap, const char *);
316                 if (s != NULL) {
317                         _set_str(h_passphrase);
318                 } else {
319                         _clr_str(h_passphrase);
320                 }
321         } else if (_match(key, "new_passphrase")) {
322                 s = va_arg(ap, const char *);
323                 if (s != NULL) {
324                         _set_str(new_passphrase);
325                 } else {
326                         _clr_str(new_passphrase);
327                 }
328         } else if (_match(key, "dev")) {
329                 s = va_arg(ap, const char *);
330                 if (s != NULL) {
331                         _set_str(dev);
332                 } else {
333                         _clr_str(dev);
334                 }
335         } else if (_match(key, "map_name")) {
336                 s = va_arg(ap, const char *);
337                 if (s != NULL) {
338                         _set_str(map_name);
339                 } else {
340                         _clr_str(map_name);
341                 }
342         } else if (_match(key, "keyfiles")) {
343                 s = va_arg(ap, const char *);
344                 if (s != NULL) {
345                         opts_add_keyfile(opts, s);
346                 } else {
347                         opts_clear_keyfile(opts);
348                 }
349         } else if (_match(key, "h_keyfiles")) {
350                 s = va_arg(ap, const char *);
351                 if (s != NULL) {
352                         opts_add_keyfile_hidden(opts, s);
353                 } else {
354                         opts_clear_keyfile_hidden(opts);
355                 }
356         } else if (_match(key, "new_keyfiles")) {
357                 s = va_arg(ap, const char *);
358                 if (s != NULL) {
359                         opts_add_keyfile_new(opts, s);
360                 } else {
361                         opts_clear_keyfile_new(opts);
362                 }
363         } else if (_match(key, "prf_algo")) {
364                 s = va_arg(ap, const char *);
365                 if (s != NULL) {
366                         if ((opts->prf_algo = check_prf_algo(s, 1)) == NULL) {
367                                 errno = ENOENT;
368                                 r = TC_ERR;
369                                 goto out;
370                         }
371                 } else {
372                         opts->prf_algo = NULL;
373                 }
374         } else if (_match(key, "h_prf_algo")) {
375                 s = va_arg(ap, const char *);
376                 if (s != NULL) {
377                         if ((opts->h_prf_algo = check_prf_algo(s, 1)) == NULL) {
378                                 errno = ENOENT;
379                                 r = TC_ERR;
380                                 goto out;
381                         }
382                 } else {
383                         opts->h_prf_algo = NULL;
384                 }
385         } else if (_match(key, "new_prf_algo")) {
386                 s = va_arg(ap, const char *);
387                 if (s != NULL) {
388                         if ((opts->new_prf_algo = check_prf_algo(s, 1)) == NULL) {
389                                 errno = ENOENT;
390                                 r = TC_ERR;
391                                 goto out;
392                         }
393                 } else {
394                         opts->new_prf_algo = NULL;
395                 }
396         } else if (_match(key, "cipher_chain")) {
397                 s = va_arg(ap, const char *);
398                 if (s != NULL) {
399                         if ((opts->cipher_chain = check_cipher_chain(s, 1)) == NULL) {
400                                 errno = ENOENT;
401                                 r = TC_ERR;
402                                 goto out;
403                         }
404                 } else {
405                         opts->cipher_chain = NULL;
406                 }
407         } else if (_match(key, "h_cipher_chain")) {
408                 s = va_arg(ap, const char *);
409                 if (s != NULL) {
410                         if ((opts->h_cipher_chain = check_cipher_chain(s, 1)) == NULL) {
411                                 errno = ENOENT;
412                                 r = TC_ERR;
413                                 goto out;
414                         }
415                 } else {
416                         opts->h_cipher_chain = NULL;
417                 }
418         } else if (_match(key, "state_change_fn")) {
419                 sc_fn = va_arg(ap, tc_api_state_change_fn);
420                 opts->state_change_fn = sc_fn;
421                 vp = va_arg(ap, void *);
422                 opts->api_ctx = vp;
423         } else {
424                 r = TC_ERR_UNIMPL;
425         }
426
427 out:
428         va_end(ap);
429
430         return r;
431 }
432
433 #define _not_null(x) \
434         if (opts->x == NULL) {  \
435                 return -1;      \
436         }
437
438 #define _null(x) \
439         if (opts->x != NULL) {  \
440                 return -1;      \
441         }
442
443 #define _zero(x) \
444         if (opts->x != 0) {     \
445                 return -1;      \
446         }
447
448 #define _not_set(x) \
449         if (TC_FLAG_SET(opts->flags, x)) {      \
450                 return -1;                      \
451         }
452
453 static
454 int
455 _opts_check_create(struct tcplay_opts *opts)
456 {
457         _not_null(dev);
458         _not_set(SYS);
459         _not_set(FDE);
460         _not_set(BACKUP);
461         _not_set(ONLY_RESTORE);
462         _not_set(ALLOW_TRIM);
463         _not_set(SAVE_TO_FILE);
464         _not_set(HDR_FROM_FILE);
465         _not_set(H_HDR_FROM_FILE);
466
467         _null(map_name);
468         _zero(protect_hidden);
469         _null(new_passphrase);
470         _null(new_prf_algo);
471         _zero(n_newkeyfiles);
472
473         if (opts->hidden_size_bytes && !opts->hidden) {
474                 return -1;
475         }
476
477         return 0;
478 }
479
480 static
481 int
482 _opts_check_map(struct tcplay_opts *opts)
483 {
484         _not_null(dev);
485         _not_null(map_name);
486         _not_set(ONLY_RESTORE);
487         _not_set(SAVE_TO_FILE);
488         _zero(hidden);
489         _zero(hidden_size_bytes);
490         _null(new_passphrase);
491         _null(new_prf_algo);
492         _zero(n_newkeyfiles);
493         _null(prf_algo);
494         _null(h_prf_algo);
495         _null(cipher_chain);
496         _null(h_cipher_chain);
497
498         if (!opts->protect_hidden) {
499                 _zero(n_hkeyfiles);
500                 //_null(h_passphrase);
501         }
502
503         return 0;
504 }
505
506 static
507 int
508 _opts_check_unmap(struct tcplay_opts *opts)
509 {
510         _not_null(map_name);
511         /* XXX: _not_null(dev); ? */
512         _zero(nkeyfiles);
513         _zero(n_hkeyfiles);
514         _null(prf_algo);
515         _null(cipher_chain);
516         _null(h_prf_algo);
517         _null(h_cipher_chain);
518         _null(passphrase);
519         _null(h_passphrase);
520         _zero(hidden);
521         _zero(protect_hidden);
522         _null(new_prf_algo);
523         _null(new_passphrase);
524         _zero(n_newkeyfiles);
525         _not_set(SYS);
526         _not_set(FDE);
527         _not_set(BACKUP);
528         _not_set(ONLY_RESTORE);
529         _not_set(ALLOW_TRIM);
530         _not_set(SAVE_TO_FILE);
531         _not_set(HDR_FROM_FILE);
532         _not_set(H_HDR_FROM_FILE);
533
534         return 0;
535 }
536
537 static
538 int
539 _opts_check_info(struct tcplay_opts *opts)
540 {
541         _not_null(dev);
542         _null(map_name);
543         _not_set(ONLY_RESTORE);
544         _not_set(SAVE_TO_FILE);
545         _zero(hidden);
546         _zero(hidden_size_bytes);
547         _null(new_passphrase);
548         _null(new_prf_algo);
549         _zero(n_newkeyfiles);
550         _null(prf_algo);
551         _null(h_prf_algo);
552         _null(cipher_chain);
553         _null(h_cipher_chain);
554
555         if (!opts->protect_hidden) {
556                 _zero(n_hkeyfiles);
557                 //_null(h_passphrase);
558         }
559
560         return 0;
561 }
562
563 static
564 int
565 _opts_check_info_mapped(struct tcplay_opts *opts)
566 {
567         _not_null(map_name);
568         /* XXX: _not_null(dev); ? */
569         _zero(nkeyfiles);
570         _zero(n_hkeyfiles);
571         _null(prf_algo);
572         _null(cipher_chain);
573         _null(h_prf_algo);
574         _null(h_cipher_chain);
575         _null(passphrase);
576         _null(h_passphrase);
577         _zero(hidden);
578         _zero(protect_hidden);
579         _null(new_prf_algo);
580         _null(new_passphrase);
581         _zero(n_newkeyfiles);
582         _not_set(SYS);
583         _not_set(FDE);
584         _not_set(BACKUP);
585         _not_set(ONLY_RESTORE);
586         _not_set(ALLOW_TRIM);
587         _not_set(SAVE_TO_FILE);
588         _not_set(HDR_FROM_FILE);
589         _not_set(H_HDR_FROM_FILE);
590
591         return 0;
592 }
593
594 static
595 int
596 _opts_check_modify(struct tcplay_opts *opts)
597 {
598         _not_null(dev);
599         _null(map_name);
600         _zero(hidden);
601         _zero(hidden_size_bytes);
602         _null(prf_algo);
603         _null(h_prf_algo);
604         _null(cipher_chain);
605         _null(h_cipher_chain);
606
607         if (!opts->protect_hidden) {
608                 _zero(n_hkeyfiles);
609                 _null(h_passphrase);
610         }
611
612         return 0;
613 }
614
615
616 static
617 int
618 _opts_check_restore(struct tcplay_opts *opts)
619 {
620         if ((_opts_check_modify(opts)) < 0)
621                 return -1;
622
623         _null(new_prf_algo);
624         _zero(n_newkeyfiles);
625         _null(new_passphrase);
626
627         return 0;
628 }
629
630 int
631 tc_api_task_do(tc_api_task task)
632 {
633         struct tcplay_opts *opts;
634         int r = TC_OK;
635
636         if (task == NULL || ((opts = task->opts) == NULL)) {
637                 errno = EFAULT;
638                 return TC_ERR;
639         }
640
641         if (task->last_info != NULL) {
642                 free_info(task->last_info);
643         }
644
645         switch (task->op) {
646         case TC_OP_CREATE:
647                 if ((r = _opts_check_create(task->opts)) != 0) {
648                         errno = EINVAL;
649                         return r;
650                 }
651                 r = create_volume(opts);
652                 break;
653
654         case TC_OP_MAP:
655                 if ((r = _opts_check_map(task->opts)) != 0) {
656                         errno = EINVAL;
657                         return r;
658                 }
659                 r = map_volume(opts);
660                 break;
661
662         case TC_OP_UNMAP:
663                 if ((r = _opts_check_unmap(task->opts)) != 0) {
664                         errno = EINVAL;
665                         return r;
666                 }
667                 r = dm_teardown(opts->map_name, opts->dev);
668                 break;
669
670         case TC_OP_INFO:
671                 if ((r = _opts_check_info(task->opts)) != 0) {
672                         errno = EINVAL;
673                         return r;
674                 }
675                 if ((task->last_info = info_map_common(opts, NULL)) == NULL) {
676                         r = TC_ERR;
677                 }
678                 break;
679
680         case TC_OP_INFO_MAPPED:
681                 if ((r = _opts_check_info_mapped(task->opts)) != 0) {
682                         errno = EINVAL;
683                         return r;
684                 }
685                 if ((task->last_info = dm_info_map(opts->map_name)) == NULL) {
686                         r = TC_ERR;
687                 }
688                 break;
689
690         case TC_OP_MODIFY:
691                 if ((r = _opts_check_modify(task->opts)) != 0) {
692                         errno = EINVAL;
693                         return r;
694                 }
695                 r = modify_volume(opts);
696                 break;
697
698         case TC_OP_RESTORE:
699                 if ((r = _opts_check_restore(task->opts)) != 0) {
700                         errno = EINVAL;
701                         return r;
702                 }
703                 opts->flags |= TC_FLAG_ONLY_RESTORE;
704                 r = modify_volume(opts);
705                 opts->flags &= ~TC_FLAG_ONLY_RESTORE;
706                 break;
707         }
708
709         return r;
710 }
711
712
713 int
714 tc_api_task_info_get(tc_api_task task, const char *key, ...)
715 {
716         char buf[1024];
717         va_list ap;
718         struct tcplay_info *info;
719         char *s;
720         int *ip;
721         int64_t *i64p;
722         int r = TC_OK;
723         size_t sz;
724
725         if (task == NULL || ((info = task->last_info) == NULL)) {
726                 errno = EFAULT;
727                 return TC_ERR;
728         }
729
730         va_start(ap, key);
731         sz = va_arg(ap, size_t);
732         if (sz < 1) {
733                 errno = EINVAL;
734                 r = TC_ERR;
735                 goto out;
736         }
737
738         if (_match(key, "device")) {
739                 s = va_arg(ap, char *);
740                 strncpy(s, info->dev, sz);
741                 s[sz-1] = '\0';
742         } else if (_match(key, "cipher")) {
743                 s = va_arg(ap, char *);
744                 tc_cipher_chain_sprint(buf, sizeof(buf), info->cipher_chain);
745                 strncpy(s, buf, sz);
746                 s[sz-1] = '\0';
747         } else if (_match(key, "prf")) {
748                 s = va_arg(ap, char *);
749                 if (info->pbkdf_prf)
750                         strncpy(s, info->pbkdf_prf->name, sz);
751                 else
752                         strncpy(s, "(unknown)", sz);
753                 s[sz-1] = '\0';
754         } else if (_match(key, "key_bits")) {
755                 if (sz != sizeof(int)) {
756                         errno = EFAULT;
757                         r = TC_ERR;
758                         goto out;
759                 }
760                 ip = va_arg(ap, int *);
761                 *ip = 8*tc_cipher_chain_klen(info->cipher_chain);
762         } else if (_match(key, "size")) {
763                 if (sz != sizeof(int64_t)) {
764                         errno = EFAULT;
765                         r = TC_ERR;
766                         goto out;
767                 }
768                 i64p = va_arg(ap, int64_t *);
769                 if (info->hdr)
770                         *i64p = (int64_t)info->size * (int64_t)info->hdr->sec_sz;
771                 else
772                         *i64p = (int64_t)info->size * (int64_t)info->blk_sz;
773         } else if (_match(key, "iv_offset")) {
774                 if (sz != sizeof(int64_t)) {
775                         errno = EFAULT;
776                         r = TC_ERR;
777                         goto out;
778                 }
779                 i64p = va_arg(ap, int64_t *);
780                 if (info->hdr)
781                         *i64p = (int64_t)info->skip * (int64_t)info->hdr->sec_sz;
782                 else
783                         *i64p = (int64_t)info->skip * (int64_t)info->blk_sz;
784         } else if (_match(key, "block_offset")) {
785                 if (sz != sizeof(int64_t)) {
786                         errno = EFAULT;
787                         r = TC_ERR;
788                         goto out;
789                 }
790                 i64p = va_arg(ap, int64_t *);
791                 if (info->hdr)
792                         *i64p = (int64_t)info->offset * (int64_t)info->hdr->sec_sz;
793                 else
794                         *i64p = (int64_t)info->offset * (int64_t)info->blk_sz;
795         } else {
796                 r = TC_ERR_UNIMPL;
797         }
798
799 out:
800         va_end(ap);
801
802         return r;
803 }