]> git.saurik.com Git - apple/libutil.git/blame - wipefs.cpp
libutil-47.20.1.tar.gz
[apple/libutil.git] / wipefs.cpp
CommitLineData
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
A
33#include <sys/syslimits.h>
34#include <uuid/uuid.h>
35#include <paths.h>
36#include <string.h>
37#include <spawn.h>
38#include <IOKit/IOKitLib.h>
39#include <IOKit/storage/IOMedia.h>
40#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
41#include <CoreFoundation/CFNumber.h>
42#include <os/log.h>
1bd2040a
A
43
44#include "ExtentManager.h"
45#include "wipefs.h"
46
a71d6fbf 47#define wipefs_roundup(x, y) ((((x)+((y)-1))/(y))*(y))
1bd2040a
A
48
49struct __wipefs_ctx {
50 int fd;
51 class ExtentManager extMan;
a71d6fbf
A
52
53 // xartutil information
54 bool have_xartutil_info;
55 uuid_string_t uuid_str;
56 bool is_internal;
1bd2040a
A
57};
58
5510e443 59static void
1bd2040a
A
60AddExtentsForFutureFS(class ExtentManager *extMan)
61{
62 // we don't know what blocks future FS will use to recognize itself. But we'd better be safe than sorry and write
63 // the first and last 2MB of the volume
64 off_t size = 2 * 1024 * 1024;
65 extMan->AddByteRangeExtent(0, size);
66 extMan->AddByteRangeExtent(extMan->totalBytes - size, size);
67}
68
5510e443 69static void
1bd2040a
A
70AddExtentsForHFS(class ExtentManager *extMan)
71{
72 // first 1KB is boot block, last 512B is reserved
73 // the Volume Header (512B) is after 1KB and before the last 512B
74 extMan->AddByteRangeExtent(0, 1024 + 512);
75 extMan->AddByteRangeExtent(extMan->totalBytes - 1024, 1024);
76}
77
5510e443 78static void
1bd2040a
A
79AddExtentsForMSDOS(class ExtentManager *extMan)
80{
81 // MSDOS needs the first block (in theory, up to 32KB)
82 extMan->AddByteRangeExtent(0, 32 * 1024);
83}
84
5510e443 85static void
1bd2040a
A
86AddExtentsForNTFS(class ExtentManager *extMan)
87{
88 // NTFS supports block size from 256B to 32768B. The first, middle and last block are needed
89 extMan->AddByteRangeExtent(0, 32 * 1024);
90 extMan->AddByteRangeExtent(extMan->totalBytes - 32 * 1024, 32 * 1024);
91 // to be safe, add the rage from (mid_point - 32KB) to (mid_point + 32KB)
92 extMan->AddByteRangeExtent(extMan->totalBytes / 2 - 32 * 1024, 64 * 1024);
93}
94
5510e443 95static void
1bd2040a
A
96AddExtentsForUDF(class ExtentManager *extMan)
97{
98 off_t lastBlockAddr = extMan->totalBlocks - 1;
99
100 // Volume Recognization Sequence (VRS) starts at 32KB, usually less than 7 Volume Structure Descriptors (2KB each)
101 extMan->AddByteRangeExtent(32 * 1024, 14 * 1024);
102
103 // AVDP is on 256, 512, last block, last block - 256
104 extMan->AddBlockRangeExtent(256, 1);
105 extMan->AddBlockRangeExtent(512, 1);
106 extMan->AddBlockRangeExtent(lastBlockAddr, 1);
107 extMan->AddBlockRangeExtent(lastBlockAddr - 256, 1);
108
109 // to be safe, assume the device has 2KB block size and do it again
110 if (extMan->blockSize != 2048) {
111 off_t blockSize = 2048;
112 // AVDP is on 256, 512, last block, last block - 256
113 extMan->AddByteRangeExtent(256 * blockSize, blockSize);
114 extMan->AddByteRangeExtent(512 * blockSize, blockSize);
115 extMan->AddByteRangeExtent(extMan->totalBytes - blockSize, blockSize);
116 extMan->AddByteRangeExtent(extMan->totalBytes - 256 * blockSize, blockSize);
117 }
118}
119
5510e443 120static void
1bd2040a
A
121AddExtentsForUFS(class ExtentManager *extMan)
122{
123 // UFS super block is 8KB at offset 8KB
124 extMan->AddByteRangeExtent(8192, 8192);
125}
126
5510e443 127static void
1bd2040a
A
128AddExtentsForZFS(class ExtentManager *extMan)
129{
130 // ZFS needs the first 512KB and last 512KB for all the 4 disk labels
131 extMan->AddByteRangeExtent(0, 512 * 1024);
132 extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 1024, 512 * 1024);
133}
134
5510e443 135static void
1bd2040a
A
136AddExtentsForPartitions(class ExtentManager *extMan)
137{
138 // MBR (Master Boot Record) needs the first sector
139 // APM (Apple Partition Map) needs the second sector
140 // GPT (GUID Partition Table) needs the first 34 and last 33 sectors
141 extMan->AddByteRangeExtent(0, 512 * 34);
142 extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 33, 512 * 33);
143}
144
18e053d4
A
145static void
146AddExtentsForCoreStorage(class ExtentManager *extMan)
147{
148 // the CoreStorage VolumeHeader structures reside in the first/last 512 bytes of each PV
149 extMan->AddByteRangeExtent(0, 512);
150 extMan->AddByteRangeExtent(extMan->totalBytes - 512, 512);
151}
152
a71d6fbf
A
153static bool
154is_disk_device(const char *pathname) {
155 bool is_disk_dev = false;
156
157 if (strncmp(pathname, "/dev/disk", strlen("/dev/disk")) == 0) {
158 is_disk_dev = true;
159 } else if (strncmp(pathname, "/dev/rdisk", strlen("/dev/rdisk")) == 0) {
160 is_disk_dev = true;
161 }
162
163 return (is_disk_dev);
164}
165
166static int
167query_disk_info(int fd, char *uuid_str, int uuid_len, bool *is_internal) {
168 io_service_t obj;
169 io_iterator_t iter;
170 kern_return_t error;
171 CFBooleanRef removableRef;
172 CFStringRef uuidRef;
173 CFStringRef locationRef;
174 CFDictionaryRef protocolCharacteristics;
175 bool deviceInternal, mediaRemovable;
176 int result;
177 char disk_path[PATH_MAX];
178 char *disk_name;
179
180 result = EINVAL;
181 deviceInternal = false;
182 mediaRemovable = false;
183 removableRef = NULL;
184 protocolCharacteristics = NULL;
185 uuidRef = NULL;
186 obj = IO_OBJECT_NULL;
187 iter = IO_OBJECT_NULL;
188
189 // Fetch the path
190 if (fcntl(fd, F_GETPATH, disk_path) == -1) {
191 goto out;
192 }
193
194 // Make sure path begins with /dev/disk or /dev/rdisk
195 if (is_disk_device(disk_path) == false) {
196 goto out;
197 }
198
199 // Derive device name
200 disk_name = disk_path;
201 if(strncmp(disk_path, _PATH_DEV, strlen(_PATH_DEV)) == 0) {
202 // Skip over leading "/dev/"
203 disk_name += strlen(_PATH_DEV);
204 }
205 if (strncmp(disk_name, "r", strlen("r")) == 0) {
206 // Raw device, skip over leading "r"
207 disk_name += strlen("r");
208 }
209
210 // Get an iterator object
211 error = IOServiceGetMatchingServices(kIOMasterPortDefault,
212 IOBSDNameMatching(kIOMasterPortDefault, 0, disk_name), &iter);
213 if (error) {
214 os_log_error(OS_LOG_DEFAULT, "Warning, unable to obtain UUID info (iterator), device %s", disk_name);
215 goto out;
216 }
217
218 // Get the matching device object
219 obj = IOIteratorNext(iter);
220 if (!obj) {
221 os_log_error(OS_LOG_DEFAULT, "Warning, unable to obtain UUID info (dev obj), device %s", disk_name);
222 goto out;
223 }
224
225 // Get a protocol characteristics dictionary
226 protocolCharacteristics = (CFDictionaryRef) IORegistryEntrySearchCFProperty(obj,
227 kIOServicePlane,
228 CFSTR(kIOPropertyProtocolCharacteristicsKey),
229 kCFAllocatorDefault,
230 kIORegistryIterateRecursively|kIORegistryIterateParents);
231
232 if ((protocolCharacteristics == NULL) || (CFDictionaryGetTypeID() != CFGetTypeID(protocolCharacteristics)))
233 {
234 os_log_error(OS_LOG_DEFAULT, "Warning, failed to obtain UUID info (protocol characteristics), device %s\n", disk_name);
235 goto out;
236 }
237
238 // Check for kIOPropertyInternalKey
239 locationRef = (CFStringRef) CFDictionaryGetValue(protocolCharacteristics, CFSTR(kIOPropertyPhysicalInterconnectLocationKey));
240
241 if ((locationRef == NULL) || (CFStringGetTypeID() != CFGetTypeID(locationRef))) {
242 os_log_error(OS_LOG_DEFAULT, "Warning, failed to obtain UUID info (location), device %s\n", disk_name);
243 goto out;
244 }
245
246 if (CFEqual(locationRef, CFSTR(kIOPropertyInternalKey))) {
247 deviceInternal = true;
248 }
249
250 // Check for kIOMediaRemovableKey
251 removableRef = (CFBooleanRef)IORegistryEntrySearchCFProperty(obj,
252 kIOServicePlane,
253 CFSTR(kIOMediaRemovableKey),
254 kCFAllocatorDefault,
255 0);
256
257 if ((removableRef == NULL) || (CFBooleanGetTypeID() != CFGetTypeID(removableRef))) {
258 os_log_error(OS_LOG_DEFAULT, "Warning, unable to obtain UUID info (MediaRemovable key), device %s", disk_name);
259 goto out;
260 }
261
262 if (CFBooleanGetValue(removableRef)) {
263 mediaRemovable = true;
264 }
265
266 // is_internal ==> DeviceInternal && !MediaRemovable
267 if ((deviceInternal == true) && (mediaRemovable == false)) {
268 *is_internal = true;
269 } else {
270 *is_internal = false;
271 }
272
273 // Get the UUID
274 uuidRef = (CFStringRef)IORegistryEntrySearchCFProperty(obj,
275 kIOServicePlane,
276 CFSTR(kIOMediaUUIDKey),
277 kCFAllocatorDefault,
278 0);
279 if ((uuidRef == NULL) || (CFStringGetTypeID() != CFGetTypeID(uuidRef)))
280 {
281 os_log_error(OS_LOG_DEFAULT, "Warning, unable to obtain UUID info (MediaUUID key), device %s", disk_name);
282 goto out;
283 }
284
285 if (!CFStringGetCString(uuidRef, uuid_str, uuid_len, kCFStringEncodingASCII)) {
286 os_log_error(OS_LOG_DEFAULT, "Warning, unable to obtain UUID info (convert UUID), device %s", disk_name);
287 goto out;
288 }
289
290 // Success
291 result = 0;
292
293out:
294 if (obj != IO_OBJECT_NULL) {
295 IOObjectRelease(obj);
296 }
297 if (iter != IO_OBJECT_NULL) {
298 IOObjectRelease(iter);
299 }
300 if (removableRef != NULL) {
301 CFRelease(removableRef);
302 }
303 if (protocolCharacteristics != NULL) {
304 CFRelease(protocolCharacteristics);
305 }
306 if (uuidRef != NULL) {
307 CFRelease(uuidRef);
308 }
309
310 return (result);
311}
312
313static
314int run_xartutil(uuid_string_t uuid_str, bool is_internal)
315{
316 char external[2];
317 pid_t child_pid, wait_pid;
318 posix_spawn_file_actions_t fileActions;
319 bool haveFileActions = false;
320 int child_status = 0;
321 int result = 0;
322
323 char arg1[] = "xartutil";
324 char arg2[] = "--erase";
325 char arg4[] = "--is-external";
326
327 external[0] = (is_internal == false) ? '1' : '0';
328 external[1] = 0;
329
330 char *xartutil_argv[] = {arg1, arg2, uuid_str, arg4, external, NULL};
331
332 result = posix_spawn_file_actions_init(&fileActions);
333 if (result) {
334 os_log_error(OS_LOG_DEFAULT, "Warning, init xartutil file actions error: %d", result);
335 result = -1;
336 goto out;
337 }
338
339 haveFileActions = true;
340
341 // Redirect stdout & stderr (results not critical, so we ignore return values).
342 posix_spawn_file_actions_addopen(&fileActions, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
343 posix_spawn_file_actions_addopen(&fileActions, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
344
345 result = posix_spawn(&child_pid, "/usr/sbin/xartutil", &fileActions, NULL, xartutil_argv, NULL);
346
347 if (result) {
348 os_log_error(OS_LOG_DEFAULT, "Warning, unable to start xartutil, spawn error: %d", result);
349 result = -1;
350 goto out;
351 }
352
353 do {
354 wait_pid = waitpid(child_pid, &child_status, 0);
355 } while (wait_pid == -1 && errno == EINTR);
356
357 if (wait_pid == -1) {
358 os_log_error(OS_LOG_DEFAULT, "Warning, unable to start xartutil, waitpid error: %d", errno);
359 result = -1;
360 goto out;
361 }
362
363 if (WIFEXITED(child_status)) {
364 // xartutil terminated normally, get exit status
365 result = WEXITSTATUS(child_status);
366
367 if (result) {
368 os_log_error(OS_LOG_DEFAULT, "Warning, xartutil returned status %d", result);
369 }
370 } else {
371 result = -1;
372
373 if (WIFSIGNALED(child_status)) {
374 os_log_error(OS_LOG_DEFAULT, "Warning, xartutil terminated by signal: %u", WTERMSIG(child_status));
375 } else if (WIFSTOPPED(child_status)) {
376 os_log_error(OS_LOG_DEFAULT, "Warning, xartutil stopped by signal: %u", WSTOPSIG(child_status));
377 } else {
378 os_log_error(OS_LOG_DEFAULT, "Warning, xartutil terminated abnormally, status 0x%x", child_status);
379 }
380 }
381
382out:
383
384 if (haveFileActions) {
385 posix_spawn_file_actions_destroy(&fileActions);
386 }
387
388 return (result);
389}
390
1bd2040a
A
391extern "C" int
392wipefs_alloc(int fd, size_t block_size, wipefs_ctx *handle)
393{
394 int err = 0;
395 uint64_t numBlocks = 0;
396 uint32_t nativeBlockSize = 0;
397 off_t totalSizeInBytes = 0;
398 class ExtentManager *extMan = NULL;
399 struct stat sbuf = { 0 };
a71d6fbf
A
400 bool have_xartutil_info = false;
401 uuid_string_t uuid_str;
402 bool is_internal;
403 int uuid_err = 0;
1bd2040a
A
404
405 *handle = NULL;
a71d6fbf 406 uuid_str[0] = 0;
1bd2040a
A
407 (void)fstat(fd, &sbuf);
408 switch (sbuf.st_mode & S_IFMT) {
409 case S_IFCHR:
410 case S_IFBLK:
411 if (ioctl(fd, DKIOCGETBLOCKSIZE, (char *)&nativeBlockSize) < 0) {
412 err = errno;
413 goto labelExit;
414 }
415 if (ioctl(fd, DKIOCGETBLOCKCOUNT, (char *)&numBlocks) < 0) {
416 err = errno;
417 goto labelExit;
418 }
419 totalSizeInBytes = numBlocks * nativeBlockSize;
a71d6fbf
A
420
421 uuid_err = query_disk_info(fd, uuid_str, sizeof(uuid_str), &is_internal);
422 if (uuid_err == 0) {
423 have_xartutil_info = true;
424 }
1bd2040a
A
425 break;
426 case S_IFREG:
427 nativeBlockSize = sbuf.st_blksize;
428 numBlocks = sbuf.st_size / sbuf.st_blksize;
429 totalSizeInBytes = sbuf.st_size;
430 break;
431 default:
432 errno = EINVAL;
433 goto labelExit;
434 }
435 if (block_size == 0) {
436 block_size = nativeBlockSize;
437 }
438 if (block_size == 0 || totalSizeInBytes == 0) {
439 err = EINVAL;
440 goto labelExit;
441 }
442
443 try {
444 *handle = new __wipefs_ctx;
445 if (*handle == NULL) {
446 bad_alloc e;
447 throw e;
448 }
449
450 (*handle)->fd = fd;
451 extMan = &(*handle)->extMan;
452
453 extMan->Init(block_size, nativeBlockSize, totalSizeInBytes);
454 AddExtentsForFutureFS(extMan);
455 AddExtentsForHFS(extMan);
456 AddExtentsForMSDOS(extMan);
457 AddExtentsForNTFS(extMan);
458 AddExtentsForUDF(extMan);
459 AddExtentsForUFS(extMan);
460 AddExtentsForZFS(extMan);
461 AddExtentsForPartitions(extMan);
18e053d4 462 AddExtentsForCoreStorage(extMan);
a71d6fbf
A
463
464 (*handle)->have_xartutil_info = false;
465
466 if (have_xartutil_info == true) {
467 (*handle)->have_xartutil_info = true;
468 (*handle)->is_internal = is_internal;
469 memcpy((*handle)->uuid_str, uuid_str, sizeof(uuid_str));
470 }
1bd2040a
A
471 }
472 catch (bad_alloc &e) {
473 err = ENOMEM;
474 }
475 catch (...) { // currently only ENOMEM is possible
476 err = ENOMEM;
477 }
478
479 labelExit:
480 if (err != 0) {
481 wipefs_free(handle);
482 }
483 return err;
484} // wipefs_alloc
485
afa5f1bd
A
486extern "C" int
487wipefs_include_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks)
488{
489 int err = 0;
490 try {
491 handle->extMan.AddBlockRangeExtent(block_offset, nblocks);
492 }
493 catch (bad_alloc &e) {
494 err = ENOMEM;
495 }
496 catch (...) { // currently only ENOMEM is possible
497 err = ENOMEM;
498 }
499 return err;
500}
501
1bd2040a
A
502extern "C" int
503wipefs_except_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks)
504{
505 int err = 0;
506 try {
507 handle->extMan.RemoveBlockRangeExtent(block_offset, nblocks);
508 }
509 catch (bad_alloc &e) {
510 err = ENOMEM;
511 }
512 catch (...) { // currently only ENOMEM is possible
513 err = ENOMEM;
514 }
515 return err;
516}
517
518extern "C" int
519wipefs_wipe(wipefs_ctx handle)
520{
521 int err = 0;
522 uint8_t *bufZero = NULL;
523 ListExtIt curExt;
524 size_t bufSize;
e682b5d1
A
525 dk_extent_t extent;
526 dk_unmap_t unmap;
a71d6fbf 527 bool did_write = false;
1bd2040a 528
e682b5d1
A
529 memset(&extent, 0, sizeof(dk_extent_t));
530 extent.length = handle->extMan.totalBytes;
531
532 memset(&unmap, 0, sizeof(dk_unmap_t));
533 unmap.extents = &extent;
534 unmap.extentsCount = 1;
1bd2040a
A
535
536 //
537 // Don't bother to check the return value since this is mostly
538 // informational for the lower-level drivers.
539 //
e682b5d1 540 ioctl(handle->fd, DKIOCUNMAP, (caddr_t)&unmap);
1bd2040a
A
541
542
afa5f1bd 543 bufSize = 128 * 1024; // issue large I/O to get better performance
5510e443
A
544 if (handle->extMan.nativeBlockSize > bufSize) {
545 bufSize = handle->extMan.nativeBlockSize;
546 }
1bd2040a
A
547 bufZero = new uint8_t[bufSize];
548 bzero(bufZero, bufSize);
549
550 off_t byteOffset, totalBytes;
551 size_t numBytes, numBytesToWrite, blockSize;
552
553 blockSize = handle->extMan.blockSize;
554 totalBytes = handle->extMan.totalBytes;
555 // write zero to all extents
556 for (curExt = handle->extMan.extentList.begin(); curExt != handle->extMan.extentList.end(); curExt++) {
557 byteOffset = curExt->blockAddr * blockSize;
558 numBytes = curExt->numBlocks * blockSize;
559 // make both offset and numBytes on native block boundary
560 if (byteOffset % handle->extMan.nativeBlockSize != 0 ||
561 numBytes % handle->extMan.nativeBlockSize != 0) {
562 size_t nativeBlockSize = handle->extMan.nativeBlockSize;
563 off_t newOffset, newEndOffset;
564 newOffset = byteOffset / nativeBlockSize * nativeBlockSize;
a71d6fbf 565 newEndOffset = wipefs_roundup(byteOffset + numBytes, nativeBlockSize);
1bd2040a
A
566 byteOffset = newOffset;
567 numBytes = newEndOffset - newOffset;
568 }
569 if (byteOffset + (off_t)numBytes > totalBytes) {
570 numBytes = totalBytes - byteOffset;
571 }
572 while (numBytes > 0) {
573 numBytesToWrite = min(numBytes, bufSize);
574 if (pwrite(handle->fd, bufZero, numBytesToWrite, byteOffset) != (ssize_t)numBytesToWrite) {
575 err = errno;
576 goto labelExit;
577 }
578 numBytes -= numBytesToWrite;
579 byteOffset += numBytesToWrite;
a71d6fbf
A
580
581 if (did_write == false) {
582 did_write = true;
583 }
1bd2040a 584 }
a71d6fbf 585 }
1bd2040a
A
586
587 labelExit:
18e053d4
A
588
589 (void)ioctl(handle->fd, DKIOCSYNCHRONIZECACHE);
1bd2040a
A
590 if (bufZero != NULL)
591 delete[] bufZero;
a71d6fbf
A
592
593 if ((did_write == true) && (handle->have_xartutil_info == true)) {
594 // We wrote some zero bytes and have UUID info, notify xART now.
595 run_xartutil(handle->uuid_str, handle->is_internal);
596 }
597
1bd2040a
A
598 return err;
599} // wipefs_wipe
600
601extern "C" void
602wipefs_free(wipefs_ctx *handle)
603{
604 if (*handle != NULL) {
605 delete *handle;
606 *handle = NULL;
607 }
608}