2 * Copyright (c) 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
30 #include <sys/ioctl.h>
33 #include <sys/syslimits.h>
34 #include <uuid/uuid.h>
38 #include <IOKit/IOKitLib.h>
39 #include <IOKit/storage/IOMedia.h>
40 #include <IOKit/storage/IOStorageProtocolCharacteristics.h>
41 #include <CoreFoundation/CFNumber.h>
44 #include "ExtentManager.h"
47 #define wipefs_roundup(x, y) ((((x)+((y)-1))/(y))*(y))
51 class ExtentManager extMan
;
53 // xartutil information
54 bool have_xartutil_info
;
55 uuid_string_t uuid_str
;
60 AddExtentsForFutureFS(class ExtentManager
*extMan
)
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
);
70 AddExtentsForHFS(class ExtentManager
*extMan
)
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);
79 AddExtentsForMSDOS(class ExtentManager
*extMan
)
81 // MSDOS needs the first block (in theory, up to 32KB)
82 extMan
->AddByteRangeExtent(0, 32 * 1024);
86 AddExtentsForNTFS(class ExtentManager
*extMan
)
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);
96 AddExtentsForUDF(class ExtentManager
*extMan
)
98 off_t lastBlockAddr
= extMan
->totalBlocks
- 1;
100 // Volume Recognization Sequence (VRS) starts at 32KB, usually less than 7 Volume Structure Descriptors (2KB each)
101 extMan
->AddByteRangeExtent(32 * 1024, 14 * 1024);
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);
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
);
121 AddExtentsForUFS(class ExtentManager
*extMan
)
123 // UFS super block is 8KB at offset 8KB
124 extMan
->AddByteRangeExtent(8192, 8192);
128 AddExtentsForZFS(class ExtentManager
*extMan
)
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);
136 AddExtentsForPartitions(class ExtentManager
*extMan
)
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);
146 AddExtentsForCoreStorage(class ExtentManager
*extMan
)
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);
154 is_disk_device(const char *pathname
) {
155 bool is_disk_dev
= false;
157 if (strncmp(pathname
, "/dev/disk", strlen("/dev/disk")) == 0) {
159 } else if (strncmp(pathname
, "/dev/rdisk", strlen("/dev/rdisk")) == 0) {
163 return (is_disk_dev
);
167 query_disk_info(int fd
, char *uuid_str
, int uuid_len
, bool *is_internal
) {
171 CFBooleanRef removableRef
;
173 CFStringRef locationRef
;
174 CFDictionaryRef protocolCharacteristics
;
175 bool deviceInternal
, mediaRemovable
;
177 char disk_path
[PATH_MAX
];
181 deviceInternal
= false;
182 mediaRemovable
= false;
184 protocolCharacteristics
= NULL
;
186 obj
= IO_OBJECT_NULL
;
187 iter
= IO_OBJECT_NULL
;
190 if (fcntl(fd
, F_GETPATH
, disk_path
) == -1) {
194 // Make sure path begins with /dev/disk or /dev/rdisk
195 if (is_disk_device(disk_path
) == false) {
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
);
205 if (strncmp(disk_name
, "r", strlen("r")) == 0) {
206 // Raw device, skip over leading "r"
207 disk_name
+= strlen("r");
210 // Get an iterator object
211 error
= IOServiceGetMatchingServices(kIOMasterPortDefault
,
212 IOBSDNameMatching(kIOMasterPortDefault
, 0, disk_name
), &iter
);
214 os_log_error(OS_LOG_DEFAULT
, "Warning, unable to obtain UUID info (iterator), device %s", disk_name
);
218 // Get the matching device object
219 obj
= IOIteratorNext(iter
);
221 os_log_error(OS_LOG_DEFAULT
, "Warning, unable to obtain UUID info (dev obj), device %s", disk_name
);
225 // Get a protocol characteristics dictionary
226 protocolCharacteristics
= (CFDictionaryRef
) IORegistryEntrySearchCFProperty(obj
,
228 CFSTR(kIOPropertyProtocolCharacteristicsKey
),
230 kIORegistryIterateRecursively
|kIORegistryIterateParents
);
232 if ((protocolCharacteristics
== NULL
) || (CFDictionaryGetTypeID() != CFGetTypeID(protocolCharacteristics
)))
234 os_log_error(OS_LOG_DEFAULT
, "Warning, failed to obtain UUID info (protocol characteristics), device %s\n", disk_name
);
238 // Check for kIOPropertyInternalKey
239 locationRef
= (CFStringRef
) CFDictionaryGetValue(protocolCharacteristics
, CFSTR(kIOPropertyPhysicalInterconnectLocationKey
));
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
);
246 if (CFEqual(locationRef
, CFSTR(kIOPropertyInternalKey
))) {
247 deviceInternal
= true;
250 // Check for kIOMediaRemovableKey
251 removableRef
= (CFBooleanRef
)IORegistryEntrySearchCFProperty(obj
,
253 CFSTR(kIOMediaRemovableKey
),
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
);
262 if (CFBooleanGetValue(removableRef
)) {
263 mediaRemovable
= true;
266 // is_internal ==> DeviceInternal && !MediaRemovable
267 if ((deviceInternal
== true) && (mediaRemovable
== false)) {
270 *is_internal
= false;
274 uuidRef
= (CFStringRef
)IORegistryEntrySearchCFProperty(obj
,
276 CFSTR(kIOMediaUUIDKey
),
279 if ((uuidRef
== NULL
) || (CFStringGetTypeID() != CFGetTypeID(uuidRef
)))
281 os_log_error(OS_LOG_DEFAULT
, "Warning, unable to obtain UUID info (MediaUUID key), device %s", disk_name
);
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
);
294 if (obj
!= IO_OBJECT_NULL
) {
295 IOObjectRelease(obj
);
297 if (iter
!= IO_OBJECT_NULL
) {
298 IOObjectRelease(iter
);
300 if (removableRef
!= NULL
) {
301 CFRelease(removableRef
);
303 if (protocolCharacteristics
!= NULL
) {
304 CFRelease(protocolCharacteristics
);
306 if (uuidRef
!= NULL
) {
314 int run_xartutil(uuid_string_t uuid_str
, bool is_internal
)
317 pid_t child_pid
, wait_pid
;
318 posix_spawn_file_actions_t fileActions
;
319 bool haveFileActions
= false;
320 int child_status
= 0;
323 char arg1
[] = "xartutil";
324 char arg2
[] = "--erase";
325 char arg4
[] = "--is-external";
327 external
[0] = (is_internal
== false) ? '1' : '0';
330 char *xartutil_argv
[] = {arg1
, arg2
, uuid_str
, arg4
, external
, NULL
};
332 result
= posix_spawn_file_actions_init(&fileActions
);
334 os_log_error(OS_LOG_DEFAULT
, "Warning, init xartutil file actions error: %d", result
);
339 haveFileActions
= true;
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);
345 result
= posix_spawn(&child_pid
, "/usr/sbin/xartutil", &fileActions
, NULL
, xartutil_argv
, NULL
);
348 os_log_error(OS_LOG_DEFAULT
, "Warning, unable to start xartutil, spawn error: %d", result
);
354 wait_pid
= waitpid(child_pid
, &child_status
, 0);
355 } while (wait_pid
== -1 && errno
== EINTR
);
357 if (wait_pid
== -1) {
358 os_log_error(OS_LOG_DEFAULT
, "Warning, unable to start xartutil, waitpid error: %d", errno
);
363 if (WIFEXITED(child_status
)) {
364 // xartutil terminated normally, get exit status
365 result
= WEXITSTATUS(child_status
);
368 os_log_error(OS_LOG_DEFAULT
, "Warning, xartutil returned status %d", result
);
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
));
378 os_log_error(OS_LOG_DEFAULT
, "Warning, xartutil terminated abnormally, status 0x%x", child_status
);
384 if (haveFileActions
) {
385 posix_spawn_file_actions_destroy(&fileActions
);
392 wipefs_alloc(int fd
, size_t block_size
, wipefs_ctx
*handle
)
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 };
400 bool have_xartutil_info
= false;
401 uuid_string_t uuid_str
;
407 (void)fstat(fd
, &sbuf
);
408 switch (sbuf
.st_mode
& S_IFMT
) {
411 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, (char *)&nativeBlockSize
) < 0) {
415 if (ioctl(fd
, DKIOCGETBLOCKCOUNT
, (char *)&numBlocks
) < 0) {
419 totalSizeInBytes
= numBlocks
* nativeBlockSize
;
421 uuid_err
= query_disk_info(fd
, uuid_str
, sizeof(uuid_str
), &is_internal
);
423 have_xartutil_info
= true;
427 nativeBlockSize
= sbuf
.st_blksize
;
428 numBlocks
= sbuf
.st_size
/ sbuf
.st_blksize
;
429 totalSizeInBytes
= sbuf
.st_size
;
435 if (block_size
== 0) {
436 block_size
= nativeBlockSize
;
438 if (block_size
== 0 || totalSizeInBytes
== 0) {
444 *handle
= new __wipefs_ctx
;
445 if (*handle
== NULL
) {
451 extMan
= &(*handle
)->extMan
;
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
);
462 AddExtentsForCoreStorage(extMan
);
464 (*handle
)->have_xartutil_info
= false;
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
));
472 catch (bad_alloc
&e
) {
475 catch (...) { // currently only ENOMEM is possible
487 wipefs_include_blocks(wipefs_ctx handle
, off_t block_offset
, off_t nblocks
)
491 handle
->extMan
.AddBlockRangeExtent(block_offset
, nblocks
);
493 catch (bad_alloc
&e
) {
496 catch (...) { // currently only ENOMEM is possible
503 wipefs_except_blocks(wipefs_ctx handle
, off_t block_offset
, off_t nblocks
)
507 handle
->extMan
.RemoveBlockRangeExtent(block_offset
, nblocks
);
509 catch (bad_alloc
&e
) {
512 catch (...) { // currently only ENOMEM is possible
519 wipefs_wipe(wipefs_ctx handle
)
522 uint8_t *bufZero
= NULL
;
527 bool did_write
= false;
529 memset(&extent
, 0, sizeof(dk_extent_t
));
530 extent
.length
= handle
->extMan
.totalBytes
;
532 memset(&unmap
, 0, sizeof(dk_unmap_t
));
533 unmap
.extents
= &extent
;
534 unmap
.extentsCount
= 1;
537 // Don't bother to check the return value since this is mostly
538 // informational for the lower-level drivers.
540 ioctl(handle
->fd
, DKIOCUNMAP
, (caddr_t
)&unmap
);
543 bufSize
= 128 * 1024; // issue large I/O to get better performance
544 if (handle
->extMan
.nativeBlockSize
> bufSize
) {
545 bufSize
= handle
->extMan
.nativeBlockSize
;
547 bufZero
= new uint8_t[bufSize
];
548 bzero(bufZero
, bufSize
);
550 off_t byteOffset
, totalBytes
;
551 size_t numBytes
, numBytesToWrite
, blockSize
;
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
;
565 newEndOffset
= wipefs_roundup(byteOffset
+ numBytes
, nativeBlockSize
);
566 byteOffset
= newOffset
;
567 numBytes
= newEndOffset
- newOffset
;
569 if (byteOffset
+ (off_t
)numBytes
> totalBytes
) {
570 numBytes
= totalBytes
- byteOffset
;
572 while (numBytes
> 0) {
573 numBytesToWrite
= min(numBytes
, bufSize
);
574 if (pwrite(handle
->fd
, bufZero
, numBytesToWrite
, byteOffset
) != (ssize_t
)numBytesToWrite
) {
578 numBytes
-= numBytesToWrite
;
579 byteOffset
+= numBytesToWrite
;
581 if (did_write
== false) {
589 (void)ioctl(handle
->fd
, DKIOCSYNCHRONIZECACHE
);
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
);
602 wipefs_free(wipefs_ctx
*handle
)
604 if (*handle
!= NULL
) {