]> git.saurik.com Git - apple/libutil.git/blob - wipefs.cpp
3be8ae1e2419d9d8cca103f27b2c5916a8ab2217
[apple/libutil.git] / wipefs.cpp
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
39 struct __wipefs_ctx {
40 int fd;
41 class ExtentManager extMan;
42 };
43
44 static void
45 AddExtentsForFutureFS(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
54 static void
55 AddExtentsForHFS(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
63 static void
64 AddExtentsForMSDOS(class ExtentManager *extMan)
65 {
66 // MSDOS needs the first block (in theory, up to 32KB)
67 extMan->AddByteRangeExtent(0, 32 * 1024);
68 }
69
70 static void
71 AddExtentsForNTFS(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
80 static void
81 AddExtentsForUDF(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
105 static void
106 AddExtentsForUFS(class ExtentManager *extMan)
107 {
108 // UFS super block is 8KB at offset 8KB
109 extMan->AddByteRangeExtent(8192, 8192);
110 }
111
112 static void
113 AddExtentsForZFS(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
120 static void
121 AddExtentsForPartitions(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
130 static void
131 AddExtentsForCoreStorage(class ExtentManager *extMan)
132 {
133 // the CoreStorage VolumeHeader structures reside in the first/last 512 bytes of each PV
134 extMan->AddByteRangeExtent(0, 512);
135 extMan->AddByteRangeExtent(extMan->totalBytes - 512, 512);
136 }
137
138 extern "C" int
139 wipefs_alloc(int fd, size_t block_size, wipefs_ctx *handle)
140 {
141 int err = 0;
142 uint64_t numBlocks = 0;
143 uint32_t nativeBlockSize = 0;
144 off_t totalSizeInBytes = 0;
145 class ExtentManager *extMan = NULL;
146 struct stat sbuf = { 0 };
147
148 *handle = NULL;
149 (void)fstat(fd, &sbuf);
150 switch (sbuf.st_mode & S_IFMT) {
151 case S_IFCHR:
152 case S_IFBLK:
153 if (ioctl(fd, DKIOCGETBLOCKSIZE, (char *)&nativeBlockSize) < 0) {
154 err = errno;
155 goto labelExit;
156 }
157 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (char *)&numBlocks) < 0) {
158 err = errno;
159 goto labelExit;
160 }
161 totalSizeInBytes = numBlocks * nativeBlockSize;
162 break;
163 case S_IFREG:
164 nativeBlockSize = sbuf.st_blksize;
165 numBlocks = sbuf.st_size / sbuf.st_blksize;
166 totalSizeInBytes = sbuf.st_size;
167 break;
168 default:
169 errno = EINVAL;
170 goto labelExit;
171 }
172 if (block_size == 0) {
173 block_size = nativeBlockSize;
174 }
175 if (block_size == 0 || totalSizeInBytes == 0) {
176 err = EINVAL;
177 goto labelExit;
178 }
179
180 try {
181 *handle = new __wipefs_ctx;
182 if (*handle == NULL) {
183 bad_alloc e;
184 throw e;
185 }
186
187 (*handle)->fd = fd;
188 extMan = &(*handle)->extMan;
189
190 extMan->Init(block_size, nativeBlockSize, totalSizeInBytes);
191 AddExtentsForFutureFS(extMan);
192 AddExtentsForHFS(extMan);
193 AddExtentsForMSDOS(extMan);
194 AddExtentsForNTFS(extMan);
195 AddExtentsForUDF(extMan);
196 AddExtentsForUFS(extMan);
197 AddExtentsForZFS(extMan);
198 AddExtentsForPartitions(extMan);
199 AddExtentsForCoreStorage(extMan);
200 }
201 catch (bad_alloc &e) {
202 err = ENOMEM;
203 }
204 catch (...) { // currently only ENOMEM is possible
205 err = ENOMEM;
206 }
207
208 labelExit:
209 if (err != 0) {
210 wipefs_free(handle);
211 }
212 return err;
213 } // wipefs_alloc
214
215 extern "C" int
216 wipefs_include_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks)
217 {
218 int err = 0;
219 try {
220 handle->extMan.AddBlockRangeExtent(block_offset, nblocks);
221 }
222 catch (bad_alloc &e) {
223 err = ENOMEM;
224 }
225 catch (...) { // currently only ENOMEM is possible
226 err = ENOMEM;
227 }
228 return err;
229 }
230
231 extern "C" int
232 wipefs_except_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks)
233 {
234 int err = 0;
235 try {
236 handle->extMan.RemoveBlockRangeExtent(block_offset, nblocks);
237 }
238 catch (bad_alloc &e) {
239 err = ENOMEM;
240 }
241 catch (...) { // currently only ENOMEM is possible
242 err = ENOMEM;
243 }
244 return err;
245 }
246
247 extern "C" int
248 wipefs_wipe(wipefs_ctx handle)
249 {
250 int err = 0;
251 uint8_t *bufZero = NULL;
252 ListExtIt curExt;
253 size_t bufSize;
254 dk_extent_t extent;
255 dk_unmap_t unmap;
256
257 memset(&extent, 0, sizeof(dk_extent_t));
258 extent.length = handle->extMan.totalBytes;
259
260 memset(&unmap, 0, sizeof(dk_unmap_t));
261 unmap.extents = &extent;
262 unmap.extentsCount = 1;
263
264 //
265 // Don't bother to check the return value since this is mostly
266 // informational for the lower-level drivers.
267 //
268 ioctl(handle->fd, DKIOCUNMAP, (caddr_t)&unmap);
269
270
271 bufSize = 128 * 1024; // issue large I/O to get better performance
272 if (handle->extMan.nativeBlockSize > bufSize) {
273 bufSize = handle->extMan.nativeBlockSize;
274 }
275 bufZero = new uint8_t[bufSize];
276 bzero(bufZero, bufSize);
277
278 off_t byteOffset, totalBytes;
279 size_t numBytes, numBytesToWrite, blockSize;
280
281 blockSize = handle->extMan.blockSize;
282 totalBytes = handle->extMan.totalBytes;
283 // write zero to all extents
284 for (curExt = handle->extMan.extentList.begin(); curExt != handle->extMan.extentList.end(); curExt++) {
285 byteOffset = curExt->blockAddr * blockSize;
286 numBytes = curExt->numBlocks * blockSize;
287 // make both offset and numBytes on native block boundary
288 if (byteOffset % handle->extMan.nativeBlockSize != 0 ||
289 numBytes % handle->extMan.nativeBlockSize != 0) {
290 size_t nativeBlockSize = handle->extMan.nativeBlockSize;
291 off_t newOffset, newEndOffset;
292 newOffset = byteOffset / nativeBlockSize * nativeBlockSize;
293 newEndOffset = roundup(byteOffset + numBytes, nativeBlockSize);
294 byteOffset = newOffset;
295 numBytes = newEndOffset - newOffset;
296 }
297 if (byteOffset + (off_t)numBytes > totalBytes) {
298 numBytes = totalBytes - byteOffset;
299 }
300 while (numBytes > 0) {
301 numBytesToWrite = min(numBytes, bufSize);
302 if (pwrite(handle->fd, bufZero, numBytesToWrite, byteOffset) != (ssize_t)numBytesToWrite) {
303 err = errno;
304 goto labelExit;
305 }
306 numBytes -= numBytesToWrite;
307 byteOffset += numBytesToWrite;
308 }
309 }
310
311 labelExit:
312
313 (void)ioctl(handle->fd, DKIOCSYNCHRONIZECACHE);
314 if (bufZero != NULL)
315 delete[] bufZero;
316 return err;
317 } // wipefs_wipe
318
319 extern "C" void
320 wipefs_free(wipefs_ctx *handle)
321 {
322 if (*handle != NULL) {
323 delete *handle;
324 *handle = NULL;
325 }
326 }