]> git.saurik.com Git - apple/libutil.git/blobdiff - wipefs.cpp
libutil-20.tar.gz
[apple/libutil.git] / wipefs.cpp
diff --git a/wipefs.cpp b/wipefs.cpp
new file mode 100644 (file)
index 0000000..d5efc05
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+//
+//     wipefs.cpp
+//
+
+#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)
+{
+       // we don't know what blocks future FS will use to recognize itself.  But we'd better be safe than sorry and write
+       // the first and last 2MB of the volume
+       off_t size = 2 * 1024 * 1024;
+       extMan->AddByteRangeExtent(0, size);
+       extMan->AddByteRangeExtent(extMan->totalBytes - size, size);
+}
+
+void
+AddExtentsForHFS(class ExtentManager *extMan)
+{
+       // first 1KB is boot block, last 512B is reserved
+       // the Volume Header (512B) is after 1KB and before the last 512B
+       extMan->AddByteRangeExtent(0, 1024 + 512);
+       extMan->AddByteRangeExtent(extMan->totalBytes - 1024, 1024);
+}
+
+void
+AddExtentsForMSDOS(class ExtentManager *extMan)
+{
+       // MSDOS needs the first block (in theory, up to 32KB)
+       extMan->AddByteRangeExtent(0, 32 * 1024);
+}
+
+void
+AddExtentsForNTFS(class ExtentManager *extMan)
+{
+       // NTFS supports block size from 256B to 32768B.  The first, middle and last block are needed
+       extMan->AddByteRangeExtent(0, 32 * 1024);
+       extMan->AddByteRangeExtent(extMan->totalBytes - 32 * 1024, 32 * 1024);
+       // to be safe, add the rage from (mid_point - 32KB) to (mid_point + 32KB)
+       extMan->AddByteRangeExtent(extMan->totalBytes / 2 - 32 * 1024, 64 * 1024);
+}
+
+void
+AddExtentsForUDF(class ExtentManager *extMan)
+{
+       off_t lastBlockAddr = extMan->totalBlocks - 1;
+
+       // Volume Recognization Sequence (VRS) starts at 32KB, usually less than 7 Volume Structure Descriptors (2KB each)
+       extMan->AddByteRangeExtent(32 * 1024, 14 * 1024);
+
+       // AVDP is on 256, 512, last block, last block - 256
+       extMan->AddBlockRangeExtent(256, 1);
+       extMan->AddBlockRangeExtent(512, 1);
+       extMan->AddBlockRangeExtent(lastBlockAddr, 1);
+       extMan->AddBlockRangeExtent(lastBlockAddr - 256, 1);
+
+       // to be safe, assume the device has 2KB block size and do it again
+       if (extMan->blockSize != 2048) {
+               off_t blockSize = 2048;
+               // AVDP is on 256, 512, last block, last block - 256
+               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)
+{
+       // UFS super block is 8KB at offset 8KB
+       extMan->AddByteRangeExtent(8192, 8192);
+}
+
+void
+AddExtentsForZFS(class ExtentManager *extMan)
+{
+       // ZFS needs the first 512KB and last 512KB for all the 4 disk labels
+       extMan->AddByteRangeExtent(0, 512 * 1024);
+       extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 1024, 512 * 1024);
+}
+
+void
+AddExtentsForPartitions(class ExtentManager *extMan)
+{
+       // MBR (Master Boot Record) needs the first sector
+       // APM (Apple Partition Map) needs the second sector
+       // GPT (GUID Partition Table) needs the first 34 and last 33 sectors
+       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 (...) { // currently only ENOMEM is possible
+               err = ENOMEM;
+       }
+
+  labelExit:
+       if (err != 0) {
+               wipefs_free(handle);
+       }
+       return err;
+} // wipefs_alloc
+
+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 (...) { // currently only ENOMEM is possible
+               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_discard_t discard;
+
+       memset(&discard, 0, sizeof(dk_discard_t));
+       discard.length = handle->extMan.totalBytes;
+
+       //
+       // Don't bother to check the return value since this is mostly
+       // informational for the lower-level drivers.
+       //
+       ioctl(handle->fd, DKIOCDISCARD, (caddr_t)&discard);
+       
+
+       bufSize = 256 * 1024; // issue large I/O to get better performance
+       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;
+       // write zero to all extents
+       for (curExt = handle->extMan.extentList.begin(); curExt != handle->extMan.extentList.end(); curExt++) {
+               byteOffset = curExt->blockAddr * blockSize;
+               numBytes = curExt->numBlocks * blockSize;
+               // make both offset and numBytes on native block boundary
+               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;
+} // wipefs_wipe
+
+extern "C" void
+wipefs_free(wipefs_ctx *handle)
+{
+       if (*handle != NULL) {
+               delete *handle;
+               *handle = NULL;
+       }
+}