]> git.saurik.com Git - apple/libutil.git/blob - wipefs.cpp
libutil-34.tar.gz
[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 extern "C" int
131 wipefs_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
206 extern "C" int
207 wipefs_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
222 extern "C" int
223 wipefs_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
238 extern "C" int
239 wipefs_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
308 extern "C" void
309 wipefs_free(wipefs_ctx *handle)
310 {
311 if (*handle != NULL) {
312 delete *handle;
313 *handle = NULL;
314 }
315 }