]> git.saurik.com Git - apple/libutil.git/blob - wipefs.cpp
7b0857a48f67c3716cd5249762ba42f47b849927
[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 <sys/uio.h>
29 #include <sys/ioctl.h>
30 #include <sys/disk.h>
31 #include <sys/stat.h>
32
33 #include "ExtentManager.h"
34 #include "wipefs.h"
35
36 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
37
38 struct __wipefs_ctx {
39 int fd;
40 class ExtentManager extMan;
41 };
42
43 void
44 AddExtentsForFutureFS(class ExtentManager *extMan)
45 {
46 // we don't know what blocks future FS will use to recognize itself. But we'd better be safe than sorry and write
47 // the first and last 2MB of the volume
48 off_t size = 2 * 1024 * 1024;
49 extMan->AddByteRangeExtent(0, size);
50 extMan->AddByteRangeExtent(extMan->totalBytes - size, size);
51 }
52
53 void
54 AddExtentsForHFS(class ExtentManager *extMan)
55 {
56 // first 1KB is boot block, last 512B is reserved
57 // the Volume Header (512B) is after 1KB and before the last 512B
58 extMan->AddByteRangeExtent(0, 1024 + 512);
59 extMan->AddByteRangeExtent(extMan->totalBytes - 1024, 1024);
60 }
61
62 void
63 AddExtentsForMSDOS(class ExtentManager *extMan)
64 {
65 // MSDOS needs the first block (in theory, up to 32KB)
66 extMan->AddByteRangeExtent(0, 32 * 1024);
67 }
68
69 void
70 AddExtentsForNTFS(class ExtentManager *extMan)
71 {
72 // NTFS supports block size from 256B to 32768B. The first, middle and last block are needed
73 extMan->AddByteRangeExtent(0, 32 * 1024);
74 extMan->AddByteRangeExtent(extMan->totalBytes - 32 * 1024, 32 * 1024);
75 // to be safe, add the rage from (mid_point - 32KB) to (mid_point + 32KB)
76 extMan->AddByteRangeExtent(extMan->totalBytes / 2 - 32 * 1024, 64 * 1024);
77 }
78
79 void
80 AddExtentsForUDF(class ExtentManager *extMan)
81 {
82 off_t lastBlockAddr = extMan->totalBlocks - 1;
83
84 // Volume Recognization Sequence (VRS) starts at 32KB, usually less than 7 Volume Structure Descriptors (2KB each)
85 extMan->AddByteRangeExtent(32 * 1024, 14 * 1024);
86
87 // AVDP is on 256, 512, last block, last block - 256
88 extMan->AddBlockRangeExtent(256, 1);
89 extMan->AddBlockRangeExtent(512, 1);
90 extMan->AddBlockRangeExtent(lastBlockAddr, 1);
91 extMan->AddBlockRangeExtent(lastBlockAddr - 256, 1);
92
93 // to be safe, assume the device has 2KB block size and do it again
94 if (extMan->blockSize != 2048) {
95 off_t blockSize = 2048;
96 // AVDP is on 256, 512, last block, last block - 256
97 extMan->AddByteRangeExtent(256 * blockSize, blockSize);
98 extMan->AddByteRangeExtent(512 * blockSize, blockSize);
99 extMan->AddByteRangeExtent(extMan->totalBytes - blockSize, blockSize);
100 extMan->AddByteRangeExtent(extMan->totalBytes - 256 * blockSize, blockSize);
101 }
102 }
103
104 void
105 AddExtentsForUFS(class ExtentManager *extMan)
106 {
107 // UFS super block is 8KB at offset 8KB
108 extMan->AddByteRangeExtent(8192, 8192);
109 }
110
111 void
112 AddExtentsForZFS(class ExtentManager *extMan)
113 {
114 // ZFS needs the first 512KB and last 512KB for all the 4 disk labels
115 extMan->AddByteRangeExtent(0, 512 * 1024);
116 extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 1024, 512 * 1024);
117 }
118
119 void
120 AddExtentsForPartitions(class ExtentManager *extMan)
121 {
122 // MBR (Master Boot Record) needs the first sector
123 // APM (Apple Partition Map) needs the second sector
124 // GPT (GUID Partition Table) needs the first 34 and last 33 sectors
125 extMan->AddByteRangeExtent(0, 512 * 34);
126 extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 33, 512 * 33);
127 }
128
129 extern "C" int
130 wipefs_alloc(int fd, size_t block_size, wipefs_ctx *handle)
131 {
132 int err = 0;
133 uint64_t numBlocks = 0;
134 uint32_t nativeBlockSize = 0;
135 off_t totalSizeInBytes = 0;
136 class ExtentManager *extMan = NULL;
137 struct stat sbuf = { 0 };
138
139 *handle = NULL;
140 (void)fstat(fd, &sbuf);
141 switch (sbuf.st_mode & S_IFMT) {
142 case S_IFCHR:
143 case S_IFBLK:
144 if (ioctl(fd, DKIOCGETBLOCKSIZE, (char *)&nativeBlockSize) < 0) {
145 err = errno;
146 goto labelExit;
147 }
148 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (char *)&numBlocks) < 0) {
149 err = errno;
150 goto labelExit;
151 }
152 totalSizeInBytes = numBlocks * nativeBlockSize;
153 break;
154 case S_IFREG:
155 nativeBlockSize = sbuf.st_blksize;
156 numBlocks = sbuf.st_size / sbuf.st_blksize;
157 totalSizeInBytes = sbuf.st_size;
158 break;
159 default:
160 errno = EINVAL;
161 goto labelExit;
162 }
163 if (block_size == 0) {
164 block_size = nativeBlockSize;
165 }
166 if (block_size == 0 || totalSizeInBytes == 0) {
167 err = EINVAL;
168 goto labelExit;
169 }
170
171 try {
172 *handle = new __wipefs_ctx;
173 if (*handle == NULL) {
174 bad_alloc e;
175 throw e;
176 }
177
178 (*handle)->fd = fd;
179 extMan = &(*handle)->extMan;
180
181 extMan->Init(block_size, nativeBlockSize, totalSizeInBytes);
182 AddExtentsForFutureFS(extMan);
183 AddExtentsForHFS(extMan);
184 AddExtentsForMSDOS(extMan);
185 AddExtentsForNTFS(extMan);
186 AddExtentsForUDF(extMan);
187 AddExtentsForUFS(extMan);
188 AddExtentsForZFS(extMan);
189 AddExtentsForPartitions(extMan);
190 }
191 catch (bad_alloc &e) {
192 err = ENOMEM;
193 }
194 catch (...) { // currently only ENOMEM is possible
195 err = ENOMEM;
196 }
197
198 labelExit:
199 if (err != 0) {
200 wipefs_free(handle);
201 }
202 return err;
203 } // wipefs_alloc
204
205 extern "C" int
206 wipefs_except_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks)
207 {
208 int err = 0;
209 try {
210 handle->extMan.RemoveBlockRangeExtent(block_offset, nblocks);
211 }
212 catch (bad_alloc &e) {
213 err = ENOMEM;
214 }
215 catch (...) { // currently only ENOMEM is possible
216 err = ENOMEM;
217 }
218 return err;
219 }
220
221 extern "C" int
222 wipefs_wipe(wipefs_ctx handle)
223 {
224 int err = 0;
225 uint8_t *bufZero = NULL;
226 ListExtIt curExt;
227 size_t bufSize;
228 dk_extent_t extent;
229 dk_unmap_t unmap;
230
231 memset(&extent, 0, sizeof(dk_extent_t));
232 extent.length = handle->extMan.totalBytes;
233
234 memset(&unmap, 0, sizeof(dk_unmap_t));
235 unmap.extents = &extent;
236 unmap.extentsCount = 1;
237
238 //
239 // Don't bother to check the return value since this is mostly
240 // informational for the lower-level drivers.
241 //
242 ioctl(handle->fd, DKIOCUNMAP, (caddr_t)&unmap);
243
244
245 bufSize = 256 * 1024; // issue large I/O to get better performance
246 bufZero = new uint8_t[bufSize];
247 bzero(bufZero, bufSize);
248
249 off_t byteOffset, totalBytes;
250 size_t numBytes, numBytesToWrite, blockSize;
251
252 blockSize = handle->extMan.blockSize;
253 totalBytes = handle->extMan.totalBytes;
254 // write zero to all extents
255 for (curExt = handle->extMan.extentList.begin(); curExt != handle->extMan.extentList.end(); curExt++) {
256 byteOffset = curExt->blockAddr * blockSize;
257 numBytes = curExt->numBlocks * blockSize;
258 // make both offset and numBytes on native block boundary
259 if (byteOffset % handle->extMan.nativeBlockSize != 0 ||
260 numBytes % handle->extMan.nativeBlockSize != 0) {
261 size_t nativeBlockSize = handle->extMan.nativeBlockSize;
262 off_t newOffset, newEndOffset;
263 newOffset = byteOffset / nativeBlockSize * nativeBlockSize;
264 newEndOffset = roundup(byteOffset + numBytes, nativeBlockSize);
265 byteOffset = newOffset;
266 numBytes = newEndOffset - newOffset;
267 }
268 if (byteOffset + (off_t)numBytes > totalBytes) {
269 numBytes = totalBytes - byteOffset;
270 }
271 while (numBytes > 0) {
272 numBytesToWrite = min(numBytes, bufSize);
273 if (pwrite(handle->fd, bufZero, numBytesToWrite, byteOffset) != (ssize_t)numBytesToWrite) {
274 err = errno;
275 goto labelExit;
276 }
277 numBytes -= numBytesToWrite;
278 byteOffset += numBytesToWrite;
279 }
280 }
281
282 labelExit:
283 if (bufZero != NULL)
284 delete[] bufZero;
285 return err;
286 } // wipefs_wipe
287
288 extern "C" void
289 wipefs_free(wipefs_ctx *handle)
290 {
291 if (*handle != NULL) {
292 delete *handle;
293 *handle = NULL;
294 }
295 }