2 // Copyright (c) 2019-2019 Apple Inc. All rights reserved.
4 // lf_cs_vfsops.c - Implemenents routines for handling VFS operations for
5 // livefiles Apple_CoreStorage plugin.
15 #include <sys/ioctl.h>
17 #include <sys/types.h>
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>
28 #include "lf_cs_checksum.h"
29 #include "lf_cs_disk_format.h"
30 #include "lf_cs_logging.h"
33 #define VALUE_UNSPECIFIED ((uint64_t)-1)
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.
42 cs_verify_cksum(cksum_alg_t alg
, const void *ptr
, size_t len
,
45 uint8_t c
[MAX_CKSUM_NBYTES
];
47 if (!(alg
== CKSUM_NONE
) && !(alg
== CKSUM_ALG_CRC_32
)) {
48 return CS_STATUS_INVALID
;
53 cksum(alg
, (chk
= (uint8_t *)ptr
) + MAX_CKSUM_NBYTES
,
54 len
- MAX_CKSUM_NBYTES
, c
);
56 cksum(alg
, ptr
, len
, c
);
59 if (memcmp(c
, chk
, MAX_CKSUM_NBYTES
)) {
60 return CS_STATUS_CKSUM
;
67 // These block types must belong to the current transaction; all
68 // other types can be referenced from older transactions.
70 #define CS_CHKPOINT_BLK(_b) \
71 ((_b)->mh_blk_type >= BLK_TYPE_VOL_HEADER &&\
72 (_b)->mh_blk_type <= BLK_TYPE_SUT)
76 // These block types are the only ones with Secure Deletion content (LVF).
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)
83 // cs_verify_blkhdr - Verify standard metadata header fields.
85 // Each block begins with a 'metadata_header_t', which often has known/expected
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
)
95 if (alg
!= CKSUM_NONE
&& (status
= cs_verify_cksum((cksum_alg_t
)alg
,
96 hdr
, blksz
, NULL
)) != CS_STATUS_OK
) {
100 if (type
!= BLK_TYPE_UNKNOWN
&& hdr
->mh_blk_type
!= type
) {
101 return CS_STATUS_BLKTYPE
;
104 if (subtype
!= BLK_SUBTYPE_UNKNOWN
&& hdr
->mh_blk_subtype
!= subtype
) {
105 return CS_STATUS_INVALID
;
108 if (vaddr
!= VALUE_UNSPECIFIED
&& hdr
->mh_vaddr
!= vaddr
) {
109 return CS_STATUS_ADDRESS
;
112 if (laddr
!= VALUE_UNSPECIFIED
&& hdr
->mh_laddr
!= laddr
) {
113 return CS_STATUS_ADDRESS
;
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
;
123 if (blksz
!= 0 && hdr
->mh_blk_size
!= blksz
) {
124 return CS_STATUS_INVALID
;
127 if (!CS_DSDLIST_BLK(hdr
) &&
128 (hdr
->mh_blk_flags
& BLK_FLAG_IN_DSD_LIST
)) {
129 return CS_STATUS_INVALID
;
136 // cs_verify_versions - Verify volume header version.
139 cs_verify_versions(const metadata_header_t
*hdr
)
141 if (hdr
!= NULL
&& hdr
->mh_format_version
!=
142 CORESTORAGE_FORMAT_VERSION
) {
143 return CS_INFO_VERSIONITIS
| CS_STATUS_NOTCS
;
147 hdr
->mh_blk_type
== BLK_TYPE_VOL_HEADER
&&
148 ((dk_vol_header_t
*)hdr
)->vh_endianness
!=
150 return CS_INFO_VERSIONITIS
| CS_STATUS_NOTCS
;
157 // cs_verify_vh - Read and verify the Volume Headers (first/last 512-bytes of
161 cs_verify_vh(dk_vol_header_t
*hdr
, uint32_t blksz
)
165 status
= cs_verify_blkhdr(&hdr
->vh_header
, hdr
->vh_cksum_alg
,
167 BLK_SUBTYPE_NO_SUBTYPE
, 0, 0,
168 VALUE_UNSPECIFIED
, VOL_HEADER_NBYTES
);
169 if (status
!= CS_STATUS_OK
) {
173 status
= cs_verify_versions(&hdr
->vh_header
);
174 if (status
!= CS_STATUS_OK
) {
178 if (!hdr
->vh_num_labels
|| hdr
->vh_num_labels
> MAX_DISK_LABELS
) {
179 return CS_STATUS_INVALID
;
182 if (hdr
->vh_label_max_nbytes
% blksz
!= 0) {
183 return CS_STATUS_INVALID
;
186 if (hdr
->vh_blksz
== 0 || hdr
->vh_blksz
% blksz
!= 0) {
187 return CS_STATUS_INVALID
;
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
;
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
;
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
;
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.
218 cs_get_content_hint_for_pv(struct stat
*st
)
221 int dev_major
, dev_minor
;
222 CFMutableDictionaryRef matching
;
225 if ((matching
= IOServiceMatching(kIOMediaClass
))) {
227 io_service_t service
;
229 if ((matching
= IOServiceMatching(kIOMediaClass
))) {
232 dev_major
= major(st
->st_rdev
);
233 dev_minor
= minor(st
->st_rdev
);
235 num_ref
= CFNumberCreate(kCFAllocatorDefault
,
236 kCFNumberIntType
, &dev_major
);
238 CFDictionarySetValue(matching
,
239 CFSTR(kIOBSDMajorKey
),
244 num_ref
= CFNumberCreate(kCFAllocatorDefault
,
245 kCFNumberIntType
, &dev_minor
);
247 CFDictionarySetValue(matching
,
248 CFSTR(kIOBSDMinorKey
),
253 service
= IOServiceGetMatchingService(
254 kIOMasterPortDefault
, matching
);
259 if ((str
= IORegistryEntryCreateCFProperty( service
,
260 CFSTR(kIOMediaContentHintKey
),
261 kCFAllocatorDefault
, 0)) !=
264 has_cs_hint
= CFStringCompare((CFStringRef
)str
,
265 CFSTR(APPLE_CORESTORAGE_UUID
), 0) ==
269 IOObjectRelease(service
);
278 cs_verify_hdrfields(void *block
, uint16_t alg
, uint8_t type
,
279 uint8_t subtype
, uint64_t vaddr
, uint64_t laddr
,
284 if (alg
!= CKSUM_NONE
&& (status
= cs_verify_cksum((cksum_alg_t
)alg
,
285 block
, blksz
, NULL
)) !=
290 if (type
!= BLK_TYPE_VOL_HEADER
) {
291 return CS_STATUS_BLKTYPE
;
294 if (subtype
!= BLK_SUBTYPE_NO_SUBTYPE
) {
295 return CS_STATUS_INVALID
;
299 return CS_STATUS_ADDRESS
;
303 return CS_STATUS_ADDRESS
;
306 if (blksz
!= VOL_HEADER_NBYTES
) {
307 return CS_STATUS_INVALID
;
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.
321 cs_older_cs_version(dk_vol_header_t
*hdr
, unsigned status
)
326 uint8_t zeroes
[VOL_HEADER_NBYTES
];
330 uint8_t mh_cksum
[MAX_CKSUM_NBYTES
];
331 uint16_t mh_format_version
;
333 uint8_t mh_blk_subtype
;
334 uint32_t mh_bundle_version
;
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
;
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];
363 if ((CS_STATUS(status
) != CS_STATUS_OK
) &&
364 (hdr
->vh_header
.mh_format_version
!=
365 CORESTORAGE_FORMAT_VERSION
)) {
368 // Ensure that the volume header is not totally empty before
369 // trying to check if this is indeed an older version.
371 if (!memcmp(hdr
, &inprogress
, VOL_HEADER_NBYTES
)) {
372 return CS_INFO_ZERO_VH
| CS_STATUS_NOTCS
;
375 v11
= (struct v11_volhdr
*)hdr
;
376 if (v11
->mh_format_version
== 11) {
378 cksum_alg
= v11
->vh_cksum_alg
;
379 if (cs_verify_hdrfields(v11
, cksum_alg
,
387 return CS_INFO_VERSIONITIS
| CS_STATUS_NOTCS
;
396 // cs_fd_is_corestorage_pv - taste if the disk is Apple_CoreStorage PV.
399 cs_fd_is_corestorage_pv(int disk_fd
, bool *is_cs_pv
)
402 bool pv_has_csuuid_hint
;
406 uint64_t offset
[NUM_VOL_HEADERS
+ 1];
407 unsigned status
[NUM_VOL_HEADERS
+ 1];
409 dk_vol_header_t hdr
[NUM_VOL_HEADERS
+ 1];
415 pv_has_csuuid_hint
= false;
417 infomsg("Tasting Apple_CoreStorage plugin, fd: %d\n", disk_fd
);
420 // Userfs corestorage plugin only supports block device. Thus, we
421 // ensure that the passed device is block device before proceeding
425 if (!S_ISBLK(st
.st_mode
)) {
426 errmsg("Apple_CoreStorage plugin only supports block "
427 "device. Aborting taste.\n");
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.
439 if (ioctl(disk_fd
, DKIOCGETBLOCKSIZE
, &pv_blksz
) == -1) {
443 if (!error
&& ioctl(disk_fd
, DKIOCGETBLOCKCOUNT
, &pv_nblks
) == -1) {
448 // If we fail to determine the block size and block count, we bail out.
450 if (!error
&& (!pv_blksz
|| !pv_nblks
)) {
455 errmsg("Failed to get blocksize and block count "
456 "for the device. Aborting taste.\n");
461 // Check if the device is tagged as being Apple_CoreStorage (Content
462 // Hint). If not we bail-out now.
464 pv_has_csuuid_hint
= cs_get_content_hint_for_pv(&st
);
465 if (!pv_has_csuuid_hint
) {
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
477 offset
[1] = pv_nblks
* pv_blksz
- VOL_HEADER_NBYTES
;
480 // Initialize status as invalid.
482 for (vh_idx
= 0; vh_idx
< NUM_VOL_HEADERS
; ++vh_idx
) {
483 status
[vh_idx
] = CS_STATUS_INVALID
;
486 for (vh_idx
= 0; vh_idx
< NUM_VOL_HEADERS
; ++vh_idx
) {
490 // Read the PV volume header and cache it inside `hdr[vh_idx]`.
492 bytes_read
= pread(disk_fd
, &hdr
[vh_idx
], VOL_HEADER_NBYTES
,
495 if (bytes_read
== -1) {
499 if (!error
&& bytes_read
!= VOL_HEADER_NBYTES
) {
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
);
511 // Verify read volume-header.
513 status
[vh_idx
] = cs_verify_vh(&hdr
[vh_idx
], pv_blksz
);
514 if (CS_STATUS(status
[vh_idx
]) != CS_STATUS_OK
) {
517 // Check if this physical volume has an older version
520 status
[vh_idx
] = cs_older_cs_version(&hdr
[vh_idx
],
522 if (CS_INFO(status
[vh_idx
]) & CS_INFO_VERSIONITIS
) {
524 infomsg("Disk with fd %u has older physical "
525 "volume header format.\n",
527 status
[vh_idx
] = CS_STATUS_OK
;
531 if (status
[vh_idx
] != CS_STATUS_OK
) {
541 // If there was no error and both the volume headers passed
542 // verification that means this is a core storage physical volume.
544 for (vh_idx
= 0; vh_idx
< NUM_VOL_HEADERS
; ++vh_idx
) {
545 if (status
[vh_idx
] != CS_STATUS_OK
) {
555 // cs_uvfsop_taste - taste if a given disk is Apple_CoreStorage PV.
557 // disk_fd: file descriptor of the disk to taste.
561 // i) 0 if the passed disk is indeed an Apple_CoreStorage PV.
565 // ii) ENOTSUP if the passed disk is not an Apple_CoreStorage PV.
569 // iii) errno if there was some error attempting to taste the disk.
572 cs_uvfsop_taste(int disk_fd
)
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:
586 // i) Read IOReg to verify that it has `APPLE_CORESTORAGE_UUID` hint.
590 // ii) Verify the PV volume headers at the start and end of the disk.
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.
596 error
= cs_fd_is_corestorage_pv(disk_fd
, &is_cs_pv
);
598 errmsg("Encountered error while tasting disk with file "
599 "descriptor %d for Apple_CoreStorage "
600 "plugin (error %d).\n", disk_fd
, error
);
606 // This is not an Apple_CoreStorage PV.
609 errmsg("Disk with file descriptor %d is not an corestorage "
610 "physical volume.\n", disk_fd
);
615 // We have found an Apple_CoreStorage PV, return success.
617 infomsg("Disk with file descriptor %d is corestorage physical "
618 "volume.\n", disk_fd
);
624 // Plugin lifecycle functions.
629 infomsg("Initializing CS UserFS plugin...\n");
636 infomsg("Cleaning up CS UserFS plugin...\n");
640 // Plugin function registration.
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
,
649 __attribute__((visibility("default")))
651 livefiles_plugin_init(UVFSFSOps
**ops
)