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>
38 #include "ExtentManager.h"
41 #define wipefs_roundup(x, y) ((((x)+((y)-1))/(y))*(y))
45 class ExtentManager extMan
;
47 // xartutil information
52 AddExtentsForFutureFS(class ExtentManager
*extMan
)
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
);
62 AddExtentsForHFS(class ExtentManager
*extMan
)
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);
71 AddExtentsForMSDOS(class ExtentManager
*extMan
)
73 // MSDOS needs the first block (in theory, up to 32KB)
74 extMan
->AddByteRangeExtent(0, 32 * 1024);
78 AddExtentsForNTFS(class ExtentManager
*extMan
)
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);
88 AddExtentsForUDF(class ExtentManager
*extMan
)
90 off_t lastBlockAddr
= extMan
->totalBlocks
- 1;
92 // Volume Recognization Sequence (VRS) starts at 32KB, usually less than 7 Volume Structure Descriptors (2KB each)
93 extMan
->AddByteRangeExtent(32 * 1024, 14 * 1024);
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);
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
);
113 AddExtentsForUFS(class ExtentManager
*extMan
)
115 // UFS super block is 8KB at offset 8KB
116 extMan
->AddByteRangeExtent(8192, 8192);
120 AddExtentsForZFS(class ExtentManager
*extMan
)
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);
128 AddExtentsForPartitions(class ExtentManager
*extMan
)
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);
138 AddExtentsForCoreStorage(class ExtentManager
*extMan
)
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);
146 query_disk_info(int fd
) {
147 char disk_path
[PATH_MAX
];
151 if (fcntl(fd
, F_GETPATH
, disk_path
) == -1) {
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
;
161 // Skip over the '/'.
165 if (*disk_name
== 'r') {
166 // Raw device; skip over leading 'r'.
170 // ...and make sure it's really a disk.
171 if (strncmp(disk_name
, "disk", strlen("disk")) != 0) {
175 return (strdup(disk_name
));
179 int run_xartutil(char *const diskname
)
181 pid_t child_pid
, wait_pid
;
182 posix_spawn_file_actions_t fileActions
;
183 bool haveFileActions
= false;
184 int child_status
= 0;
187 char arg1
[] = "xartutil";
188 char arg2
[] = "--erase-disk";
190 char *const xartutil_argv
[] = {arg1
, arg2
, diskname
, NULL
};
192 result
= posix_spawn_file_actions_init(&fileActions
);
194 os_log_error(OS_LOG_DEFAULT
, "Warning, init xartutil file actions error: %d", result
);
199 haveFileActions
= true;
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);
205 result
= posix_spawn(&child_pid
, "/usr/sbin/xartutil", &fileActions
, NULL
, xartutil_argv
, NULL
);
208 os_log_error(OS_LOG_DEFAULT
, "Warning, unable to start xartutil, spawn error: %d", result
);
214 wait_pid
= waitpid(child_pid
, &child_status
, 0);
215 } while (wait_pid
== -1 && errno
== EINTR
);
217 if (wait_pid
== -1) {
218 os_log_error(OS_LOG_DEFAULT
, "Warning, unable to start xartutil, waitpid error: %d", errno
);
223 if (WIFEXITED(child_status
)) {
224 // xartutil terminated normally, get exit status
225 result
= WEXITSTATUS(child_status
);
228 os_log_error(OS_LOG_DEFAULT
, "Warning, xartutil returned status %d", result
);
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
));
238 os_log_error(OS_LOG_DEFAULT
, "Warning, xartutil terminated abnormally, status 0x%x", child_status
);
244 if (haveFileActions
) {
245 posix_spawn_file_actions_destroy(&fileActions
);
252 wipefs_alloc(int fd
, size_t block_size
, wipefs_ctx
*handle
)
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 };
260 char *diskname
= NULL
;
263 (void)fstat(fd
, &sbuf
);
264 switch (sbuf
.st_mode
& S_IFMT
) {
267 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, (char *)&nativeBlockSize
) < 0) {
271 if (ioctl(fd
, DKIOCGETBLOCKCOUNT
, (char *)&numBlocks
) < 0) {
275 totalSizeInBytes
= numBlocks
* nativeBlockSize
;
277 diskname
= query_disk_info(fd
);
280 nativeBlockSize
= sbuf
.st_blksize
;
281 numBlocks
= sbuf
.st_size
/ sbuf
.st_blksize
;
282 totalSizeInBytes
= sbuf
.st_size
;
288 if (block_size
== 0) {
289 block_size
= nativeBlockSize
;
291 if (block_size
== 0 || totalSizeInBytes
== 0) {
297 *handle
= new __wipefs_ctx
;
298 if (*handle
== NULL
) {
304 (*handle
)->diskname
= NULL
;
305 extMan
= &(*handle
)->extMan
;
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
);
316 AddExtentsForCoreStorage(extMan
);
318 (*handle
)->diskname
= diskname
;
320 catch (bad_alloc
&e
) {
323 catch (...) { // currently only ENOMEM is possible
329 if (diskname
!= NULL
)
337 wipefs_include_blocks(wipefs_ctx handle
, off_t block_offset
, off_t nblocks
)
341 handle
->extMan
.AddBlockRangeExtent(block_offset
, nblocks
);
343 catch (bad_alloc
&e
) {
346 catch (...) { // currently only ENOMEM is possible
353 wipefs_except_blocks(wipefs_ctx handle
, off_t block_offset
, off_t nblocks
)
357 handle
->extMan
.RemoveBlockRangeExtent(block_offset
, nblocks
);
359 catch (bad_alloc
&e
) {
362 catch (...) { // currently only ENOMEM is possible
369 wipefs_wipe(wipefs_ctx handle
)
372 uint8_t *bufZero
= NULL
;
378 if (handle
->diskname
!= NULL
) {
379 // Remove this disk's entry from the xART.
380 run_xartutil(handle
->diskname
);
383 memset(&extent
, 0, sizeof(dk_extent_t
));
384 extent
.length
= handle
->extMan
.totalBytes
;
386 memset(&unmap
, 0, sizeof(dk_unmap_t
));
387 unmap
.extents
= &extent
;
388 unmap
.extentsCount
= 1;
391 // Don't bother to check the return value since this is mostly
392 // informational for the lower-level drivers.
394 ioctl(handle
->fd
, DKIOCUNMAP
, (caddr_t
)&unmap
);
396 bufSize
= 128 * 1024; // issue large I/O to get better performance
397 if (handle
->extMan
.nativeBlockSize
> bufSize
) {
398 bufSize
= handle
->extMan
.nativeBlockSize
;
400 bufZero
= new uint8_t[bufSize
];
401 bzero(bufZero
, bufSize
);
403 off_t byteOffset
, totalBytes
;
404 size_t numBytes
, numBytesToWrite
, blockSize
;
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
;
418 newEndOffset
= wipefs_roundup(byteOffset
+ numBytes
, nativeBlockSize
);
419 byteOffset
= newOffset
;
420 numBytes
= newEndOffset
- newOffset
;
422 if (byteOffset
+ (off_t
)numBytes
> totalBytes
) {
423 numBytes
= totalBytes
- byteOffset
;
425 while (numBytes
> 0) {
426 numBytesToWrite
= min(numBytes
, bufSize
);
427 if (pwrite(handle
->fd
, bufZero
, numBytesToWrite
, byteOffset
) != (ssize_t
)numBytesToWrite
) {
431 numBytes
-= numBytesToWrite
;
432 byteOffset
+= numBytesToWrite
;
438 (void)ioctl(handle
->fd
, DKIOCSYNCHRONIZECACHE
);
446 wipefs_free(wipefs_ctx
*handle
)
448 if (*handle
!= NULL
) {
451 if ((diskname
= (*handle
)->diskname
) != NULL
)