"Fossies" - the Fresh Open Source Software Archive

Member "tc-play-3.3/tcplay_api.c" (2 Mar 2020, 16459 Bytes) of package /linux/misc/tc-play-3.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "tcplay_api.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.0_vs_3.1.

    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 }