#include <fcntl.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <sys/disk.h>
#include <sys/stat.h>
#include "ExtentManager.h"
#include "wipefs.h"
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
struct __wipefs_ctx {
int fd;
class ExtentManager extMan;
};
void
AddExtentsForFutureFS(class ExtentManager *extMan)
{
off_t size = 2 * 1024 * 1024;
extMan->AddByteRangeExtent(0, size);
extMan->AddByteRangeExtent(extMan->totalBytes - size, size);
}
void
AddExtentsForHFS(class ExtentManager *extMan)
{
extMan->AddByteRangeExtent(0, 1024 + 512);
extMan->AddByteRangeExtent(extMan->totalBytes - 1024, 1024);
}
void
AddExtentsForMSDOS(class ExtentManager *extMan)
{
extMan->AddByteRangeExtent(0, 32 * 1024);
}
void
AddExtentsForNTFS(class ExtentManager *extMan)
{
extMan->AddByteRangeExtent(0, 32 * 1024);
extMan->AddByteRangeExtent(extMan->totalBytes - 32 * 1024, 32 * 1024);
extMan->AddByteRangeExtent(extMan->totalBytes / 2 - 32 * 1024, 64 * 1024);
}
void
AddExtentsForUDF(class ExtentManager *extMan)
{
off_t lastBlockAddr = extMan->totalBlocks - 1;
extMan->AddByteRangeExtent(32 * 1024, 14 * 1024);
extMan->AddBlockRangeExtent(256, 1);
extMan->AddBlockRangeExtent(512, 1);
extMan->AddBlockRangeExtent(lastBlockAddr, 1);
extMan->AddBlockRangeExtent(lastBlockAddr - 256, 1);
if (extMan->blockSize != 2048) {
off_t blockSize = 2048;
extMan->AddByteRangeExtent(256 * blockSize, blockSize);
extMan->AddByteRangeExtent(512 * blockSize, blockSize);
extMan->AddByteRangeExtent(extMan->totalBytes - blockSize, blockSize);
extMan->AddByteRangeExtent(extMan->totalBytes - 256 * blockSize, blockSize);
}
}
void
AddExtentsForUFS(class ExtentManager *extMan)
{
extMan->AddByteRangeExtent(8192, 8192);
}
void
AddExtentsForZFS(class ExtentManager *extMan)
{
extMan->AddByteRangeExtent(0, 512 * 1024);
extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 1024, 512 * 1024);
}
void
AddExtentsForPartitions(class ExtentManager *extMan)
{
extMan->AddByteRangeExtent(0, 512 * 34);
extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 33, 512 * 33);
}
extern "C" int
wipefs_alloc(int fd, size_t block_size, wipefs_ctx *handle)
{
int err = 0;
uint64_t numBlocks = 0;
uint32_t nativeBlockSize = 0;
off_t totalSizeInBytes = 0;
class ExtentManager *extMan = NULL;
struct stat sbuf = { 0 };
*handle = NULL;
(void)fstat(fd, &sbuf);
switch (sbuf.st_mode & S_IFMT) {
case S_IFCHR:
case S_IFBLK:
if (ioctl(fd, DKIOCGETBLOCKSIZE, (char *)&nativeBlockSize) < 0) {
err = errno;
goto labelExit;
}
if (ioctl(fd, DKIOCGETBLOCKCOUNT, (char *)&numBlocks) < 0) {
err = errno;
goto labelExit;
}
totalSizeInBytes = numBlocks * nativeBlockSize;
break;
case S_IFREG:
nativeBlockSize = sbuf.st_blksize;
numBlocks = sbuf.st_size / sbuf.st_blksize;
totalSizeInBytes = sbuf.st_size;
break;
default:
errno = EINVAL;
goto labelExit;
}
if (block_size == 0) {
block_size = nativeBlockSize;
}
if (block_size == 0 || totalSizeInBytes == 0) {
err = EINVAL;
goto labelExit;
}
try {
*handle = new __wipefs_ctx;
if (*handle == NULL) {
bad_alloc e;
throw e;
}
(*handle)->fd = fd;
extMan = &(*handle)->extMan;
extMan->Init(block_size, nativeBlockSize, totalSizeInBytes);
AddExtentsForFutureFS(extMan);
AddExtentsForHFS(extMan);
AddExtentsForMSDOS(extMan);
AddExtentsForNTFS(extMan);
AddExtentsForUDF(extMan);
AddExtentsForUFS(extMan);
AddExtentsForZFS(extMan);
AddExtentsForPartitions(extMan);
}
catch (bad_alloc &e) {
err = ENOMEM;
}
catch (...) { err = ENOMEM;
}
labelExit:
if (err != 0) {
wipefs_free(handle);
}
return err;
}
extern "C" int
wipefs_except_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks)
{
int err = 0;
try {
handle->extMan.RemoveBlockRangeExtent(block_offset, nblocks);
}
catch (bad_alloc &e) {
err = ENOMEM;
}
catch (...) { err = ENOMEM;
}
return err;
}
extern "C" int
wipefs_wipe(wipefs_ctx handle)
{
int err = 0;
uint8_t *bufZero = NULL;
ListExtIt curExt;
size_t bufSize;
dk_extent_t extent;
dk_unmap_t unmap;
memset(&extent, 0, sizeof(dk_extent_t));
extent.length = handle->extMan.totalBytes;
memset(&unmap, 0, sizeof(dk_unmap_t));
unmap.extents = &extent;
unmap.extentsCount = 1;
ioctl(handle->fd, DKIOCUNMAP, (caddr_t)&unmap);
bufSize = 256 * 1024; bufZero = new uint8_t[bufSize];
bzero(bufZero, bufSize);
off_t byteOffset, totalBytes;
size_t numBytes, numBytesToWrite, blockSize;
blockSize = handle->extMan.blockSize;
totalBytes = handle->extMan.totalBytes;
for (curExt = handle->extMan.extentList.begin(); curExt != handle->extMan.extentList.end(); curExt++) {
byteOffset = curExt->blockAddr * blockSize;
numBytes = curExt->numBlocks * blockSize;
if (byteOffset % handle->extMan.nativeBlockSize != 0 ||
numBytes % handle->extMan.nativeBlockSize != 0) {
size_t nativeBlockSize = handle->extMan.nativeBlockSize;
off_t newOffset, newEndOffset;
newOffset = byteOffset / nativeBlockSize * nativeBlockSize;
newEndOffset = roundup(byteOffset + numBytes, nativeBlockSize);
byteOffset = newOffset;
numBytes = newEndOffset - newOffset;
}
if (byteOffset + (off_t)numBytes > totalBytes) {
numBytes = totalBytes - byteOffset;
}
while (numBytes > 0) {
numBytesToWrite = min(numBytes, bufSize);
if (pwrite(handle->fd, bufZero, numBytesToWrite, byteOffset) != (ssize_t)numBytesToWrite) {
err = errno;
goto labelExit;
}
numBytes -= numBytesToWrite;
byteOffset += numBytesToWrite;
}
}
labelExit:
if (bufZero != NULL)
delete[] bufZero;
return err;
}
extern "C" void
wipefs_free(wipefs_ctx *handle)
{
if (*handle != NULL) {
delete *handle;
*handle = NULL;
}
}