1/* 2 * Copyright (c) 2008 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23// 24// wipefs.cpp 25// 26 27#include <fcntl.h> 28#include <unistd.h> 29#include <sys/uio.h> 30#include <sys/ioctl.h> 31#include <sys/disk.h> 32#include <sys/stat.h> 33 34#include "ExtentManager.h" 35#include "wipefs.h" 36 37#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) 38 39struct __wipefs_ctx { 40 int fd; 41 class ExtentManager extMan; 42}; 43 44static void 45AddExtentsForFutureFS(class ExtentManager *extMan) 46{ 47 // we don't know what blocks future FS will use to recognize itself. But we'd better be safe than sorry and write 48 // the first and last 2MB of the volume 49 off_t size = 2 * 1024 * 1024; 50 extMan->AddByteRangeExtent(0, size); 51 extMan->AddByteRangeExtent(extMan->totalBytes - size, size); 52} 53 54static void 55AddExtentsForHFS(class ExtentManager *extMan) 56{ 57 // first 1KB is boot block, last 512B is reserved 58 // the Volume Header (512B) is after 1KB and before the last 512B 59 extMan->AddByteRangeExtent(0, 1024 + 512); 60 extMan->AddByteRangeExtent(extMan->totalBytes - 1024, 1024); 61} 62 63static void 64AddExtentsForMSDOS(class ExtentManager *extMan) 65{ 66 // MSDOS needs the first block (in theory, up to 32KB) 67 extMan->AddByteRangeExtent(0, 32 * 1024); 68} 69 70static void 71AddExtentsForNTFS(class ExtentManager *extMan) 72{ 73 // NTFS supports block size from 256B to 32768B. The first, middle and last block are needed 74 extMan->AddByteRangeExtent(0, 32 * 1024); 75 extMan->AddByteRangeExtent(extMan->totalBytes - 32 * 1024, 32 * 1024); 76 // to be safe, add the rage from (mid_point - 32KB) to (mid_point + 32KB) 77 extMan->AddByteRangeExtent(extMan->totalBytes / 2 - 32 * 1024, 64 * 1024); 78} 79 80static void 81AddExtentsForUDF(class ExtentManager *extMan) 82{ 83 off_t lastBlockAddr = extMan->totalBlocks - 1; 84 85 // Volume Recognization Sequence (VRS) starts at 32KB, usually less than 7 Volume Structure Descriptors (2KB each) 86 extMan->AddByteRangeExtent(32 * 1024, 14 * 1024); 87 88 // AVDP is on 256, 512, last block, last block - 256 89 extMan->AddBlockRangeExtent(256, 1); 90 extMan->AddBlockRangeExtent(512, 1); 91 extMan->AddBlockRangeExtent(lastBlockAddr, 1); 92 extMan->AddBlockRangeExtent(lastBlockAddr - 256, 1); 93 94 // to be safe, assume the device has 2KB block size and do it again 95 if (extMan->blockSize != 2048) { 96 off_t blockSize = 2048; 97 // AVDP is on 256, 512, last block, last block - 256 98 extMan->AddByteRangeExtent(256 * blockSize, blockSize); 99 extMan->AddByteRangeExtent(512 * blockSize, blockSize); 100 extMan->AddByteRangeExtent(extMan->totalBytes - blockSize, blockSize); 101 extMan->AddByteRangeExtent(extMan->totalBytes - 256 * blockSize, blockSize); 102 } 103} 104 105static void 106AddExtentsForUFS(class ExtentManager *extMan) 107{ 108 // UFS super block is 8KB at offset 8KB 109 extMan->AddByteRangeExtent(8192, 8192); 110} 111 112static void 113AddExtentsForZFS(class ExtentManager *extMan) 114{ 115 // ZFS needs the first 512KB and last 512KB for all the 4 disk labels 116 extMan->AddByteRangeExtent(0, 512 * 1024); 117 extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 1024, 512 * 1024); 118} 119 120static void 121AddExtentsForPartitions(class ExtentManager *extMan) 122{ 123 // MBR (Master Boot Record) needs the first sector 124 // APM (Apple Partition Map) needs the second sector 125 // GPT (GUID Partition Table) needs the first 34 and last 33 sectors 126 extMan->AddByteRangeExtent(0, 512 * 34); 127 extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 33, 512 * 33); 128} 129 130extern "C" int 131wipefs_alloc(int fd, size_t block_size, wipefs_ctx *handle) 132{ 133 int err = 0; 134 uint64_t numBlocks = 0; 135 uint32_t nativeBlockSize = 0; 136 off_t totalSizeInBytes = 0; 137 class ExtentManager *extMan = NULL; 138 struct stat sbuf = { 0 }; 139 140 *handle = NULL; 141 (void)fstat(fd, &sbuf); 142 switch (sbuf.st_mode & S_IFMT) { 143 case S_IFCHR: 144 case S_IFBLK: 145 if (ioctl(fd, DKIOCGETBLOCKSIZE, (char *)&nativeBlockSize) < 0) { 146 err = errno; 147 goto labelExit; 148 } 149 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (char *)&numBlocks) < 0) { 150 err = errno; 151 goto labelExit; 152 } 153 totalSizeInBytes = numBlocks * nativeBlockSize; 154 break; 155 case S_IFREG: 156 nativeBlockSize = sbuf.st_blksize; 157 numBlocks = sbuf.st_size / sbuf.st_blksize; 158 totalSizeInBytes = sbuf.st_size; 159 break; 160 default: 161 errno = EINVAL; 162 goto labelExit; 163 } 164 if (block_size == 0) { 165 block_size = nativeBlockSize; 166 } 167 if (block_size == 0 || totalSizeInBytes == 0) { 168 err = EINVAL; 169 goto labelExit; 170 } 171 172 try { 173 *handle = new __wipefs_ctx; 174 if (*handle == NULL) { 175 bad_alloc e; 176 throw e; 177 } 178 179 (*handle)->fd = fd; 180 extMan = &(*handle)->extMan; 181 182 extMan->Init(block_size, nativeBlockSize, totalSizeInBytes); 183 AddExtentsForFutureFS(extMan); 184 AddExtentsForHFS(extMan); 185 AddExtentsForMSDOS(extMan); 186 AddExtentsForNTFS(extMan); 187 AddExtentsForUDF(extMan); 188 AddExtentsForUFS(extMan); 189 AddExtentsForZFS(extMan); 190 AddExtentsForPartitions(extMan); 191 } 192 catch (bad_alloc &e) { 193 err = ENOMEM; 194 } 195 catch (...) { // currently only ENOMEM is possible 196 err = ENOMEM; 197 } 198 199 labelExit: 200 if (err != 0) { 201 wipefs_free(handle); 202 } 203 return err; 204} // wipefs_alloc 205 206extern "C" int 207wipefs_include_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks) 208{ 209 int err = 0; 210 try { 211 handle->extMan.AddBlockRangeExtent(block_offset, nblocks); 212 } 213 catch (bad_alloc &e) { 214 err = ENOMEM; 215 } 216 catch (...) { // currently only ENOMEM is possible 217 err = ENOMEM; 218 } 219 return err; 220} 221 222extern "C" int 223wipefs_except_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks) 224{ 225 int err = 0; 226 try { 227 handle->extMan.RemoveBlockRangeExtent(block_offset, nblocks); 228 } 229 catch (bad_alloc &e) { 230 err = ENOMEM; 231 } 232 catch (...) { // currently only ENOMEM is possible 233 err = ENOMEM; 234 } 235 return err; 236} 237 238extern "C" int 239wipefs_wipe(wipefs_ctx handle) 240{ 241 int err = 0; 242 uint8_t *bufZero = NULL; 243 ListExtIt curExt; 244 size_t bufSize; 245 dk_extent_t extent; 246 dk_unmap_t unmap; 247 248 memset(&extent, 0, sizeof(dk_extent_t)); 249 extent.length = handle->extMan.totalBytes; 250 251 memset(&unmap, 0, sizeof(dk_unmap_t)); 252 unmap.extents = &extent; 253 unmap.extentsCount = 1; 254 255 // 256 // Don't bother to check the return value since this is mostly 257 // informational for the lower-level drivers. 258 // 259 ioctl(handle->fd, DKIOCUNMAP, (caddr_t)&unmap); 260 261 262 bufSize = 128 * 1024; // issue large I/O to get better performance 263 if (handle->extMan.nativeBlockSize > bufSize) { 264 bufSize = handle->extMan.nativeBlockSize; 265 } 266 bufZero = new uint8_t[bufSize]; 267 bzero(bufZero, bufSize); 268 269 off_t byteOffset, totalBytes; 270 size_t numBytes, numBytesToWrite, blockSize; 271 272 blockSize = handle->extMan.blockSize; 273 totalBytes = handle->extMan.totalBytes; 274 // write zero to all extents 275 for (curExt = handle->extMan.extentList.begin(); curExt != handle->extMan.extentList.end(); curExt++) { 276 byteOffset = curExt->blockAddr * blockSize; 277 numBytes = curExt->numBlocks * blockSize; 278 // make both offset and numBytes on native block boundary 279 if (byteOffset % handle->extMan.nativeBlockSize != 0 || 280 numBytes % handle->extMan.nativeBlockSize != 0) { 281 size_t nativeBlockSize = handle->extMan.nativeBlockSize; 282 off_t newOffset, newEndOffset; 283 newOffset = byteOffset / nativeBlockSize * nativeBlockSize; 284 newEndOffset = roundup(byteOffset + numBytes, nativeBlockSize); 285 byteOffset = newOffset; 286 numBytes = newEndOffset - newOffset; 287 } 288 if (byteOffset + (off_t)numBytes > totalBytes) { 289 numBytes = totalBytes - byteOffset; 290 } 291 while (numBytes > 0) { 292 numBytesToWrite = min(numBytes, bufSize); 293 if (pwrite(handle->fd, bufZero, numBytesToWrite, byteOffset) != (ssize_t)numBytesToWrite) { 294 err = errno; 295 goto labelExit; 296 } 297 numBytes -= numBytesToWrite; 298 byteOffset += numBytesToWrite; 299 } 300 } 301 302 labelExit: 303 if (bufZero != NULL) 304 delete[] bufZero; 305 return err; 306} // wipefs_wipe 307 308extern "C" void 309wipefs_free(wipefs_ctx *handle) 310{ 311 if (*handle != NULL) { 312 delete *handle; 313 *handle = NULL; 314 } 315} 316