]>
Commit | Line | Data |
---|---|---|
1bd2040a A |
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> | |
afa5f1bd | 28 | #include <unistd.h> |
1bd2040a A |
29 | #include <sys/uio.h> |
30 | #include <sys/ioctl.h> | |
31 | #include <sys/disk.h> | |
32 | #include <sys/stat.h> | |
a71d6fbf | 33 | #include <sys/syslimits.h> |
a71d6fbf A |
34 | #include <string.h> |
35 | #include <spawn.h> | |
a71d6fbf | 36 | #include <os/log.h> |
1bd2040a A |
37 | |
38 | #include "ExtentManager.h" | |
39 | #include "wipefs.h" | |
40 | ||
a71d6fbf | 41 | #define wipefs_roundup(x, y) ((((x)+((y)-1))/(y))*(y)) |
1bd2040a A |
42 | |
43 | struct __wipefs_ctx { | |
44 | int fd; | |
45 | class ExtentManager extMan; | |
a71d6fbf A |
46 | |
47 | // xartutil information | |
55a31469 | 48 | char *diskname; |
1bd2040a A |
49 | }; |
50 | ||
5510e443 | 51 | static void |
1bd2040a A |
52 | AddExtentsForFutureFS(class ExtentManager *extMan) |
53 | { | |
54 | // we don't know what blocks future FS will use to recognize itself. But we'd better be safe than sorry and write | |
55 | // the first and last 2MB of the volume | |
56 | off_t size = 2 * 1024 * 1024; | |
57 | extMan->AddByteRangeExtent(0, size); | |
58 | extMan->AddByteRangeExtent(extMan->totalBytes - size, size); | |
59 | } | |
60 | ||
5510e443 | 61 | static void |
1bd2040a A |
62 | AddExtentsForHFS(class ExtentManager *extMan) |
63 | { | |
64 | // first 1KB is boot block, last 512B is reserved | |
65 | // the Volume Header (512B) is after 1KB and before the last 512B | |
66 | extMan->AddByteRangeExtent(0, 1024 + 512); | |
67 | extMan->AddByteRangeExtent(extMan->totalBytes - 1024, 1024); | |
68 | } | |
69 | ||
5510e443 | 70 | static void |
1bd2040a A |
71 | AddExtentsForMSDOS(class ExtentManager *extMan) |
72 | { | |
73 | // MSDOS needs the first block (in theory, up to 32KB) | |
74 | extMan->AddByteRangeExtent(0, 32 * 1024); | |
75 | } | |
76 | ||
5510e443 | 77 | static void |
1bd2040a A |
78 | AddExtentsForNTFS(class ExtentManager *extMan) |
79 | { | |
80 | // NTFS supports block size from 256B to 32768B. The first, middle and last block are needed | |
81 | extMan->AddByteRangeExtent(0, 32 * 1024); | |
82 | extMan->AddByteRangeExtent(extMan->totalBytes - 32 * 1024, 32 * 1024); | |
83 | // to be safe, add the rage from (mid_point - 32KB) to (mid_point + 32KB) | |
84 | extMan->AddByteRangeExtent(extMan->totalBytes / 2 - 32 * 1024, 64 * 1024); | |
85 | } | |
86 | ||
5510e443 | 87 | static void |
1bd2040a A |
88 | AddExtentsForUDF(class ExtentManager *extMan) |
89 | { | |
90 | off_t lastBlockAddr = extMan->totalBlocks - 1; | |
91 | ||
92 | // Volume Recognization Sequence (VRS) starts at 32KB, usually less than 7 Volume Structure Descriptors (2KB each) | |
93 | extMan->AddByteRangeExtent(32 * 1024, 14 * 1024); | |
94 | ||
95 | // AVDP is on 256, 512, last block, last block - 256 | |
96 | extMan->AddBlockRangeExtent(256, 1); | |
97 | extMan->AddBlockRangeExtent(512, 1); | |
98 | extMan->AddBlockRangeExtent(lastBlockAddr, 1); | |
99 | extMan->AddBlockRangeExtent(lastBlockAddr - 256, 1); | |
100 | ||
101 | // to be safe, assume the device has 2KB block size and do it again | |
102 | if (extMan->blockSize != 2048) { | |
103 | off_t blockSize = 2048; | |
104 | // AVDP is on 256, 512, last block, last block - 256 | |
105 | extMan->AddByteRangeExtent(256 * blockSize, blockSize); | |
106 | extMan->AddByteRangeExtent(512 * blockSize, blockSize); | |
107 | extMan->AddByteRangeExtent(extMan->totalBytes - blockSize, blockSize); | |
108 | extMan->AddByteRangeExtent(extMan->totalBytes - 256 * blockSize, blockSize); | |
109 | } | |
110 | } | |
111 | ||
5510e443 | 112 | static void |
1bd2040a A |
113 | AddExtentsForUFS(class ExtentManager *extMan) |
114 | { | |
115 | // UFS super block is 8KB at offset 8KB | |
116 | extMan->AddByteRangeExtent(8192, 8192); | |
117 | } | |
118 | ||
5510e443 | 119 | static void |
1bd2040a A |
120 | AddExtentsForZFS(class ExtentManager *extMan) |
121 | { | |
122 | // ZFS needs the first 512KB and last 512KB for all the 4 disk labels | |
123 | extMan->AddByteRangeExtent(0, 512 * 1024); | |
124 | extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 1024, 512 * 1024); | |
125 | } | |
126 | ||
5510e443 | 127 | static void |
1bd2040a A |
128 | AddExtentsForPartitions(class ExtentManager *extMan) |
129 | { | |
130 | // MBR (Master Boot Record) needs the first sector | |
131 | // APM (Apple Partition Map) needs the second sector | |
132 | // GPT (GUID Partition Table) needs the first 34 and last 33 sectors | |
133 | extMan->AddByteRangeExtent(0, 512 * 34); | |
134 | extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 33, 512 * 33); | |
135 | } | |
136 | ||
18e053d4 A |
137 | static void |
138 | AddExtentsForCoreStorage(class ExtentManager *extMan) | |
139 | { | |
140 | // the CoreStorage VolumeHeader structures reside in the first/last 512 bytes of each PV | |
141 | extMan->AddByteRangeExtent(0, 512); | |
142 | extMan->AddByteRangeExtent(extMan->totalBytes - 512, 512); | |
143 | } | |
144 | ||
55a31469 A |
145 | static char * |
146 | query_disk_info(int fd) { | |
a71d6fbf A |
147 | char disk_path[PATH_MAX]; |
148 | char *disk_name; | |
149 | ||
a71d6fbf A |
150 | // Fetch the path |
151 | if (fcntl(fd, F_GETPATH, disk_path) == -1) { | |
55a31469 | 152 | return (NULL); |
a71d6fbf A |
153 | } |
154 | ||
55a31469 A |
155 | // Find the last pathname component. |
156 | disk_name = strrchr(disk_path, '/'); | |
157 | if (disk_name == NULL) { | |
158 | // Not that we expect this to happen... | |
159 | disk_name = disk_path; | |
a71d6fbf | 160 | } else { |
55a31469 A |
161 | // Skip over the '/'. |
162 | disk_name++; | |
a71d6fbf A |
163 | } |
164 | ||
55a31469 A |
165 | if (*disk_name == 'r') { |
166 | // Raw device; skip over leading 'r'. | |
167 | disk_name++; | |
a71d6fbf A |
168 | } |
169 | ||
55a31469 A |
170 | // ...and make sure it's really a disk. |
171 | if (strncmp(disk_name, "disk", strlen("disk")) != 0) { | |
172 | return (NULL); | |
a71d6fbf A |
173 | } |
174 | ||
55a31469 | 175 | return (strdup(disk_name)); |
a71d6fbf A |
176 | } |
177 | ||
178 | static | |
55a31469 | 179 | int run_xartutil(char *const diskname) |
a71d6fbf | 180 | { |
a71d6fbf A |
181 | pid_t child_pid, wait_pid; |
182 | posix_spawn_file_actions_t fileActions; | |
183 | bool haveFileActions = false; | |
184 | int child_status = 0; | |
185 | int result = 0; | |
186 | ||
187 | char arg1[] = "xartutil"; | |
55a31469 | 188 | char arg2[] = "--erase-disk"; |
a71d6fbf | 189 | |
55a31469 | 190 | char *const xartutil_argv[] = {arg1, arg2, diskname, NULL}; |
a71d6fbf A |
191 | |
192 | result = posix_spawn_file_actions_init(&fileActions); | |
193 | if (result) { | |
194 | os_log_error(OS_LOG_DEFAULT, "Warning, init xartutil file actions error: %d", result); | |
195 | result = -1; | |
196 | goto out; | |
197 | } | |
198 | ||
199 | haveFileActions = true; | |
200 | ||
201 | // Redirect stdout & stderr (results not critical, so we ignore return values). | |
202 | posix_spawn_file_actions_addopen(&fileActions, STDOUT_FILENO, "/dev/null", O_WRONLY, 0); | |
203 | posix_spawn_file_actions_addopen(&fileActions, STDERR_FILENO, "/dev/null", O_WRONLY, 0); | |
204 | ||
205 | result = posix_spawn(&child_pid, "/usr/sbin/xartutil", &fileActions, NULL, xartutil_argv, NULL); | |
206 | ||
207 | if (result) { | |
208 | os_log_error(OS_LOG_DEFAULT, "Warning, unable to start xartutil, spawn error: %d", result); | |
209 | result = -1; | |
210 | goto out; | |
211 | } | |
212 | ||
213 | do { | |
214 | wait_pid = waitpid(child_pid, &child_status, 0); | |
215 | } while (wait_pid == -1 && errno == EINTR); | |
216 | ||
217 | if (wait_pid == -1) { | |
218 | os_log_error(OS_LOG_DEFAULT, "Warning, unable to start xartutil, waitpid error: %d", errno); | |
219 | result = -1; | |
220 | goto out; | |
221 | } | |
222 | ||
223 | if (WIFEXITED(child_status)) { | |
224 | // xartutil terminated normally, get exit status | |
225 | result = WEXITSTATUS(child_status); | |
226 | ||
227 | if (result) { | |
228 | os_log_error(OS_LOG_DEFAULT, "Warning, xartutil returned status %d", result); | |
229 | } | |
230 | } else { | |
231 | result = -1; | |
232 | ||
233 | if (WIFSIGNALED(child_status)) { | |
234 | os_log_error(OS_LOG_DEFAULT, "Warning, xartutil terminated by signal: %u", WTERMSIG(child_status)); | |
235 | } else if (WIFSTOPPED(child_status)) { | |
236 | os_log_error(OS_LOG_DEFAULT, "Warning, xartutil stopped by signal: %u", WSTOPSIG(child_status)); | |
237 | } else { | |
238 | os_log_error(OS_LOG_DEFAULT, "Warning, xartutil terminated abnormally, status 0x%x", child_status); | |
239 | } | |
240 | } | |
241 | ||
242 | out: | |
243 | ||
244 | if (haveFileActions) { | |
245 | posix_spawn_file_actions_destroy(&fileActions); | |
246 | } | |
247 | ||
248 | return (result); | |
249 | } | |
250 | ||
1bd2040a A |
251 | extern "C" int |
252 | wipefs_alloc(int fd, size_t block_size, wipefs_ctx *handle) | |
253 | { | |
254 | int err = 0; | |
255 | uint64_t numBlocks = 0; | |
256 | uint32_t nativeBlockSize = 0; | |
257 | off_t totalSizeInBytes = 0; | |
258 | class ExtentManager *extMan = NULL; | |
259 | struct stat sbuf = { 0 }; | |
55a31469 | 260 | char *diskname = NULL; |
1bd2040a A |
261 | |
262 | *handle = NULL; | |
263 | (void)fstat(fd, &sbuf); | |
264 | switch (sbuf.st_mode & S_IFMT) { | |
265 | case S_IFCHR: | |
266 | case S_IFBLK: | |
267 | if (ioctl(fd, DKIOCGETBLOCKSIZE, (char *)&nativeBlockSize) < 0) { | |
268 | err = errno; | |
269 | goto labelExit; | |
270 | } | |
271 | if (ioctl(fd, DKIOCGETBLOCKCOUNT, (char *)&numBlocks) < 0) { | |
272 | err = errno; | |
273 | goto labelExit; | |
274 | } | |
275 | totalSizeInBytes = numBlocks * nativeBlockSize; | |
a71d6fbf | 276 | |
55a31469 | 277 | diskname = query_disk_info(fd); |
1bd2040a A |
278 | break; |
279 | case S_IFREG: | |
280 | nativeBlockSize = sbuf.st_blksize; | |
281 | numBlocks = sbuf.st_size / sbuf.st_blksize; | |
282 | totalSizeInBytes = sbuf.st_size; | |
283 | break; | |
284 | default: | |
285 | errno = EINVAL; | |
286 | goto labelExit; | |
287 | } | |
288 | if (block_size == 0) { | |
289 | block_size = nativeBlockSize; | |
290 | } | |
291 | if (block_size == 0 || totalSizeInBytes == 0) { | |
292 | err = EINVAL; | |
293 | goto labelExit; | |
294 | } | |
295 | ||
296 | try { | |
297 | *handle = new __wipefs_ctx; | |
298 | if (*handle == NULL) { | |
299 | bad_alloc e; | |
300 | throw e; | |
301 | } | |
302 | ||
303 | (*handle)->fd = fd; | |
55a31469 | 304 | (*handle)->diskname = NULL; |
1bd2040a A |
305 | extMan = &(*handle)->extMan; |
306 | ||
307 | extMan->Init(block_size, nativeBlockSize, totalSizeInBytes); | |
308 | AddExtentsForFutureFS(extMan); | |
309 | AddExtentsForHFS(extMan); | |
310 | AddExtentsForMSDOS(extMan); | |
311 | AddExtentsForNTFS(extMan); | |
312 | AddExtentsForUDF(extMan); | |
313 | AddExtentsForUFS(extMan); | |
314 | AddExtentsForZFS(extMan); | |
315 | AddExtentsForPartitions(extMan); | |
18e053d4 | 316 | AddExtentsForCoreStorage(extMan); |
a71d6fbf | 317 | |
55a31469 | 318 | (*handle)->diskname = diskname; |
1bd2040a A |
319 | } |
320 | catch (bad_alloc &e) { | |
321 | err = ENOMEM; | |
322 | } | |
323 | catch (...) { // currently only ENOMEM is possible | |
324 | err = ENOMEM; | |
325 | } | |
326 | ||
327 | labelExit: | |
328 | if (err != 0) { | |
55a31469 A |
329 | if (diskname != NULL) |
330 | free(diskname); | |
1bd2040a A |
331 | wipefs_free(handle); |
332 | } | |
333 | return err; | |
334 | } // wipefs_alloc | |
335 | ||
afa5f1bd A |
336 | extern "C" int |
337 | wipefs_include_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks) | |
338 | { | |
339 | int err = 0; | |
340 | try { | |
341 | handle->extMan.AddBlockRangeExtent(block_offset, nblocks); | |
342 | } | |
343 | catch (bad_alloc &e) { | |
344 | err = ENOMEM; | |
345 | } | |
346 | catch (...) { // currently only ENOMEM is possible | |
347 | err = ENOMEM; | |
348 | } | |
349 | return err; | |
350 | } | |
351 | ||
1bd2040a A |
352 | extern "C" int |
353 | wipefs_except_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks) | |
354 | { | |
355 | int err = 0; | |
356 | try { | |
357 | handle->extMan.RemoveBlockRangeExtent(block_offset, nblocks); | |
358 | } | |
359 | catch (bad_alloc &e) { | |
360 | err = ENOMEM; | |
361 | } | |
362 | catch (...) { // currently only ENOMEM is possible | |
363 | err = ENOMEM; | |
364 | } | |
365 | return err; | |
366 | } | |
367 | ||
368 | extern "C" int | |
369 | wipefs_wipe(wipefs_ctx handle) | |
370 | { | |
371 | int err = 0; | |
372 | uint8_t *bufZero = NULL; | |
373 | ListExtIt curExt; | |
374 | size_t bufSize; | |
e682b5d1 A |
375 | dk_extent_t extent; |
376 | dk_unmap_t unmap; | |
55a31469 A |
377 | |
378 | if (handle->diskname != NULL) { | |
379 | // Remove this disk's entry from the xART. | |
380 | run_xartutil(handle->diskname); | |
381 | } | |
1bd2040a | 382 | |
e682b5d1 A |
383 | memset(&extent, 0, sizeof(dk_extent_t)); |
384 | extent.length = handle->extMan.totalBytes; | |
385 | ||
386 | memset(&unmap, 0, sizeof(dk_unmap_t)); | |
387 | unmap.extents = &extent; | |
388 | unmap.extentsCount = 1; | |
1bd2040a A |
389 | |
390 | // | |
391 | // Don't bother to check the return value since this is mostly | |
392 | // informational for the lower-level drivers. | |
393 | // | |
e682b5d1 | 394 | ioctl(handle->fd, DKIOCUNMAP, (caddr_t)&unmap); |
1bd2040a | 395 | |
afa5f1bd | 396 | bufSize = 128 * 1024; // issue large I/O to get better performance |
5510e443 A |
397 | if (handle->extMan.nativeBlockSize > bufSize) { |
398 | bufSize = handle->extMan.nativeBlockSize; | |
399 | } | |
1bd2040a A |
400 | bufZero = new uint8_t[bufSize]; |
401 | bzero(bufZero, bufSize); | |
402 | ||
403 | off_t byteOffset, totalBytes; | |
404 | size_t numBytes, numBytesToWrite, blockSize; | |
405 | ||
406 | blockSize = handle->extMan.blockSize; | |
407 | totalBytes = handle->extMan.totalBytes; | |
408 | // write zero to all extents | |
409 | for (curExt = handle->extMan.extentList.begin(); curExt != handle->extMan.extentList.end(); curExt++) { | |
410 | byteOffset = curExt->blockAddr * blockSize; | |
411 | numBytes = curExt->numBlocks * blockSize; | |
412 | // make both offset and numBytes on native block boundary | |
413 | if (byteOffset % handle->extMan.nativeBlockSize != 0 || | |
414 | numBytes % handle->extMan.nativeBlockSize != 0) { | |
415 | size_t nativeBlockSize = handle->extMan.nativeBlockSize; | |
416 | off_t newOffset, newEndOffset; | |
417 | newOffset = byteOffset / nativeBlockSize * nativeBlockSize; | |
a71d6fbf | 418 | newEndOffset = wipefs_roundup(byteOffset + numBytes, nativeBlockSize); |
1bd2040a A |
419 | byteOffset = newOffset; |
420 | numBytes = newEndOffset - newOffset; | |
421 | } | |
422 | if (byteOffset + (off_t)numBytes > totalBytes) { | |
423 | numBytes = totalBytes - byteOffset; | |
424 | } | |
425 | while (numBytes > 0) { | |
426 | numBytesToWrite = min(numBytes, bufSize); | |
427 | if (pwrite(handle->fd, bufZero, numBytesToWrite, byteOffset) != (ssize_t)numBytesToWrite) { | |
428 | err = errno; | |
429 | goto labelExit; | |
430 | } | |
431 | numBytes -= numBytesToWrite; | |
432 | byteOffset += numBytesToWrite; | |
433 | } | |
a71d6fbf | 434 | } |
1bd2040a A |
435 | |
436 | labelExit: | |
18e053d4 A |
437 | |
438 | (void)ioctl(handle->fd, DKIOCSYNCHRONIZECACHE); | |
1bd2040a A |
439 | if (bufZero != NULL) |
440 | delete[] bufZero; | |
a71d6fbf | 441 | |
1bd2040a A |
442 | return err; |
443 | } // wipefs_wipe | |
444 | ||
445 | extern "C" void | |
446 | wipefs_free(wipefs_ctx *handle) | |
447 | { | |
448 | if (*handle != NULL) { | |
55a31469 A |
449 | char *diskname; |
450 | ||
451 | if ((diskname = (*handle)->diskname) != NULL) | |
452 | free(diskname); | |
1bd2040a A |
453 | delete *handle; |
454 | *handle = NULL; | |
455 | } | |
456 | } |