]> git.saurik.com Git - apple/libutil.git/blob - wipefs.cpp
libutil-47.30.1.tar.gz
[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 <unistd.h>
29 #include <sys/uio.h>
30 #include <sys/ioctl.h>
31 #include <sys/disk.h>
32 #include <sys/stat.h>
33 #include <sys/syslimits.h>
34 #include <string.h>
35 #include <spawn.h>
36 #include <os/log.h>
37
38 #include "ExtentManager.h"
39 #include "wipefs.h"
40
41 #define wipefs_roundup(x, y) ((((x)+((y)-1))/(y))*(y))
42
43 struct __wipefs_ctx {
44 int fd;
45 class ExtentManager extMan;
46
47 // xartutil information
48 char *diskname;
49 };
50
51 static void
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
61 static void
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
70 static void
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
77 static void
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
87 static void
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
112 static void
113 AddExtentsForUFS(class ExtentManager *extMan)
114 {
115 // UFS super block is 8KB at offset 8KB
116 extMan->AddByteRangeExtent(8192, 8192);
117 }
118
119 static void
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
127 static void
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
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
145 static char *
146 query_disk_info(int fd) {
147 char disk_path[PATH_MAX];
148 char *disk_name;
149
150 // Fetch the path
151 if (fcntl(fd, F_GETPATH, disk_path) == -1) {
152 return (NULL);
153 }
154
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;
160 } else {
161 // Skip over the '/'.
162 disk_name++;
163 }
164
165 if (*disk_name == 'r') {
166 // Raw device; skip over leading 'r'.
167 disk_name++;
168 }
169
170 // ...and make sure it's really a disk.
171 if (strncmp(disk_name, "disk", strlen("disk")) != 0) {
172 return (NULL);
173 }
174
175 return (strdup(disk_name));
176 }
177
178 static
179 int run_xartutil(char *const diskname)
180 {
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";
188 char arg2[] = "--erase-disk";
189
190 char *const xartutil_argv[] = {arg1, arg2, diskname, NULL};
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
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 };
260 char *diskname = NULL;
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;
276
277 diskname = query_disk_info(fd);
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;
304 (*handle)->diskname = NULL;
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);
316 AddExtentsForCoreStorage(extMan);
317
318 (*handle)->diskname = diskname;
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) {
329 if (diskname != NULL)
330 free(diskname);
331 wipefs_free(handle);
332 }
333 return err;
334 } // wipefs_alloc
335
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
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;
375 dk_extent_t extent;
376 dk_unmap_t unmap;
377
378 if (handle->diskname != NULL) {
379 // Remove this disk's entry from the xART.
380 run_xartutil(handle->diskname);
381 }
382
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;
389
390 //
391 // Don't bother to check the return value since this is mostly
392 // informational for the lower-level drivers.
393 //
394 ioctl(handle->fd, DKIOCUNMAP, (caddr_t)&unmap);
395
396 bufSize = 128 * 1024; // issue large I/O to get better performance
397 if (handle->extMan.nativeBlockSize > bufSize) {
398 bufSize = handle->extMan.nativeBlockSize;
399 }
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;
418 newEndOffset = wipefs_roundup(byteOffset + numBytes, nativeBlockSize);
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 }
434 }
435
436 labelExit:
437
438 (void)ioctl(handle->fd, DKIOCSYNCHRONIZECACHE);
439 if (bufZero != NULL)
440 delete[] bufZero;
441
442 return err;
443 } // wipefs_wipe
444
445 extern "C" void
446 wipefs_free(wipefs_ctx *handle)
447 {
448 if (*handle != NULL) {
449 char *diskname;
450
451 if ((diskname = (*handle)->diskname) != NULL)
452 free(diskname);
453 delete *handle;
454 *handle = NULL;
455 }
456 }