]> git.saurik.com Git - apple/hfs.git/blob - livefiles_cs_plugin/lf_cs_vfsops.c
hfs-556.41.1.tar.gz
[apple/hfs.git] / livefiles_cs_plugin / lf_cs_vfsops.c
1 //
2 // Copyright (c) 2019-2019 Apple Inc. All rights reserved.
3 //
4 // lf_cs_vfsops.c - Implemenents routines for handling VFS operations for
5 // livefiles Apple_CoreStorage plugin.
6 //
7
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <stdbool.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <sys/ioctl.h>
16 #include <sys/disk.h>
17 #include <sys/types.h>
18 #include <sys/uio.h>
19 #include <unistd.h>
20
21 #include <CoreFoundation/CoreFoundation.h>
22 #include <IOKit/IOKitLib.h>
23 #include <IOKit/storage/IOMedia.h>
24 #include <MediaKit/GPTTypes.h>
25 #include <IOKit/IOBSD.h>
26 #include <IOKit/IOTypes.h>
27
28 #include "lf_cs_checksum.h"
29 #include "lf_cs_disk_format.h"
30 #include "lf_cs_logging.h"
31 #include "lf_cs.h"
32
33 #define VALUE_UNSPECIFIED ((uint64_t)-1)
34
35 //
36 // Verify the given data checksum. If no explicit 'chk' area the cksum
37 // value to match against is assumed to reside at the beginning of the
38 // provided buffer (typically a 'metadata_header_t') and is excluded from
39 // the cksum calculation itself.
40 //
41 static unsigned
42 cs_verify_cksum(cksum_alg_t alg, const void *ptr, size_t len,
43 uint8_t *chk)
44 {
45 uint8_t c[MAX_CKSUM_NBYTES];
46
47 if (!(alg == CKSUM_NONE) && !(alg == CKSUM_ALG_CRC_32)) {
48 return CS_STATUS_INVALID;
49 }
50
51 cksum_init(alg, c);
52 if (chk == NULL) {
53 cksum(alg, (chk = (uint8_t *)ptr) + MAX_CKSUM_NBYTES,
54 len - MAX_CKSUM_NBYTES, c);
55 } else {
56 cksum(alg, ptr, len, c);
57 }
58
59 if (memcmp(c, chk, MAX_CKSUM_NBYTES)) {
60 return CS_STATUS_CKSUM;
61 }
62
63 return CS_STATUS_OK;
64 }
65
66 //
67 // These block types must belong to the current transaction; all
68 // other types can be referenced from older transactions.
69 //
70 #define CS_CHKPOINT_BLK(_b) \
71 ((_b)->mh_blk_type >= BLK_TYPE_VOL_HEADER &&\
72 (_b)->mh_blk_type <= BLK_TYPE_SUT)
73
74
75 //
76 // These block types are the only ones with Secure Deletion content (LVF).
77 //
78 #define CS_DSDLIST_BLK(_b) \
79 ((_b)->mh_blk_type == BLK_TYPE_LV_FAMILY_SUPERBLOCK ||\
80 (_b)->mh_blk_type == BLK_TYPE_LV_FAMILY_XATTR)
81
82 //
83 // cs_verify_blkhdr - Verify standard metadata header fields.
84 //
85 // Each block begins with a 'metadata_header_t', which often has known/expected
86 // values.
87 //
88 static unsigned
89 cs_verify_blkhdr(const metadata_header_t *hdr, uint16_t alg,
90 uint8_t type, uint8_t subtype, uint64_t vaddr,
91 uint64_t laddr, uint64_t txg, uint32_t blksz)
92 {
93 unsigned status;
94
95 if (alg != CKSUM_NONE && (status = cs_verify_cksum((cksum_alg_t)alg,
96 hdr, blksz, NULL)) != CS_STATUS_OK) {
97 return status;
98 }
99
100 if (type != BLK_TYPE_UNKNOWN && hdr->mh_blk_type != type) {
101 return CS_STATUS_BLKTYPE;
102 }
103
104 if (subtype != BLK_SUBTYPE_UNKNOWN && hdr->mh_blk_subtype != subtype) {
105 return CS_STATUS_INVALID;
106 }
107
108 if (vaddr != VALUE_UNSPECIFIED && hdr->mh_vaddr != vaddr) {
109 return CS_STATUS_ADDRESS;
110 }
111
112 if (laddr != VALUE_UNSPECIFIED && hdr->mh_laddr != laddr) {
113 return CS_STATUS_ADDRESS;
114 }
115
116 if (txg != VALUE_UNSPECIFIED) {
117 if (CS_CHKPOINT_BLK(hdr) && hdr->mh_txg_id != txg)
118 return CS_STATUS_TXG;
119 else if (hdr->mh_txg_id > txg)
120 return CS_STATUS_TXG;
121 }
122
123 if (blksz != 0 && hdr->mh_blk_size != blksz) {
124 return CS_STATUS_INVALID;
125 }
126
127 if (!CS_DSDLIST_BLK(hdr) &&
128 (hdr->mh_blk_flags & BLK_FLAG_IN_DSD_LIST)) {
129 return CS_STATUS_INVALID;
130 }
131
132 return CS_STATUS_OK;
133 }
134
135 //
136 // cs_verify_versions - Verify volume header version.
137 //
138 static unsigned
139 cs_verify_versions(const metadata_header_t *hdr)
140 {
141 if (hdr != NULL && hdr->mh_format_version !=
142 CORESTORAGE_FORMAT_VERSION) {
143 return CS_INFO_VERSIONITIS | CS_STATUS_NOTCS;
144 }
145
146 if (hdr != NULL &&
147 hdr->mh_blk_type == BLK_TYPE_VOL_HEADER &&
148 ((dk_vol_header_t *)hdr)->vh_endianness !=
149 BYTE_ORDER_MARK) {
150 return CS_INFO_VERSIONITIS | CS_STATUS_NOTCS;
151 }
152
153 return CS_STATUS_OK;
154 }
155
156 //
157 // cs_verify_vh - Read and verify the Volume Headers (first/last 512-bytes of
158 // the PV).
159 //
160 static unsigned
161 cs_verify_vh(dk_vol_header_t *hdr, uint32_t blksz)
162 {
163 unsigned status;
164
165 status = cs_verify_blkhdr(&hdr->vh_header, hdr->vh_cksum_alg,
166 BLK_TYPE_VOL_HEADER,
167 BLK_SUBTYPE_NO_SUBTYPE, 0, 0,
168 VALUE_UNSPECIFIED, VOL_HEADER_NBYTES);
169 if (status != CS_STATUS_OK) {
170 return status;
171 }
172
173 status = cs_verify_versions(&hdr->vh_header);
174 if (status != CS_STATUS_OK) {
175 return status;
176 }
177
178 if (!hdr->vh_num_labels || hdr->vh_num_labels > MAX_DISK_LABELS) {
179 return CS_STATUS_INVALID;
180 }
181
182 if (hdr->vh_label_max_nbytes % blksz != 0) {
183 return CS_STATUS_INVALID;
184 }
185
186 if (hdr->vh_blksz == 0 || hdr->vh_blksz % blksz != 0) {
187 return CS_STATUS_INVALID;
188 }
189
190 if (hdr->vh_pv_nbytes % blksz != 0 ||
191 hdr->vh_pv_nbytes < CS_ALIGN(VOL_HEADER_NBYTES,
192 blksz, true) * NUM_VOL_HEADERS) {
193 return CS_STATUS_INVALID;
194 }
195
196 if (hdr->vh_pv_resize != 0 && (hdr->vh_pv_resize % blksz != 0 ||
197 hdr->vh_pv_resize < CS_ALIGN(VOL_HEADER_NBYTES,
198 blksz, true) * NUM_VOL_HEADERS)) {
199 return CS_STATUS_INVALID;
200 }
201
202 if (hdr->vh_old_pv_nbytes != 0 &&
203 (hdr->vh_old_pv_nbytes % blksz != 0 ||
204 hdr->vh_old_pv_nbytes < CS_ALIGN(VOL_HEADER_NBYTES,
205 blksz, true) * NUM_VOL_HEADERS)) {
206 return CS_STATUS_INVALID;
207 }
208
209 return CS_STATUS_OK;
210 }
211
212 //
213 // cs_get_content_hint_for_pv - get content hint of the disk from IOReg. We
214 // read the IOReg to check if the passed disk has
215 // a Apple_CoreStorage content hint.
216 //
217 static bool
218 cs_get_content_hint_for_pv(struct stat *st)
219 {
220 bool has_cs_hint;
221 int dev_major, dev_minor;
222 CFMutableDictionaryRef matching;
223
224 has_cs_hint = false;
225 if ((matching = IOServiceMatching(kIOMediaClass))) {
226 CFTypeRef str;
227 io_service_t service;
228
229 if ((matching = IOServiceMatching(kIOMediaClass))) {
230 CFNumberRef num_ref;
231
232 dev_major = major(st->st_rdev);
233 dev_minor = minor(st->st_rdev);
234
235 num_ref = CFNumberCreate(kCFAllocatorDefault,
236 kCFNumberIntType, &dev_major);
237 if (num_ref) {
238 CFDictionarySetValue(matching,
239 CFSTR(kIOBSDMajorKey),
240 num_ref);
241 CFRelease(num_ref);
242 }
243
244 num_ref = CFNumberCreate(kCFAllocatorDefault,
245 kCFNumberIntType, &dev_minor);
246 if (num_ref) {
247 CFDictionarySetValue(matching,
248 CFSTR(kIOBSDMinorKey),
249 num_ref);
250 CFRelease(num_ref);
251 }
252
253 service = IOServiceGetMatchingService(
254 kIOMasterPortDefault, matching);
255 if (!service) {
256 goto out;
257 }
258
259 if ((str = IORegistryEntryCreateCFProperty( service,
260 CFSTR(kIOMediaContentHintKey),
261 kCFAllocatorDefault, 0)) !=
262 NULL) {
263
264 has_cs_hint = CFStringCompare((CFStringRef)str,
265 CFSTR(APPLE_CORESTORAGE_UUID), 0) ==
266 kCFCompareEqualTo;
267
268 CFRelease(str);
269 IOObjectRelease(service);
270 }
271 }
272 }
273 out:
274 return has_cs_hint;
275 }
276
277 static unsigned
278 cs_verify_hdrfields(void *block, uint16_t alg, uint8_t type,
279 uint8_t subtype, uint64_t vaddr, uint64_t laddr,
280 uint32_t blksz)
281 {
282 unsigned status;
283
284 if (alg != CKSUM_NONE && (status = cs_verify_cksum((cksum_alg_t)alg,
285 block, blksz, NULL)) !=
286 CS_STATUS_OK) {
287 return status;
288 }
289
290 if (type != BLK_TYPE_VOL_HEADER) {
291 return CS_STATUS_BLKTYPE;
292 }
293
294 if (subtype != BLK_SUBTYPE_NO_SUBTYPE) {
295 return CS_STATUS_INVALID;
296 }
297
298 if (vaddr != 0) {
299 return CS_STATUS_ADDRESS;
300 }
301
302 if (laddr != 0) {
303 return CS_STATUS_ADDRESS;
304 }
305
306 if (blksz != VOL_HEADER_NBYTES) {
307 return CS_STATUS_INVALID;
308 }
309
310 return CS_STATUS_OK;
311 }
312
313 //
314 // Verify if a Volume Header error might actually be due to a stale format.
315 // Some historic block layouts were such that they spuriously fail recent
316 // validity checks (there was a relocation of some key identifying fields in
317 // the v9->v10->v11 switch). Thus we follow-up a failure by some rudimentary
318 // probing (incl cksum) and override error.
319 //
320 static unsigned
321 cs_older_cs_version(dk_vol_header_t *hdr, unsigned status)
322 {
323 uint16_t cksum_alg;
324
325 struct {
326 uint8_t zeroes[VOL_HEADER_NBYTES];
327 } inprogress = {0};
328
329 struct v11_volhdr {
330 uint8_t mh_cksum[MAX_CKSUM_NBYTES];
331 uint16_t mh_format_version;
332 uint8_t mh_blk_type;
333 uint8_t mh_blk_subtype;
334 uint32_t mh_bundle_version;
335 uint64_t mh_txg_id;
336 uint64_t mh_vaddr;
337 uint64_t mh_laddr;
338 uint64_t mh_blk_owner;
339 uint32_t mh_blk_size;
340 uint8_t mh_blk_flags;
341 uint8_t mh_reserved1;
342 uint16_t mh_reserved2;
343 uint64_t mh_reserved8;
344 uint64_t vh_pv_nbytes;
345 uint64_t vh_pv_resize;
346 uint64_t vh_old_pv_nbytes;
347 uint16_t vh_endianness;
348 uint16_t vh_cksum_alg;
349 uint16_t vh_reserved2;
350 uint16_t vh_num_labels;
351 uint32_t vh_blksz;
352 uint32_t vh_label_max_nbytes;
353 uint64_t vh_label_addr[MAX_DISK_LABELS];
354 uint64_t vh_move_label_addr[MAX_DISK_LABELS];
355 uint16_t vh_wipe_key_nbytes[2];
356 uint16_t vh_wipe_key_alg[2];
357 uint8_t vh_wipe_key[2][MAX_WIPEKEY_NBYTES];
358 uint8_t vh_pv_uuid[16];
359 uint8_t vh_lvg_uuid[16];
360 } *v11;
361
362
363 if ((CS_STATUS(status) != CS_STATUS_OK) &&
364 (hdr->vh_header.mh_format_version !=
365 CORESTORAGE_FORMAT_VERSION)) {
366
367 //
368 // Ensure that the volume header is not totally empty before
369 // trying to check if this is indeed an older version.
370 //
371 if (!memcmp(hdr, &inprogress, VOL_HEADER_NBYTES)) {
372 return CS_INFO_ZERO_VH | CS_STATUS_NOTCS;
373 }
374
375 v11 = (struct v11_volhdr *)hdr;
376 if (v11->mh_format_version == 11) {
377
378 cksum_alg = v11->vh_cksum_alg;
379 if (cs_verify_hdrfields(v11, cksum_alg,
380 v11->mh_blk_type,
381 v11->mh_blk_subtype,
382 v11->mh_vaddr,
383 v11->mh_laddr,
384 v11->mh_blk_size) ==
385 CS_STATUS_OK) {
386
387 return CS_INFO_VERSIONITIS | CS_STATUS_NOTCS;
388 }
389 }
390 }
391
392 return status;
393 }
394
395 //
396 // cs_fd_is_corestorage_pv - taste if the disk is Apple_CoreStorage PV.
397 //
398 static int
399 cs_fd_is_corestorage_pv(int disk_fd, bool *is_cs_pv)
400 {
401 struct stat st;
402 bool pv_has_csuuid_hint;
403 int vh_idx, error;
404 uint32_t pv_blksz;
405 uint64_t pv_nblks;
406 uint64_t offset[NUM_VOL_HEADERS + 1];
407 unsigned status[NUM_VOL_HEADERS + 1];
408
409 dk_vol_header_t hdr[NUM_VOL_HEADERS + 1];
410
411 error = 0;
412 pv_blksz = 0;
413 pv_nblks = 0;
414 *is_cs_pv = false;
415 pv_has_csuuid_hint = false;
416
417 infomsg("Tasting Apple_CoreStorage plugin, fd: %d\n", disk_fd);
418
419 //
420 // Userfs corestorage plugin only supports block device. Thus, we
421 // ensure that the passed device is block device before proceeding
422 // further.
423 //
424 fstat(disk_fd, &st);
425 if (!S_ISBLK(st.st_mode)) {
426 errmsg("Apple_CoreStorage plugin only supports block "
427 "device. Aborting taste.\n");
428 return ENOTSUP;
429 }
430
431 //
432 // Each PV has two volume headers, each has size 512 bytes, and resides
433 // on the first and last 512 bytes of the PV. Each volume header has a
434 // common block header with txg id and checksum, and only the volume
435 // header with the right checksum and largest txg id is used when
436 // mounting the LVG. Thus, to read the last volume header we need to
437 // know the block-size and the number of blocks in the block device.
438 //
439 if (ioctl(disk_fd, DKIOCGETBLOCKSIZE, &pv_blksz) == -1) {
440 error = errno;
441 }
442
443 if (!error && ioctl(disk_fd, DKIOCGETBLOCKCOUNT, &pv_nblks) == -1) {
444 error = errno;
445 }
446
447 //
448 // If we fail to determine the block size and block count, we bail out.
449 //
450 if (!error && (!pv_blksz || !pv_nblks)) {
451 error = ENOTBLK;
452 }
453
454 if (error) {
455 errmsg("Failed to get blocksize and block count "
456 "for the device. Aborting taste.\n");
457 return error;
458 }
459
460 //
461 // Check if the device is tagged as being Apple_CoreStorage (Content
462 // Hint). If not we bail-out now.
463 //
464 pv_has_csuuid_hint = cs_get_content_hint_for_pv(&st);
465 if (!pv_has_csuuid_hint) {
466 *is_cs_pv = false;
467 return 0;
468 }
469
470 //
471 // We go through the two volume headers at offset 0 and at offset
472 // (pv_nblks * pv_blksz - VOL_HEADER_NBYTES) and try to verify if
473 // the volume headers are valid core storage physical volume volume
474 // headers.
475 //
476 offset[0] = 0;
477 offset[1] = pv_nblks * pv_blksz - VOL_HEADER_NBYTES;
478
479 //
480 // Initialize status as invalid.
481 //
482 for (vh_idx = 0; vh_idx < NUM_VOL_HEADERS; ++vh_idx ) {
483 status[vh_idx] = CS_STATUS_INVALID;
484 }
485
486 for (vh_idx = 0; vh_idx < NUM_VOL_HEADERS; ++vh_idx) {
487 ssize_t bytes_read;
488
489 //
490 // Read the PV volume header and cache it inside `hdr[vh_idx]`.
491 //
492 bytes_read = pread(disk_fd, &hdr[vh_idx], VOL_HEADER_NBYTES,
493 offset[vh_idx]);
494
495 if (bytes_read == -1) {
496 error = errno;
497 }
498
499 if (!error && bytes_read != VOL_HEADER_NBYTES) {
500 error = EIO;
501 }
502
503 if (error) {
504 errmsg("Failed to read volume-hearder at offset %llu "
505 "for disk with fd %d, Aborting taste.\n",
506 offset[vh_idx], disk_fd);
507 break;
508 }
509
510 //
511 // Verify read volume-header.
512 //
513 status[vh_idx] = cs_verify_vh(&hdr[vh_idx], pv_blksz);
514 if (CS_STATUS(status[vh_idx]) != CS_STATUS_OK) {
515
516 //
517 // Check if this physical volume has an older version
518 // of header.
519 //
520 status[vh_idx] = cs_older_cs_version(&hdr[vh_idx],
521 status[vh_idx]);
522 if (CS_INFO(status[vh_idx]) & CS_INFO_VERSIONITIS) {
523
524 infomsg("Disk with fd %u has older physical "
525 "volume header format.\n",
526 disk_fd);
527 status[vh_idx] = CS_STATUS_OK;
528 }
529 }
530
531 if (status[vh_idx] != CS_STATUS_OK) {
532 break;
533 }
534 }
535
536 if (error) {
537 return error;
538 }
539
540 //
541 // If there was no error and both the volume headers passed
542 // verification that means this is a core storage physical volume.
543 //
544 for (vh_idx = 0; vh_idx < NUM_VOL_HEADERS; ++vh_idx ) {
545 if (status[vh_idx] != CS_STATUS_OK) {
546 break;
547 }
548 *is_cs_pv = true;
549 }
550
551 return 0;
552 }
553
554 //
555 // cs_uvfsop_taste - taste if a given disk is Apple_CoreStorage PV.
556 //
557 // disk_fd: file descriptor of the disk to taste.
558 //
559 // Returns:
560 //
561 // i) 0 if the passed disk is indeed an Apple_CoreStorage PV.
562 //
563 // Or
564 //
565 // ii) ENOTSUP if the passed disk is not an Apple_CoreStorage PV.
566 //
567 // Or
568 //
569 // iii) errno if there was some error attempting to taste the disk.
570 //
571 static int
572 cs_uvfsop_taste(int disk_fd)
573 {
574 int error;
575 bool is_cs_pv;
576
577 //
578 // Each PV has two volume headers, each has size 512 bytes, and resides
579 // on the first and last 512 bytes of the PV. Each volume header has a
580 // common block header with transaction-id and checksum, and only the
581 // volume header with the right checksum and largest transaction-id is
582 // used when mounting the LVG(logical volume group). To verify that
583 // the disk with file descriptor is indeed a Apple_CoreStorage PV
584 // (physical volume), we:
585 //
586 // i) Read IOReg to verify that it has `APPLE_CORESTORAGE_UUID` hint.
587 //
588 // and
589 //
590 // ii) Verify the PV volume headers at the start and end of the disk.
591 //
592 // Please NOTE: This taste function is defensive (strict). We do a
593 // strict match so that we ensure that we don't falsely match some
594 // other volume format.
595 //
596 error = cs_fd_is_corestorage_pv(disk_fd, &is_cs_pv);
597 if (error) {
598 errmsg("Encountered error while tasting disk with file "
599 "descriptor %d for Apple_CoreStorage "
600 "plugin (error %d).\n", disk_fd, error);
601 return error;
602
603 }
604
605 //
606 // This is not an Apple_CoreStorage PV.
607 //
608 if (!is_cs_pv) {
609 errmsg("Disk with file descriptor %d is not an corestorage "
610 "physical volume.\n", disk_fd);
611 return ENOTSUP;
612 }
613
614 //
615 // We have found an Apple_CoreStorage PV, return success.
616 //
617 infomsg("Disk with file descriptor %d is corestorage physical "
618 "volume.\n", disk_fd);
619
620 return 0;
621 }
622
623 //
624 // Plugin lifecycle functions.
625 //
626 static int
627 cs_uvfsop_init(void)
628 {
629 infomsg("Initializing CS UserFS plugin...\n");
630 return 0;
631 }
632
633 static void
634 cs_uvfsop_fini(void)
635 {
636 infomsg("Cleaning up CS UserFS plugin...\n");
637 }
638
639 //
640 // Plugin function registration.
641 //
642 UVFSFSOps cs_fsops = {
643 .fsops_version = UVFS_FSOPS_VERSION_CURRENT,
644 .fsops_init = cs_uvfsop_init,
645 .fsops_fini = cs_uvfsop_fini,
646 .fsops_taste = cs_uvfsop_taste,
647 };
648
649 __attribute__((visibility("default")))
650 void
651 livefiles_plugin_init(UVFSFSOps **ops)
652 {
653 if (ops) {
654 *ops = &cs_fsops;
655 }
656 }