+ strncpy(c, *p, arg_key_size);
+ free(*p);
+ *p = c;
+ }
+
+ return 0;
+}
+
+static int attach_tcrypt(struct crypt_device *cd,
+ const char *name,
+ const char *key_file,
+ char **passwords,
+ uint32_t flags) {
+ int r = 0;
+ _cleanup_free_ char *passphrase = NULL;
+ struct crypt_params_tcrypt params = {
+ .flags = CRYPT_TCRYPT_LEGACY_MODES,
+ .keyfiles = (const char **)arg_tcrypt_keyfiles,
+ .keyfiles_count = strv_length(arg_tcrypt_keyfiles)
+ };
+
+ assert(cd);
+ assert(name);
+ assert(key_file || passwords);
+
+ if (arg_tcrypt_hidden)
+ params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
+
+ if (arg_tcrypt_system)
+ params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
+
+ if (key_file) {
+ r = read_one_line_file(key_file, &passphrase);
+ if (r < 0) {
+ log_error("Failed to read password file '%s': %s", key_file, strerror(-r));
+ return -EAGAIN;
+ }
+
+ params.passphrase = passphrase;
+ } else
+ params.passphrase = passwords[0];
+ params.passphrase_size = strlen(params.passphrase);
+
+ r = crypt_load(cd, CRYPT_TCRYPT, ¶ms);
+ if (r < 0) {
+ if (key_file && r == -EPERM) {
+ log_error("Failed to activate using password file '%s'.", key_file);
+ return -EAGAIN;
+ }
+ return r;
+ }
+
+ return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
+}
+
+static int attach_luks_or_plain(struct crypt_device *cd,
+ const char *name,
+ const char *key_file,
+ char **passwords,
+ uint32_t flags) {
+ int r = 0;
+ bool pass_volume_key = false;
+
+ assert(cd);
+ assert(name);
+ assert(key_file || passwords);
+
+ if (!arg_type || streq(arg_type, CRYPT_LUKS1))
+ r = crypt_load(cd, CRYPT_LUKS1, NULL);
+
+ if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) {
+ struct crypt_params_plain params = {};
+ const char *cipher, *cipher_mode;
+ _cleanup_free_ char *truncated_cipher = NULL;
+
+ if (arg_hash) {
+ /* plain isn't a real hash type. it just means "use no hash" */
+ if (!streq(arg_hash, "plain"))
+ params.hash = arg_hash;
+ } else
+ params.hash = "ripemd160";
+
+ if (arg_cipher) {
+ size_t l;
+
+ l = strcspn(arg_cipher, "-");
+ truncated_cipher = strndup(arg_cipher, l);
+ if (!truncated_cipher)
+ return log_oom();
+
+ cipher = truncated_cipher;
+ cipher_mode = arg_cipher[l] ? arg_cipher+l+1 : "plain";
+ } else {
+ cipher = "aes";
+ cipher_mode = "cbc-essiv:sha256";
+ }
+
+ /* for CRYPT_PLAIN limit reads
+ * from keyfile to key length, and
+ * ignore keyfile-size */
+ arg_keyfile_size = arg_key_size / 8;
+
+ /* In contrast to what the name
+ * crypt_setup() might suggest this
+ * doesn't actually format anything,
+ * it just configures encryption
+ * parameters when used for plain
+ * mode. */
+ r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
+ NULL, NULL, arg_keyfile_size, ¶ms);
+
+ /* hash == NULL implies the user passed "plain" */
+ pass_volume_key = (params.hash == NULL);
+ }
+
+ if (r < 0) {
+ log_error("Loading of cryptographic parameters failed: %s", strerror(-r));
+ return r;
+ }
+
+ log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
+ crypt_get_cipher(cd),
+ crypt_get_cipher_mode(cd),
+ crypt_get_volume_key_size(cd)*8,
+ crypt_get_device_name(cd));
+
+ if (key_file) {
+ r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot,
+ key_file, arg_keyfile_size,
+ arg_keyfile_offset, flags);
+ if (r < 0) {
+ log_error("Failed to activate with key file '%s': %s", key_file, strerror(-r));
+ return -EAGAIN;
+ }
+ } else {
+ char **p;
+
+ STRV_FOREACH(p, passwords) {
+ if (pass_volume_key)
+ r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
+ else
+ r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
+
+ if (r >= 0)
+ break;
+ }
+ }
+
+ return r;