]> git.saurik.com Git - apple/libutil.git/blob - wipefs.cpp
d5efc0558507ea1e7a359fa07b29c4805d4bc9dd
[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_discard_t discard;
229
230 memset(&discard, 0, sizeof(dk_discard_t));
231 discard.length = handle->extMan.totalBytes;
232
233 //
234 // Don't bother to check the return value since this is mostly
235 // informational for the lower-level drivers.
236 //
237 ioctl(handle->fd, DKIOCDISCARD, (caddr_t)&discard);
238
239
240 bufSize = 256 * 1024; // issue large I/O to get better performance
241 bufZero = new uint8_t[bufSize];
242 bzero(bufZero, bufSize);
243
244 off_t byteOffset, totalBytes;
245 size_t numBytes, numBytesToWrite, blockSize;
246
247 blockSize = handle->extMan.blockSize;
248 totalBytes = handle->extMan.totalBytes;
249 // write zero to all extents
250 for (curExt = handle->extMan.extentList.begin(); curExt != handle->extMan.extentList.end(); curExt++) {
251 byteOffset = curExt->blockAddr * blockSize;
252 numBytes = curExt->numBlocks * blockSize;
253 // make both offset and numBytes on native block boundary
254 if (byteOffset % handle->extMan.nativeBlockSize != 0 ||
255 numBytes % handle->extMan.nativeBlockSize != 0) {
256 size_t nativeBlockSize = handle->extMan.nativeBlockSize;
257 off_t newOffset, newEndOffset;
258 newOffset = byteOffset / nativeBlockSize * nativeBlockSize;
259 newEndOffset = roundup(byteOffset + numBytes, nativeBlockSize);
260 byteOffset = newOffset;
261 numBytes = newEndOffset - newOffset;
262 }
263 if (byteOffset + (off_t)numBytes > totalBytes) {
264 numBytes = totalBytes - byteOffset;
265 }
266 while (numBytes > 0) {
267 numBytesToWrite = min(numBytes, bufSize);
268 if (pwrite(handle->fd, bufZero, numBytesToWrite, byteOffset) != (ssize_t)numBytesToWrite) {
269 err = errno;
270 goto labelExit;
271 }
272 numBytes -= numBytesToWrite;
273 byteOffset += numBytesToWrite;
274 }
275 }
276
277 labelExit:
278 if (bufZero != NULL)
279 delete[] bufZero;
280 return err;
281 } // wipefs_wipe
282
283 extern "C" void
284 wipefs_free(wipefs_ctx *handle)
285 {
286 if (*handle != NULL) {
287 delete *handle;
288 *handle = NULL;
289 }
290 }