static struct tcplay_info * new_info(const char *dev, int flags, struct tc_cipher_chain *cipher_chain, struct pbkdf_prf_algo *prf, struct tchdr_dec *hdr, off_t start) { struct tc_cipher_chain *chain_start; struct tcplay_info *info; int i; int error; chain_start = cipher_chain; if ((info = (struct tcplay_info *)alloc_safe_mem(sizeof(*info))) == NULL) { tc_log(1, "could not allocate safe info memory\n"); return NULL; } strncpy(info->dev, dev, sizeof(info->dev)); info->cipher_chain = cipher_chain; info->pbkdf_prf = prf; info->start = start; info->hdr = hdr; info->blk_sz = hdr->sec_sz; info->size = hdr->sz_mk_scope / hdr->sec_sz; /* volume size */ info->skip = hdr->off_mk_scope / hdr->sec_sz; /* iv skip */ info->volflags = hdr->flags; info->flags = flags; if (TC_FLAG_SET(flags, SYS)) info->offset = 0; /* offset is 0 for system volumes */ else info->offset = hdr->off_mk_scope / hdr->sec_sz; /* block offset */ /* Associate a key out of the key pool with each cipher in the chain */ error = tc_cipher_chain_populate_keys(cipher_chain, hdr->keys); if (error) { tc_log(1, "could not populate keys in cipher chain\n"); return NULL; } for (; cipher_chain != NULL; cipher_chain = cipher_chain->next) { for (i = 0; i < cipher_chain->cipher->klen; i++) sprintf(&cipher_chain->dm_key[i*2], "%02x", cipher_chain->key[i]); } tc_cipher_chain_free_keys(chain_start); return info; }
int main(int argc, char *argv[]) { const char *dev = NULL, *sys_dev = NULL, *map_name = NULL; const char *keyfiles[MAX_KEYFILES]; const char *h_keyfiles[MAX_KEYFILES]; int nkeyfiles; int n_hkeyfiles; int ch, error; int flags = 0; int info_vol = 0, map_vol = 0, protect_hidden = 0, unmap_vol = 0, info_map = 0, create_vol = 0, contain_hidden = 0, use_secure_erase = 1, use_weak_keys = 0; struct pbkdf_prf_algo *prf = NULL; struct tc_cipher_chain *cipher_chain = NULL; struct pbkdf_prf_algo *h_prf = NULL; struct tc_cipher_chain *h_cipher_chain = NULL; if ((error = tc_play_init()) != 0) { fprintf(stderr, "Initialization failed, exiting."); exit(EXIT_FAILURE); } atexit(check_and_purge_safe_mem); signal(SIGUSR1, sig_handler); signal(SIGINFO, sig_handler); nkeyfiles = 0; n_hkeyfiles = 0; while ((ch = getopt_long(argc, argv, "a:b:cd:ef:ghij:k:m:s:u:vwx:y:z", longopts, NULL)) != -1) { switch(ch) { case 'a': if (prf != NULL) usage(); if ((prf = check_prf_algo(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'b': if (cipher_chain != NULL) usage(); if ((cipher_chain = check_cipher_chain(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'c': create_vol = 1; break; case 'd': dev = optarg; break; case 'e': protect_hidden = 1; break; case 'f': h_keyfiles[n_hkeyfiles++] = optarg; break; case 'g': contain_hidden = 1; break; case 'i': info_vol = 1; break; case 'j': info_map = 1; map_name = optarg; break; case 'k': keyfiles[nkeyfiles++] = optarg; break; case 'm': map_vol = 1; map_name = optarg; break; case 's': flags |= TC_FLAG_SYS; sys_dev = optarg; break; case 'u': unmap_vol = 1; map_name = optarg; break; case 'v': printf("tcplay v%d.%d\n", MAJ_VER, MIN_VER); exit(EXIT_SUCCESS); /* NOT REACHED */ case 'w': fprintf(stderr, "WARNING: Using urandom as source of " "entropy for key material is a really bad idea.\n"); use_weak_keys = 1; break; case 'x': if (h_prf != NULL) usage(); if ((h_prf = check_prf_algo(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'y': if (h_cipher_chain != NULL) usage(); if ((h_cipher_chain = check_cipher_chain(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'z': use_secure_erase = 0; break; case FLAG_LONG_FDE: flags |= TC_FLAG_FDE; break; case FLAG_LONG_USE_BACKUP: flags |= TC_FLAG_BACKUP; break; case 'h': case '?': default: usage(); /* NOT REACHED */ } } argc -= optind; argv += optind; /* Check arguments */ if (!(((map_vol || info_vol || create_vol) && dev != NULL) || ((unmap_vol || info_map) && map_name != NULL)) || (TC_FLAG_SET(flags, SYS) && TC_FLAG_SET(flags, FDE)) || (map_vol && info_vol) || (map_vol && create_vol) || (unmap_vol && map_vol) || (unmap_vol && info_vol) || (unmap_vol && create_vol) || (create_vol && info_vol) || (contain_hidden && !create_vol) || (TC_FLAG_SET(flags, SYS) && (sys_dev == NULL)) || (map_vol && (map_name == NULL)) || (unmap_vol && (map_name == NULL)) || (!(protect_hidden || create_vol) && n_hkeyfiles > 0)) { usage(); /* NOT REACHED */ } /* Create a new volume */ if (create_vol) { error = create_volume(dev, contain_hidden, keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, prf, cipher_chain, h_prf, h_cipher_chain, NULL, NULL, 0, 1 /* interactive */, use_secure_erase, use_weak_keys); if (error) { tc_log(1, "could not create new volume on %s\n", dev); } } else if (info_map) { error = info_mapped_volume(map_name, 1 /* interactive */); } else if (info_vol) { error = info_volume(dev, flags, sys_dev, protect_hidden, keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, NULL, NULL, 1 /* interactive */, DEFAULT_RETRIES, 0); } else if (map_vol) { error = map_volume(map_name, dev, flags, sys_dev, protect_hidden, keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, NULL, NULL, 1 /* interactive */, DEFAULT_RETRIES, 0); } else if (unmap_vol) { error = dm_teardown(map_name, NULL); } return error; }
struct tcplay_info * info_map_common(struct tcplay_opts *opts, char *passphrase_out) { struct tchdr_enc *ehdr, *hehdr = NULL; struct tcplay_info *info, *hinfo = NULL; char *pass; char *h_pass; int error, error2 = 0; size_t sz; size_t blksz; disksz_t blocks; int is_hidden = 0; int try_empty = 0; int retries; if ((error = get_disk_info(opts->dev, &blocks, &blksz)) != 0) { tc_log(1, "could not get disk information\n"); return NULL; } if (opts->retries < 1) retries = 1; else retries = opts->retries; /* * Add one retry so we can do a first try without asking for * a password if keyfiles are passed in. */ if (opts->interactive && (opts->nkeyfiles > 0)) { try_empty = 1; ++retries; } info = NULL; ehdr = NULL; pass = h_pass = NULL; while ((info == NULL) && retries-- > 0) { pass = h_pass = NULL; ehdr = hehdr = NULL; info = hinfo = NULL; if ((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) { tc_log(1, "could not allocate safe passphrase memory\n"); goto out; } if (try_empty) { pass[0] = '\0'; } else if (opts->interactive) { if ((error = read_passphrase("Passphrase: ", pass, MAX_PASSSZ, PASS_BUFSZ, opts->timeout))) { tc_log(1, "could not read passphrase\n"); /* XXX: handle timeout differently? */ goto out; } pass[MAX_PASSSZ] = '\0'; } else { /* In batch mode, use provided passphrase */ if (opts->passphrase != NULL) { strncpy(pass, opts->passphrase, MAX_PASSSZ); pass[MAX_PASSSZ] = '\0'; } } if (passphrase_out != NULL) { strcpy(passphrase_out, pass); } if (opts->nkeyfiles > 0) { /* Apply keyfiles to 'pass' */ if ((error = apply_keyfiles((unsigned char *)pass, PASS_BUFSZ, opts->keyfiles, opts->nkeyfiles))) { tc_log(1, "could not apply keyfiles"); goto out; } } if (opts->protect_hidden) { if ((h_pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) { tc_log(1, "could not allocate safe passphrase memory\n"); goto out; } if (opts->interactive) { if ((error = read_passphrase( "Passphrase for hidden volume: ", h_pass, MAX_PASSSZ, PASS_BUFSZ, opts->timeout))) { tc_log(1, "could not read passphrase\n"); goto out; } h_pass[MAX_PASSSZ] = '\0'; } else { /* In batch mode, use provided passphrase */ if (opts->h_passphrase != NULL) { strncpy(h_pass, opts->h_passphrase, MAX_PASSSZ); h_pass[MAX_PASSSZ] = '\0'; } } if (opts->n_hkeyfiles > 0) { /* Apply keyfiles to 'pass' */ if ((error = apply_keyfiles((unsigned char *)h_pass, PASS_BUFSZ, opts->h_keyfiles, opts->n_hkeyfiles))) { tc_log(1, "could not apply keyfiles"); goto out; } } } /* Always read blksz-sized chunks */ sz = blksz; if (TC_FLAG_SET(opts->flags, HDR_FROM_FILE)) { ehdr = (struct tchdr_enc *)read_to_safe_mem( opts->hdr_file_in, 0, &sz); if (ehdr == NULL) { tc_log(1, "error read hdr_enc: %s", opts->hdr_file_in); goto out; } } else { ehdr = (struct tchdr_enc *)read_to_safe_mem( (TC_FLAG_SET(opts->flags, SYS)) ? opts->sys_dev : opts->dev, (TC_FLAG_SET(opts->flags, SYS) || TC_FLAG_SET(opts->flags, FDE)) ? HDR_OFFSET_SYS : (!TC_FLAG_SET(opts->flags, BACKUP)) ? 0 : -BACKUP_HDR_OFFSET_END, &sz); if (ehdr == NULL) { tc_log(1, "error read hdr_enc: %s", opts->dev); goto out; } } if (!TC_FLAG_SET(opts->flags, SYS)) { /* Always read blksz-sized chunks */ sz = blksz; if (TC_FLAG_SET(opts->flags, H_HDR_FROM_FILE)) { hehdr = (struct tchdr_enc *)read_to_safe_mem( opts->h_hdr_file_in, 0, &sz); if (hehdr == NULL) { tc_log(1, "error read hdr_enc: %s", opts->h_hdr_file_in); goto out; } } else { hehdr = (struct tchdr_enc *)read_to_safe_mem(opts->dev, (!TC_FLAG_SET(opts->flags, BACKUP)) ? HDR_OFFSET_HIDDEN : -BACKUP_HDR_HIDDEN_OFFSET_END, &sz); if (hehdr == NULL) { tc_log(1, "error read hdr_enc: %s", opts->dev); goto out; } } } else { hehdr = NULL; } error = process_hdr(opts->dev, opts->flags, (unsigned char *)pass, (opts->nkeyfiles > 0)?MAX_PASSSZ:strlen(pass), ehdr, &info); /* * Try to process hidden header if we have to protect the hidden * volume, or the decryption/verification of the main header * failed. */ if (hehdr && (error || opts->protect_hidden)) { if (error) { error2 = process_hdr(opts->dev, opts->flags, (unsigned char *)pass, (opts->nkeyfiles > 0)?MAX_PASSSZ:strlen(pass), hehdr, &info); is_hidden = !error2; } else if (opts->protect_hidden) { error2 = process_hdr(opts->dev, opts->flags, (unsigned char *)h_pass, (opts->n_hkeyfiles > 0)?MAX_PASSSZ:strlen(h_pass), hehdr, &hinfo); } } /* We need both to protect a hidden volume */ if ((opts->protect_hidden && (error || error2)) || (error && error2)) { if (!try_empty) tc_log(1, "Incorrect password or not a TrueCrypt volume\n"); if (info) { free_info(info); info = NULL; } if (hinfo) { free_info(hinfo); hinfo = NULL; } /* Try again (or finish) */ free_safe_mem(pass); pass = NULL; if (h_pass) { free_safe_mem(h_pass); h_pass = NULL; } if (ehdr) { free_safe_mem(ehdr); ehdr = NULL; } if (hehdr) { free_safe_mem(hehdr); hehdr = NULL; } try_empty = 0; continue; } if (opts->protect_hidden) { if (adjust_info(info, hinfo) != 0) { tc_log(1, "Could not protect hidden volume\n"); if (info) free_info(info); info = NULL; if (hinfo) free_info(hinfo); hinfo = NULL; goto out; } if (hinfo) { free_info(hinfo); hinfo = NULL; } } try_empty = 0; } out: if (hinfo) free_info(hinfo); if (pass) free_safe_mem(pass); if (h_pass) free_safe_mem(h_pass); if (ehdr) free_safe_mem(ehdr); if (hehdr) free_safe_mem(hehdr); if (info != NULL) info->hidden = is_hidden; return info; }
int modify_volume(struct tcplay_opts *opts) { struct tcplay_info *info; struct tchdr_enc *ehdr, *ehdr_backup; const char *new_passphrase = opts->new_passphrase; const char **new_keyfiles = opts->new_keyfiles; struct pbkdf_prf_algo *new_prf_algo = opts->new_prf_algo; int n_newkeyfiles = opts->n_newkeyfiles; char *pass, *pass_again; int ret = -1; off_t offset, offset_backup = 0; const char *dev; size_t blksz; disksz_t blocks; int error; ehdr = ehdr_backup = NULL; pass = pass_again = NULL; info = NULL; if (TC_FLAG_SET(opts->flags, ONLY_RESTORE)) { if (opts->interactive) { if ((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) { tc_log(1, "could not allocate safe " "passphrase memory"); goto out; } } else { new_passphrase = opts->passphrase; } new_keyfiles = opts->keyfiles; n_newkeyfiles = opts->nkeyfiles; new_prf_algo = NULL; } info = info_map_common(opts, pass); if (info == NULL) goto out; if (opts->interactive && !TC_FLAG_SET(opts->flags, ONLY_RESTORE)) { if (((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) || ((pass_again = alloc_safe_mem(PASS_BUFSZ)) == NULL)) { tc_log(1, "could not allocate safe passphrase memory\n"); goto out; } if ((error = read_passphrase("New passphrase: ", pass, MAX_PASSSZ, PASS_BUFSZ, 0) || (read_passphrase("Repeat passphrase: ", pass_again, MAX_PASSSZ, PASS_BUFSZ, 0)))) { tc_log(1, "could not read passphrase\n"); goto out; } if (strcmp(pass, pass_again) != 0) { tc_log(1, "Passphrases don't match\n"); goto out; } free_safe_mem(pass_again); pass_again = NULL; } else if (!opts->interactive) { /* In batch mode, use provided passphrase */ if ((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) { tc_log(1, "could not allocate safe " "passphrase memory"); goto out; } if (new_passphrase != NULL) { strncpy(pass, new_passphrase, MAX_PASSSZ); pass[MAX_PASSSZ] = '\0'; } } if (n_newkeyfiles > 0) { /* Apply keyfiles to 'pass' */ if ((error = apply_keyfiles((unsigned char *)pass, PASS_BUFSZ, new_keyfiles, n_newkeyfiles))) { tc_log(1, "could not apply keyfiles\n"); goto out; } } ehdr = copy_reencrypt_hdr((unsigned char *)pass, (opts->n_newkeyfiles > 0)?MAX_PASSSZ:strlen(pass), new_prf_algo, opts->weak_keys_and_salt, info, &ehdr_backup); if (ehdr == NULL) { tc_log(1, "Could not create header\n"); goto out; } dev = (TC_FLAG_SET(opts->flags, SYS)) ? opts->sys_dev : opts->dev; if (TC_FLAG_SET(opts->flags, SYS) || TC_FLAG_SET(opts->flags, FDE)) { /* SYS and FDE don't have backup headers (as far as I understand) */ if (info->hidden) { offset = HDR_OFFSET_HIDDEN; } else { offset = HDR_OFFSET_SYS; } } else { if (info->hidden) { offset = HDR_OFFSET_HIDDEN; offset_backup = -BACKUP_HDR_HIDDEN_OFFSET_END; } else { offset = 0; offset_backup = -BACKUP_HDR_OFFSET_END; } } if ((error = get_disk_info(dev, &blocks, &blksz)) != 0) { tc_log(1, "could not get disk information\n"); goto out; } tc_log(0, "Writing new volume headers to disk/file...\n"); if (TC_FLAG_SET(opts->flags, SAVE_TO_FILE)) { if ((error = write_to_file(opts->hdr_file_out, ehdr, sizeof(*ehdr))) != 0) { tc_log(1, "Could not write volume header to file\n"); goto out; } } else { if ((error = write_to_disk(dev, offset, blksz, ehdr, sizeof(*ehdr))) != 0) { tc_log(1, "Could not write volume header to device\n"); goto out; } if (!TC_FLAG_SET(opts->flags, SYS) && !TC_FLAG_SET(opts->flags, FDE)) { if ((error = write_to_disk(dev, offset_backup, blksz, ehdr_backup, sizeof(*ehdr_backup))) != 0) { tc_log(1, "Could not write backup volume header to device\n"); goto out; } } } /* Everything went ok */ tc_log(0, "All done!\n"); ret = 0; out: if (pass) free_safe_mem(pass); if (pass_again) free_safe_mem(pass_again); if (ehdr) free_safe_mem(ehdr); if (ehdr_backup) free_safe_mem(ehdr_backup); if (info) free_safe_mem(info); return ret; }
int main(int argc, char *argv[]) { struct tcplay_opts *opts; int ch, error; int info_vol = 0, map_vol = 0, unmap_vol = 0, info_map = 0, create_vol = 0, modify_vol = 0; if ((error = tc_play_init()) != 0) { fprintf(stderr, "Initialization failed, exiting."); exit(EXIT_FAILURE); } atexit(check_and_purge_safe_mem); signal(SIGUSR1, sig_handler); signal(SIGINFO, sig_handler); if ((opts = opts_init()) == NULL) { fprintf(stderr, "Initialization failed (opts), exiting."); exit(EXIT_FAILURE); } opts->interactive = 1; while ((ch = getopt_long(argc, argv, "a:b:cd:ef:ghij:k:m:s:tu:vwx:y:zC:", longopts, NULL)) != -1) { switch(ch) { case 'a': if (opts->prf_algo != NULL) usage(); if ((opts->prf_algo = check_prf_algo(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'b': if (opts->cipher_chain != NULL) usage(); if ((opts->cipher_chain = check_cipher_chain(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'c': create_vol = 1; break; case 'C': opts->custom_iterations = atoi(optarg); break; case 'd': _set_str_opt(dev); break; case 'e': opts->protect_hidden = 1; break; case 'f': if ((error = opts_add_keyfile_hidden(opts, optarg)) != 0) { fprintf(stderr, "Could not add keyfile: %s\n", optarg); exit(EXIT_FAILURE); } break; case 'g': opts->hidden = 1; break; case 'i': info_vol = 1; break; case 'j': info_map = 1; _set_str_opt(map_name); break; case 'k': if ((error = opts_add_keyfile(opts, optarg)) != 0) { fprintf(stderr, "Could not add keyfile: %s\n", optarg); exit(EXIT_FAILURE); } break; case 'm': map_vol = 1; _set_str_opt(map_name); break; case 's': opts->flags |= TC_FLAG_SYS; _set_str_opt(sys_dev); break; case 't': opts->flags |= TC_FLAG_ALLOW_TRIM; break; case 'u': unmap_vol = 1; _set_str_opt(map_name); break; case 'v': printf("tcplay v%d.%d\n", MAJ_VER, MIN_VER); exit(EXIT_SUCCESS); /* NOT REACHED */ case 'w': fprintf(stderr, "WARNING: Using urandom as source of " "entropy for key material is a really bad idea.\n"); opts->weak_keys_and_salt = 1; break; case 'x': if (opts->h_prf_algo != NULL) usage(); if ((opts->h_prf_algo = check_prf_algo(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'y': if (opts->h_cipher_chain != NULL) usage(); if ((opts->h_cipher_chain = check_cipher_chain(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'z': opts->secure_erase = 0; break; case FLAG_LONG_FDE: opts->flags |= TC_FLAG_FDE; break; case FLAG_LONG_USE_BACKUP: opts->flags |= TC_FLAG_BACKUP; break; case FLAG_LONG_USE_HDR_FILE: opts->flags |= TC_FLAG_HDR_FROM_FILE; _set_str_opt(hdr_file_in); break; case FLAG_LONG_USE_HHDR_FILE: opts->flags |= TC_FLAG_H_HDR_FROM_FILE; _set_str_opt(h_hdr_file_in); break; case FLAG_LONG_MOD: modify_vol = 1; break; case FLAG_LONG_MOD_KF: if ((error = opts_add_keyfile_new(opts, optarg)) != 0) { fprintf(stderr, "Could not add keyfile: %s\n", optarg); exit(EXIT_FAILURE); } break; case FLAG_LONG_MOD_PRF: if (opts->new_prf_algo != NULL) usage(); if ((opts->new_prf_algo = check_prf_algo(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case FLAG_LONG_MOD_NONE: opts->new_prf_algo = NULL; opts->flags |= TC_FLAG_ONLY_RESTORE; opts->flags |= TC_FLAG_BACKUP; break; case FLAG_LONG_MOD_TO_FILE: opts->flags |= TC_FLAG_SAVE_TO_FILE; _set_str_opt(hdr_file_out); break; case FLAG_LONG_NO_RETRIES: opts->retries = 1; break; case 'h': case '?': default: usage(); /* NOT REACHED */ } } argc -= optind; argv += optind; /* Check arguments */ if (!(((map_vol || info_vol || create_vol || modify_vol) && opts->dev != NULL) || ((unmap_vol || info_map) && opts->map_name != NULL)) || (TC_FLAG_SET(opts->flags, SYS) && TC_FLAG_SET(opts->flags, FDE)) || (map_vol + info_vol + create_vol + unmap_vol + info_map + modify_vol > 1) || (opts->hidden && !create_vol) || (TC_FLAG_SET(opts->flags, SYS) && (opts->sys_dev == NULL)) || (TC_FLAG_SET(opts->flags, ONLY_RESTORE) && (opts->n_newkeyfiles > 0 || opts->new_prf_algo != NULL)) || (TC_FLAG_SET(opts->flags, BACKUP) && (opts->sys_dev != NULL || TC_FLAG_SET(opts->flags, FDE))) || (map_vol && (opts->map_name == NULL)) || (unmap_vol && (opts->map_name == NULL)) || (!modify_vol && opts->n_newkeyfiles > 0) || (!modify_vol && opts->new_prf_algo != NULL) || (!modify_vol && TC_FLAG_SET(opts->flags, ONLY_RESTORE)) || (!modify_vol && TC_FLAG_SET(opts->flags, SAVE_TO_FILE)) || (!(opts->protect_hidden || create_vol) && opts->n_hkeyfiles > 0)) { usage(); /* NOT REACHED */ } /* Create a new volume */ if (create_vol) { error = create_volume(opts); if (error) { tc_log(1, "could not create new volume on %s\n", opts->dev); } } else if (info_map) { error = info_mapped_volume(opts); } else if (info_vol) { error = info_volume(opts); } else if (map_vol) { error = map_volume(opts); } else if (unmap_vol) { error = dm_teardown(opts->map_name, NULL); } else if (modify_vol) { error = modify_volume(opts); } return error; }