Add ZEROOUT ioctl support for crypt_wipe.

For block devices we can use in-kernel BLKZEROOUT ioctl,
that should be faster in most cases.

This cannot be used for images in files.
This commit is contained in:
Milan Broz
2022-04-15 12:58:35 +02:00
parent 41d61df667
commit 0009089855

View File

@@ -22,8 +22,36 @@
#include <stdlib.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/fs.h>
#include "internal.h"
/* block device zeroout ioctls, introduced in Linux kernel 3.7 */
#ifndef BLKZEROOUT
#define BLKZEROOUT _IO(0x12,127)
#endif
static int wipe_zeroout(struct crypt_device *cd, int devfd,
uint64_t offset, uint64_t length)
{
static bool zeroout_available = true;
uint64_t range[2] = { offset, length };
int r;
if (!zeroout_available)
return -ENOTSUP;
r = ioctl(devfd, BLKZEROOUT, &range);
if (r < 0) {
log_dbg(cd, "BLKZEROOUT ioctl not available (error %i), disabling.", r);
zeroout_available = false;
return -ENOTSUP;
}
return 0;
}
/*
* Wipe using Peter Gutmann method described in
* https://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html
@@ -93,7 +121,8 @@ static int crypt_wipe_special(struct crypt_device *cd, int fd, size_t bsize,
static int wipe_block(struct crypt_device *cd, int devfd, crypt_wipe_pattern pattern,
char *sf, size_t device_block_size, size_t alignment,
size_t wipe_block_size, uint64_t offset, bool *need_block_init)
size_t wipe_block_size, uint64_t offset, bool *need_block_init,
bool blockdev)
{
int r;
@@ -122,6 +151,16 @@ static int wipe_block(struct crypt_device *cd, int devfd, crypt_wipe_pattern pat
return r;
}
if (blockdev && pattern == CRYPT_WIPE_ZERO &&
!wipe_zeroout(cd, devfd, offset, wipe_block_size)) {
/* zeroout ioctl does not move offset */
if (lseek64(devfd, offset + wipe_block_size, SEEK_SET) < 0) {
log_err(cd, _("Cannot seek to device offset."));
return -EINVAL;
}
return 0;
}
if (write_blockwise(devfd, device_block_size, alignment, sf,
wipe_block_size) == (ssize_t)wipe_block_size)
return 0;
@@ -139,6 +178,7 @@ int crypt_wipe_device(struct crypt_device *cd,
void *usrptr)
{
int r, devfd;
struct stat st;
size_t bsize, alignment;
char *sf = NULL;
uint64_t dev_size;
@@ -163,6 +203,11 @@ int crypt_wipe_device(struct crypt_device *cd,
if (devfd < 0)
return errno ? -errno : -EINVAL;
if (fstat(devfd, &st) < 0) {
r = -EINVAL;
goto out;
}
if (length)
dev_size = offset + length;
else {
@@ -200,10 +245,8 @@ int crypt_wipe_device(struct crypt_device *cd,
if ((offset + wipe_block_size) > dev_size)
wipe_block_size = dev_size - offset;
//log_dbg("Wipe %012" PRIu64 "-%012" PRIu64 " bytes", offset, offset + wipe_block_size);
r = wipe_block(cd, devfd, pattern, sf, bsize, alignment,
wipe_block_size, offset, &need_block_init);
wipe_block_size, offset, &need_block_init, S_ISBLK(st.st_mode));
if (r) {
log_err(cd,_("Device wipe error, offset %" PRIu64 "."), offset);
break;