static int _crypt_format_luks2(struct crypt_device *cd,
			       const char *cipher,
			       const char *cipher_mode,
			       const char *uuid,
			       const char *volume_key,
			       size_t volume_key_size,
			       struct crypt_params_luks2 *params,
			       bool sector_size_autodetect)
{
	int r, integrity_key_size = 0;
	unsigned long required_alignment = DEFAULT_DISK_ALIGNMENT;
	unsigned long alignment_offset = 0;
	unsigned int sector_size;
	const char *integrity = params ? params->integrity : NULL;
	uint64_t dev_size;
	uint32_t dmc_flags;

	cd->u.luks2.hdr.jobj = NULL;
	cd->u.luks2.keyslot_cipher = NULL;

	if (!cipher || !cipher_mode)
		return -EINVAL;

	if (!crypt_metadata_device(cd)) {
		log_err(cd, _("Can't format LUKS without device."));
		return -EINVAL;
	}

	if (params && cd->data_offset && params->data_alignment &&
	   (cd->data_offset % params->data_alignment)) {
		log_err(cd, _("Requested data alignment is not compatible with data offset."));
		return -EINVAL;
	}

	if (params && params->sector_size)
		sector_size_autodetect = false;

	if (sector_size_autodetect) {
		sector_size = device_optimal_encryption_sector_size(cd, crypt_data_device(cd));
		log_dbg(cd, "Auto-detected optimal encryption sector size for device %s is %d bytes.",
			device_path(crypt_data_device(cd)), sector_size);
	} else
		sector_size = params ? params->sector_size : SECTOR_SIZE;

	if (sector_size < SECTOR_SIZE || sector_size > MAX_SECTOR_SIZE ||
	    NOTPOW2(sector_size)) {
		log_err(cd, _("Unsupported encryption sector size."));
		return -EINVAL;
	}
	if (sector_size != SECTOR_SIZE && !dm_flags(cd, DM_CRYPT, &dmc_flags) &&
	    !(dmc_flags & DM_SECTOR_SIZE_SUPPORTED)) {
		if (sector_size_autodetect) {
			log_dbg(cd, "dm-crypt does not support encryption sector size option. Reverting to 512 bytes.");
			sector_size = SECTOR_SIZE;
		} else
			log_std(cd, _("WARNING: The device activation will fail, dm-crypt is missing "
				      "support for requested encryption sector size.\n"));
	}

	if (integrity) {
		if (params->integrity_params) {
			/* Standalone dm-integrity must not be used */
			if (params->integrity_params->integrity ||
			    params->integrity_params->integrity_key_size)
				return -EINVAL;
			/* FIXME: journal encryption and MAC is here not yet supported */
			if (params->integrity_params->journal_crypt ||
			params->integrity_params->journal_integrity)
				return -ENOTSUP;
		}
		if (!INTEGRITY_tag_size(cd, integrity, cipher, cipher_mode)) {
			if (!strcmp(integrity, "none"))
				integrity = NULL;
			else
				return -EINVAL;
		}
		integrity_key_size = INTEGRITY_key_size(cd, integrity);
		if ((integrity_key_size < 0) || (integrity_key_size >= (int)volume_key_size)) {
			log_err(cd, _("Volume key is too small for encryption with integrity extensions."));
			return -EINVAL;
		}
	}

	r = device_check_access(cd, crypt_metadata_device(cd), DEV_EXCL);
	if (r < 0)
		return r;

	if (!(cd->type = strdup(CRYPT_LUKS2)))
		return -ENOMEM;

	if (volume_key)
		cd->volume_key = crypt_alloc_volume_key(volume_key_size,
						      volume_key);
	else
		cd->volume_key = crypt_generate_volume_key(cd, volume_key_size);

	if (!cd->volume_key)
		return -ENOMEM;

	if (params && params->pbkdf)
		r = crypt_set_pbkdf_type(cd, params->pbkdf);
	else if (verify_pbkdf_params(cd, &cd->pbkdf))
		r = init_pbkdf_type(cd, NULL, CRYPT_LUKS2);

	if (r < 0)
		return r;

	if (params && params->data_device) {
		if (!cd->metadata_device)
			cd->metadata_device = cd->device;
		else
			device_free(cd, cd->device);
		cd->device = NULL;
		if (device_alloc(cd, &cd->device, params->data_device) < 0)
			return -ENOMEM;
	}

	if (params && cd->metadata_device) {
		/* For detached header the alignment is used directly as data offset */
		if (!cd->data_offset)
			cd->data_offset = params->data_alignment;
		required_alignment = params->data_alignment * SECTOR_SIZE;
	} else if (params && params->data_alignment) {
		required_alignment = params->data_alignment * SECTOR_SIZE;
	} else
		device_topology_alignment(cd, cd->device,
				       &required_alignment,
				       &alignment_offset, DEFAULT_DISK_ALIGNMENT);

	r = device_size(crypt_data_device(cd), &dev_size);
	if (r < 0)
		goto out;

	if (sector_size_autodetect) {
		if (cd->data_offset && MISALIGNED(cd->data_offset, sector_size)) {
			log_dbg(cd, "Data offset not aligned to sector size. Reverting to 512 bytes.");
			sector_size = SECTOR_SIZE;
		} else if (MISALIGNED(dev_size - (uint64_t)required_alignment - (uint64_t)alignment_offset, sector_size)) {
			/* underflow does not affect misalignment checks */
			log_dbg(cd, "Device size is not aligned to sector size. Reverting to 512 bytes.");
			sector_size = SECTOR_SIZE;
		}
	}

	/* FIXME: allow this later also for normal ciphers (check AF_ALG availability. */
	if (integrity && !integrity_key_size) {
		r = crypt_cipher_check_kernel(cipher, cipher_mode, integrity, volume_key_size);
		if (r < 0) {
			log_err(cd, _("Cipher %s-%s (key size %zd bits) is not available."),
				cipher, cipher_mode, volume_key_size * 8);
			goto out;
		}
	}

	if ((!integrity || integrity_key_size) && !crypt_cipher_wrapped_key(cipher, cipher_mode) &&
	    !INTEGRITY_tag_size(cd, NULL, cipher, cipher_mode)) {
		r = LUKS_check_cipher(cd, volume_key_size - integrity_key_size,
				      cipher, cipher_mode);
		if (r < 0)
			goto out;
	}

	r = LUKS2_generate_hdr(cd, &cd->u.luks2.hdr, cd->volume_key,
			       cipher, cipher_mode,
			       integrity, uuid,
			       sector_size,
			       cd->data_offset * SECTOR_SIZE,
			       alignment_offset,
			       required_alignment,
			       cd->metadata_size, cd->keyslots_size);
	if (r < 0)
		goto out;

	if (dev_size < (crypt_get_data_offset(cd) * SECTOR_SIZE))
		log_std(cd, _("WARNING: Data offset is outside of currently available data device.\n"));

	if (cd->metadata_size && (cd->metadata_size != LUKS2_metadata_size(&cd->u.luks2.hdr)))
		log_std(cd, _("WARNING: LUKS2 metadata size changed to %" PRIu64 " bytes.\n"),
			LUKS2_metadata_size(&cd->u.luks2.hdr));

	if (cd->keyslots_size && (cd->keyslots_size != LUKS2_keyslots_size(&cd->u.luks2.hdr)))
		log_std(cd, _("WARNING: LUKS2 keyslots area size changed to %" PRIu64 " bytes.\n"),
			LUKS2_keyslots_size(&cd->u.luks2.hdr));

	if (!integrity && sector_size > SECTOR_SIZE) {
		dev_size -= (crypt_get_data_offset(cd) * SECTOR_SIZE);
		if (dev_size % sector_size) {
			log_err(cd, _("Device size is not aligned to requested sector size."));
			r = -EINVAL;
			goto out;
		}
	}

	if (params && (params->label || params->subsystem)) {
		r = LUKS2_hdr_labels(cd, &cd->u.luks2.hdr,
				     params->label, params->subsystem, 0);
		if (r < 0)
			goto out;
	}

	device_set_block_size(crypt_data_device(cd), sector_size);

	r = LUKS2_wipe_header_areas(cd, &cd->u.luks2.hdr, cd->metadata_device != NULL);
	if (r < 0) {
		log_err(cd, _("Cannot wipe header on device %s."),
			mdata_device_path(cd));
		if (dev_size < LUKS2_hdr_and_areas_size(&cd->u.luks2.hdr))
			log_err(cd, _("Device %s is too small."), device_path(crypt_metadata_device(cd)));
		goto out;
	}

	/* Wipe integrity superblock and create integrity superblock */
	if (crypt_get_integrity_tag_size(cd)) {
		r = crypt_wipe_device(cd, crypt_data_device(cd), CRYPT_WIPE_ZERO,
				      crypt_get_data_offset(cd) * SECTOR_SIZE,
				      8 * SECTOR_SIZE, 8 * SECTOR_SIZE, NULL, NULL);
		if (r < 0) {
			if (r == -EBUSY)
				log_err(cd, _("Cannot format device %s in use."),
					data_device_path(cd));
			else if (r == -EACCES) {
				log_err(cd, _("Cannot format device %s, permission denied."),
					data_device_path(cd));
				r = -EINVAL;
			} else
				log_err(cd, _("Cannot wipe header on device %s."),
					data_device_path(cd));

			goto out;
		}

		r = INTEGRITY_format(cd, params ? params->integrity_params : NULL, NULL, NULL);
		if (r)
			log_err(cd, _("Cannot format integrity for device %s."),
				data_device_path(cd));
	}

	if (r < 0)
		goto out;

	/* override sequence id check with format */
	r = LUKS2_hdr_write_force(cd, &cd->u.luks2.hdr);
	if (r < 0) {
		if (r == -EBUSY)
			log_err(cd, _("Cannot format device %s in use."),
				mdata_device_path(cd));
		else if (r == -EACCES) {
			log_err(cd, _("Cannot format device %s, permission denied."),
				mdata_device_path(cd));
			r = -EINVAL;
		} else
			log_err(cd, _("Cannot format device %s."),
				mdata_device_path(cd));
	}

out:
	if (r)
		LUKS2_hdr_free(cd, &cd->u.luks2.hdr);

	return r;
}