2 * Copyright (c) 2014 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/cprotect.h>
30 #include <sys/xattr.h>
31 #include <sys/utfconv.h>
32 #include <libkern/OSByteOrder.h>
33 #include <kern/kalloc.h>
37 #include "hfs_fsctl.h"
38 #include "hfs_endian.h"
39 #include "hfscommon/headers/BTreesInternal.h"
40 #include "hfscommon/headers/BTreesPrivate.h"
41 #include "hfscommon/headers/FileMgrInternal.h"
43 #include <hfs/hfs_cprotect.h>
47 HFSPlusCatalogFolder folder_record
;
48 HFSPlusCatalogFile file_record
;
49 HFSPlusCatalogThread thread_record
;
50 HFSPlusExtentRecord extent_record
;
51 HFSPlusAttrRecord attr_record
;
53 typedef union HFSPlusRecord HFSPlusRecord
;
56 HFSPlusExtentKey extent_key
;
57 HFSPlusAttrKey attr_key
;
59 typedef union HFSPlusKey HFSPlusKey
;
61 typedef enum traverse_btree_flag
{
63 //If set, extents btree will also be traversed along with catalog btree, so grab correct locks upfront
64 TRAVERSE_BTREE_EXTENTS
= 1,
66 // Getting content-protection attributes, allocate enough space to accomodate the records.
67 TRAVERSE_BTREE_XATTR_CPROTECT
= 2,
69 } traverse_btree_flag_t
;
73 static errno_t
hfs_fsinfo_metadata_blocks(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
);
74 static errno_t
hfs_fsinfo_metadata_extents(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
);
75 static errno_t
hfs_fsinfo_metadata_percentfree(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
);
76 static errno_t
fsinfo_file_extent_count_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
77 static errno_t
fsinfo_file_extent_size_catalog_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
78 static errno_t
fsinfo_file_extent_size_overflow_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
79 static errno_t
fsinfo_file_size_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
80 static errno_t
fsinfo_dir_valence_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
81 static errno_t
fsinfo_name_size_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
82 static errno_t
fsinfo_xattr_size_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
83 static errno_t
traverse_btree(struct hfsmount
*hfsmp
, uint32_t btree_fileID
, traverse_btree_flag_t flags
, void *fsinfo
,
84 int (*callback
)(struct hfsmount
*, HFSPlusKey
*, HFSPlusRecord
*, void *));
85 static errno_t
hfs_fsinfo_free_extents(struct hfsmount
*hfsmp
, struct hfs_fsinfo_data
*fsinfo
);
86 static void fsinfo_free_extents_callback(void *data
, off_t free_extent_size
);
88 static errno_t
fsinfo_cprotect_count_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
90 static errno_t
fsinfo_symlink_size_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
93 * Entry function for all the fsinfo requests from hfs_vnop_ioctl()
94 * Depending on the type of request, this function will call the
95 * appropriate sub-function and return success or failure back to
99 errno_t
hfs_get_fsinfo(struct hfsmount
*hfsmp
, void *a_data
)
102 hfs_fsinfo
*fsinfo_union
;
103 uint32_t request_type
;
104 uint32_t header_len
= sizeof(hfs_fsinfo_header_t
);
106 fsinfo_union
= (hfs_fsinfo
*)a_data
;
107 request_type
= fsinfo_union
->header
.request_type
;
109 // Zero out output fields to fsinfo_union, keep the user input fields intact.
110 bzero((char *)fsinfo_union
+ header_len
, sizeof(hfs_fsinfo
) - header_len
);
112 switch (request_type
) {
113 case HFS_FSINFO_METADATA_BLOCKS_INFO
:
114 error
= hfs_fsinfo_metadata_blocks(hfsmp
, &(fsinfo_union
->metadata
));
117 case HFS_FSINFO_METADATA_EXTENTS
:
118 error
= hfs_fsinfo_metadata_extents(hfsmp
, &(fsinfo_union
->metadata
));
121 case HFS_FSINFO_METADATA_PERCENTFREE
:
122 error
= hfs_fsinfo_metadata_percentfree(hfsmp
, &(fsinfo_union
->metadata
));
125 case HFS_FSINFO_FILE_EXTENT_COUNT
:
126 /* Traverse catalog btree and invoke callback for all records */
127 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, TRAVERSE_BTREE_EXTENTS
, &(fsinfo_union
->data
), fsinfo_file_extent_count_callback
);
130 case HFS_FSINFO_FILE_EXTENT_SIZE
:
131 /* Traverse the catalog btree first */
132 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, 0, &(fsinfo_union
->data
), &fsinfo_file_extent_size_catalog_callback
);
136 /* Traverse the overflow extents btree now */
137 error
= traverse_btree(hfsmp
, kHFSExtentsFileID
, 0, &(fsinfo_union
->data
), &fsinfo_file_extent_size_overflow_callback
);
140 case HFS_FSINFO_FILE_SIZE
:
141 /* Traverse catalog btree and invoke callback for all records */
142 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, 0, &(fsinfo_union
->data
), &fsinfo_file_size_callback
);
145 case HFS_FSINFO_DIR_VALENCE
:
146 /* Traverse catalog btree and invoke callback for all records */
147 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, 0, &(fsinfo_union
->data
), &fsinfo_dir_valence_callback
);
150 case HFS_FSINFO_NAME_SIZE
:
151 /* Traverse catalog btree and invoke callback for all records */
152 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, 0, &(fsinfo_union
->name
), &fsinfo_name_size_callback
);
155 case HFS_FSINFO_XATTR_SIZE
:
156 /* Traverse attribute btree and invoke callback for all records */
157 error
= traverse_btree(hfsmp
, kHFSAttributesFileID
, 0, &(fsinfo_union
->data
), &fsinfo_xattr_size_callback
);
160 case HFS_FSINFO_FREE_EXTENTS
:
161 error
= hfs_fsinfo_free_extents(hfsmp
, &(fsinfo_union
->data
));
164 case HFS_FSINFO_SYMLINK_SIZE
:
165 /* Traverse catalog btree and invoke callback for all records */
166 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, 0, &(fsinfo_union
->data
), &fsinfo_symlink_size_callback
);
170 case HFS_FSINFO_FILE_CPROTECT_COUNT
:
171 /* Traverse attribute btree and invoke callback for all records */
172 error
= traverse_btree(hfsmp
, kHFSAttributesFileID
, TRAVERSE_BTREE_XATTR_CPROTECT
, &(fsinfo_union
->cprotect
), &fsinfo_cprotect_count_callback
);
184 * This function provides information about total number of allocation blocks
185 * for each individual metadata file.
188 hfs_fsinfo_metadata_blocks(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
)
191 int ret_lockflags
= 0;
194 * Getting number of allocation blocks for all metadata files
195 * should be a relatively quick operation, so we grab locks for all
196 * the btrees at the same time
198 lockflags
= SFL_CATALOG
| SFL_EXTENTS
| SFL_BITMAP
| SFL_ATTRIBUTE
;
199 ret_lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_SHARED_LOCK
);
201 /* Get information about all the btrees */
202 fsinfo
->extents
= hfsmp
->hfs_extents_cp
->c_datafork
->ff_blocks
;
203 fsinfo
->catalog
= hfsmp
->hfs_catalog_cp
->c_datafork
->ff_blocks
;
204 fsinfo
->allocation
= hfsmp
->hfs_allocation_cp
->c_datafork
->ff_blocks
;
205 if (hfsmp
->hfs_attribute_cp
)
206 fsinfo
->attribute
= hfsmp
->hfs_attribute_cp
->c_datafork
->ff_blocks
;
208 fsinfo
->attribute
= 0;
210 /* Done with btrees, give up the locks */
211 hfs_systemfile_unlock(hfsmp
, ret_lockflags
);
213 /* Get information about journal file */
214 fsinfo
->journal
= howmany(hfsmp
->jnl_size
, hfsmp
->blockSize
);
220 * Helper function to count the number of valid extents in a file fork structure
223 hfs_count_extents_fp(struct filefork
*ff
)
227 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
228 if (ff
->ff_data
.cf_extents
[i
].blockCount
== 0) {
238 * This is a helper function that counts the total number of valid
239 * extents in all the overflow extent records for given fileID
240 * in overflow extents btree
243 hfs_count_overflow_extents(struct hfsmount
*hfsmp
, uint32_t fileID
, uint32_t *num_extents
)
247 struct BTreeIterator
*iterator
= NULL
;
248 FSBufferDescriptor btdata
;
249 HFSPlusExtentKey
*extentKey
;
250 HFSPlusExtentRecord extentData
;
251 uint32_t extent_count
= 0;
254 fcb
= VTOF(hfsmp
->hfs_extents_vp
);
255 MALLOC(iterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
| M_ZERO
);
257 extentKey
= (HFSPlusExtentKey
*) &iterator
->key
;
258 extentKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
259 extentKey
->forkType
= kHFSDataForkType
;
260 extentKey
->fileID
= fileID
;
261 extentKey
->startBlock
= 0;
263 btdata
.bufferAddress
= &extentData
;
264 btdata
.itemSize
= sizeof(HFSPlusExtentRecord
);
265 btdata
.itemCount
= 1;
267 /* Search for overflow extent record */
268 error
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, iterator
);
271 * We used startBlock of zero, so we will not find any records and errors
272 * are expected. It will also position the iterator just before the first
273 * overflow extent record for given fileID (if any).
275 if (error
&& error
!= fsBTRecordNotFoundErr
&& error
!= fsBTEndOfIterationErr
)
281 if (msleep(NULL
, NULL
, PINOD
| PCATCH
,
282 "hfs_fsinfo", NULL
) == EINTR
) {
287 error
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, &btdata
, NULL
);
289 /* These are expected errors, so mask them */
290 if (error
== fsBTRecordNotFoundErr
|| error
== fsBTEndOfIterationErr
) {
296 /* If we encounter different fileID, stop the iteration */
297 if (extentKey
->fileID
!= fileID
) {
301 if (extentKey
->forkType
!= kHFSDataForkType
)
304 /* This is our record of interest; only count the datafork extents. */
305 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
306 if (extentData
[i
].blockCount
== 0) {
314 FREE(iterator
, M_TEMP
);
317 *num_extents
= extent_count
;
319 return MacToVFSError(error
);
323 * This function provides information about total number of extents (including
324 * extents from overflow extents btree, if any) for each individual metadata
328 hfs_fsinfo_metadata_extents(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
)
332 int ret_lockflags
= 0;
333 uint32_t overflow_count
;
336 * Counting the number of extents for all metadata files should
337 * be a relatively quick operation, so we grab locks for all the
338 * btrees at the same time
340 lockflags
= SFL_CATALOG
| SFL_EXTENTS
| SFL_BITMAP
| SFL_ATTRIBUTE
;
341 ret_lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_SHARED_LOCK
);
343 /* Get number of extents for extents overflow btree */
344 fsinfo
->extents
= hfs_count_extents_fp(hfsmp
->hfs_extents_cp
->c_datafork
);
346 /* Get number of extents for catalog btree */
347 fsinfo
->catalog
= hfs_count_extents_fp(hfsmp
->hfs_catalog_cp
->c_datafork
);
348 if (fsinfo
->catalog
>= kHFSPlusExtentDensity
) {
349 error
= hfs_count_overflow_extents(hfsmp
, kHFSCatalogFileID
, &overflow_count
);
353 fsinfo
->catalog
+= overflow_count
;
356 /* Get number of extents for allocation file */
357 fsinfo
->allocation
= hfs_count_extents_fp(hfsmp
->hfs_allocation_cp
->c_datafork
);
358 if (fsinfo
->allocation
>= kHFSPlusExtentDensity
) {
359 error
= hfs_count_overflow_extents(hfsmp
, kHFSAllocationFileID
, &overflow_count
);
363 fsinfo
->allocation
+= overflow_count
;
367 * Get number of extents for attribute btree.
368 * hfs_attribute_cp might be NULL.
370 if (hfsmp
->hfs_attribute_cp
) {
371 fsinfo
->attribute
= hfs_count_extents_fp(hfsmp
->hfs_attribute_cp
->c_datafork
);
372 if (fsinfo
->attribute
>= kHFSPlusExtentDensity
) {
373 error
= hfs_count_overflow_extents(hfsmp
, kHFSAttributesFileID
, &overflow_count
);
377 fsinfo
->attribute
+= overflow_count
;
380 /* Journal always has one extent */
383 hfs_systemfile_unlock(hfsmp
, ret_lockflags
);
388 * Helper function to calculate percentage i.e. X is what percent of Y?
390 static inline uint32_t
391 hfs_percent(uint32_t X
, uint32_t Y
)
393 return (X
* 100ll) / Y
;
397 * This function provides percentage of free nodes vs total nodes for each
398 * individual metadata btrees, i.e. for catalog, overflow extents and
399 * attributes btree. This information is not applicable for allocation
400 * file and journal file.
403 hfs_fsinfo_metadata_percentfree(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
)
406 int ret_lockflags
= 0;
407 BTreeControlBlockPtr btreePtr
;
408 uint32_t free_nodes
, total_nodes
;
411 * Getting total and used nodes for all metadata btrees should
412 * be a relatively quick operation, so we grab locks for all the
413 * btrees at the same time
415 lockflags
= SFL_CATALOG
| SFL_EXTENTS
| SFL_BITMAP
| SFL_ATTRIBUTE
;
416 ret_lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_SHARED_LOCK
);
418 /* Overflow extents btree */
419 btreePtr
= VTOF(hfsmp
->hfs_extents_vp
)->fcbBTCBPtr
;
420 total_nodes
= btreePtr
->totalNodes
;
421 free_nodes
= btreePtr
->freeNodes
;
422 fsinfo
->extents
= hfs_percent(free_nodes
, total_nodes
);
425 btreePtr
= VTOF(hfsmp
->hfs_catalog_vp
)->fcbBTCBPtr
;
426 total_nodes
= btreePtr
->totalNodes
;
427 free_nodes
= btreePtr
->freeNodes
;
428 fsinfo
->catalog
= hfs_percent(free_nodes
, total_nodes
);
430 /* Attributes btree */
431 if (hfsmp
->hfs_attribute_vp
) {
432 btreePtr
= VTOF(hfsmp
->hfs_attribute_vp
)->fcbBTCBPtr
;
433 total_nodes
= btreePtr
->totalNodes
;
434 free_nodes
= btreePtr
->freeNodes
;
435 fsinfo
->attribute
= hfs_percent(free_nodes
, total_nodes
);
438 hfs_systemfile_unlock(hfsmp
, ret_lockflags
);
443 * Helper function to calculate log base 2 for given number
446 hfs_log2(uint64_t entry
)
448 return (63 - __builtin_clzll(entry
|1));
452 * Helper function to account for input entry into the data
453 * array based on its log base 2 value
456 void hfs_fsinfo_data_add(struct hfs_fsinfo_data
*fsinfo
, uint64_t entry
)
461 * hfs_fsinfo_data is generic data structure to aggregate information like sizes
462 * or counts in buckets of power of 2. Each bucket represents a range of values
463 * that is determined based on its index in the array. Specifically, buckets[i]
464 * represents values that are greater than or equal to 2^(i-1) and less than 2^i,
465 * except the last bucket which represents range greater than or equal to 2^(i-1)
467 * The current maximum number of buckets is 41, so we can represent range from
468 * 0 up to 1TB in increments of power of 2, and then a catch-all bucket of
469 * anything that is greater than or equal to 1TB.
472 * bucket[0] -> greater than or equal to 0 and less than 1
473 * bucket[1] -> greater than or equal to 1 and less than 2
474 * bucket[10] -> greater than or equal to 2^(10-1) = 512 and less than 2^10 = 1024
475 * bucket[20] -> greater than or equal to 2^(20-1) = 512KB and less than 2^20 = 1MB
476 * bucket[41] -> greater than or equal to 2^(41-1) = 1TB
482 * Calculate log base 2 value for the entry.
483 * Account for this value in the appropriate bucket.
484 * The last bucket is a catch-all bucket of
485 * anything that is greater than or equal to 1TB
487 bucket
= MIN(hfs_log2(entry
) + 1, HFS_FSINFO_DATA_MAX_BUCKETS
-1);
488 ++fsinfo
->bucket
[bucket
];
490 /* Entry is zero, so account it in 0th offset */
496 * Function to traverse all the records of a btree and then call caller-provided
497 * callback function for every record found. The type of btree is chosen based
498 * on the fileID provided by the caller. This fuction grabs the correct locks
499 * depending on the type of btree it will be traversing and flags provided
502 * Note: It might drop and reacquire the locks during execution.
505 traverse_btree(struct hfsmount
*hfsmp
, uint32_t btree_fileID
, traverse_btree_flag_t flags
,
506 void *fsinfo
, int (*callback
)(struct hfsmount
*, HFSPlusKey
*, HFSPlusRecord
*, void *))
510 int ret_lockflags
= 0;
512 struct BTreeIterator
*iterator
= NULL
;
513 struct FSBufferDescriptor btdata
;
515 HFSPlusRecord record
;
517 uint64_t start
, timeout_abs
;
519 switch(btree_fileID
) {
520 case kHFSExtentsFileID
:
521 fcb
= VTOF(hfsmp
->hfs_extents_vp
);
522 lockflags
= SFL_EXTENTS
;
524 case kHFSCatalogFileID
:
525 fcb
= VTOF(hfsmp
->hfs_catalog_vp
);
526 lockflags
= SFL_CATALOG
;
528 case kHFSAttributesFileID
:
529 // Attributes file doesn’t exist, There are no records to iterate.
530 if (hfsmp
->hfs_attribute_vp
== NULL
)
532 fcb
= VTOF(hfsmp
->hfs_attribute_vp
);
533 lockflags
= SFL_ATTRIBUTE
;
540 MALLOC(iterator
, struct BTreeIterator
*, sizeof(struct BTreeIterator
), M_TEMP
, M_WAITOK
| M_ZERO
);
542 /* The key is initialized to zero because we are traversing entire btree */
543 key
= (HFSPlusKey
*)&iterator
->key
;
545 if (flags
& TRAVERSE_BTREE_EXTENTS
) {
546 lockflags
|= SFL_EXTENTS
;
549 btdata
.bufferAddress
= &record
;
550 btdata
.itemSize
= sizeof(HFSPlusRecord
);
551 btdata
.itemCount
= 1;
553 /* Lock btree for duration of traversal */
554 ret_lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_SHARED_LOCK
);
555 btree_operation
= kBTreeFirstRecord
;
557 nanoseconds_to_absolutetime(HFS_FSINFO_MAX_LOCKHELD_TIME
, &timeout_abs
);
558 start
= mach_absolute_time();
562 if (msleep(NULL
, NULL
, PINOD
| PCATCH
,
563 "hfs_fsinfo", NULL
) == EINTR
) {
568 error
= BTIterateRecord(fcb
, btree_operation
, iterator
, &btdata
, NULL
);
570 if (error
== fsBTRecordNotFoundErr
|| error
== fsBTEndOfIterationErr
) {
575 /* Lookup next btree record on next call to BTIterateRecord() */
576 btree_operation
= kBTreeNextRecord
;
578 /* Call our callback function and stop iteration if there are any errors */
579 error
= callback(hfsmp
, key
, &record
, fsinfo
);
584 /* let someone else use the tree after we've processed over HFS_FSINFO_MAX_LOCKHELD_TIME */
585 if ((mach_absolute_time() - start
) >= timeout_abs
) {
587 /* release b-tree locks and let someone else get the lock */
588 hfs_systemfile_unlock (hfsmp
, ret_lockflags
);
590 /* add tsleep here to force context switch and fairness */
591 tsleep((caddr_t
)hfsmp
, PRIBIO
, "hfs_fsinfo", 1);
594 * re-acquire the locks in the same way that we wanted them originally.
595 * note: it is subtle but worth pointing out that in between the time that we
596 * released and now want to re-acquire these locks that the b-trees may have shifted
597 * slightly but significantly. For example, the catalog or other b-tree could have grown
598 * past 8 extents and now requires the extents lock to be held in order to be safely
599 * manipulated. We can't be sure of the state of the b-tree from where we last left off.
602 ret_lockflags
= hfs_systemfile_lock (hfsmp
, lockflags
, HFS_SHARED_LOCK
);
605 * It's highly likely that the search key we stashed away before dropping lock
606 * no longer points to an existing item. Iterator's IterateRecord is able to
607 * re-position itself and process the next record correctly. With lock dropped,
608 * there might be records missed for statistic gathering, which is ok. The
609 * point is to get aggregate values.
612 start
= mach_absolute_time();
614 /* loop back around and get another record */
618 hfs_systemfile_unlock(hfsmp
, ret_lockflags
);
619 FREE (iterator
, M_TEMP
);
620 return MacToVFSError(error
);
624 * Callback function to get distribution of number of extents
625 * for all user files in given file system. Note that this only
626 * accounts for data fork, no resource fork.
629 fsinfo_file_extent_count_callback(struct hfsmount
*hfsmp
,
630 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
634 uint32_t num_extents
= 0;
635 uint32_t num_overflow
= 0;
638 if (record
->file_record
.recordType
== kHFSPlusFileRecord
) {
639 /* Count total number of extents for this file */
640 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
641 blockCount
= record
->file_record
.dataFork
.extents
[i
].blockCount
;
642 if (blockCount
== 0) {
647 /* This file has overflow extent records, so search overflow btree */
648 if (num_extents
>= kHFSPlusExtentDensity
) {
649 /* The caller also hold extents overflow btree lock */
650 error
= hfs_count_overflow_extents(hfsmp
, record
->file_record
.fileID
, &num_overflow
);
654 num_extents
+= num_overflow
;
656 hfs_fsinfo_data_add(data
, num_extents
);
663 * Callback function to get distribution of individual extent sizes
664 * (in bytes) for all user files in given file system from catalog
665 * btree only. Note that this only accounts for data fork, no resource
668 static errno_t
fsinfo_file_extent_size_catalog_callback(__unused
struct hfsmount
*hfsmp
,
669 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
673 uint64_t extent_size
;
675 if (record
->file_record
.recordType
== kHFSPlusFileRecord
) {
676 /* Traverse through all valid extents */
677 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
678 blockCount
= record
->file_record
.dataFork
.extents
[i
].blockCount
;
679 if (blockCount
== 0) {
682 extent_size
= hfs_blk_to_bytes(blockCount
, hfsmp
->blockSize
);
683 hfs_fsinfo_data_add(data
, extent_size
);
690 * Callback function to get distribution of individual extent sizes
691 * (in bytes) for all user files in given file system from overflow
692 * extents btree only. Note that this only accounts for data fork,
695 static errno_t
fsinfo_file_extent_size_overflow_callback(__unused
struct hfsmount
*hfsmp
,
696 HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
700 uint64_t extent_size
;
702 if (key
->extent_key
.fileID
>= kHFSFirstUserCatalogNodeID
) {
703 // Only count the data fork extents.
704 if (key
->extent_key
.forkType
== kHFSDataForkType
) {
705 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
706 blockCount
= record
->extent_record
[i
].blockCount
;
707 if (blockCount
== 0) {
710 extent_size
= hfs_blk_to_bytes(blockCount
, hfsmp
->blockSize
);
711 hfs_fsinfo_data_add(data
, extent_size
);
719 * Callback function to get distribution of file sizes (in bytes)
720 * for all user files in given file system. Note that this only
721 * accounts for data fork, no resource fork.
723 static errno_t
fsinfo_file_size_callback(__unused
struct hfsmount
*hfsmp
,
724 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
726 if (record
->file_record
.recordType
== kHFSPlusFileRecord
) {
727 /* Record of interest, account for the size in the bucket */
728 hfs_fsinfo_data_add(data
, record
->file_record
.dataFork
.logicalSize
);
734 * Callback function to get distribution of directory valence
735 * for all directories in the given file system.
737 static errno_t
fsinfo_dir_valence_callback(__unused
struct hfsmount
*hfsmp
,
738 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
740 if (record
->folder_record
.recordType
== kHFSPlusFolderRecord
) {
741 hfs_fsinfo_data_add(data
, record
->folder_record
.valence
);
747 * Callback function to get distribution of number of unicode
748 * characters in name for all files and directories for a given
751 static errno_t
fsinfo_name_size_callback(__unused
struct hfsmount
*hfsmp
,
752 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
754 struct hfs_fsinfo_name
*fsinfo
= (struct hfs_fsinfo_name
*)data
;
757 if ((record
->folder_record
.recordType
== kHFSPlusFolderThreadRecord
) ||
758 (record
->folder_record
.recordType
== kHFSPlusFileThreadRecord
)) {
759 length
= record
->thread_record
.nodeName
.length
;
760 /* Make sure that the nodeName is bounded, otherwise return error */
761 if (length
> kHFSPlusMaxFileNameChars
) {
765 // sanity check for a name length of zero, which isn't valid on disk.
769 /* Round it down to nearest multiple of 5 to match our buckets granularity */
770 length
= (length
- 1)/ 5;
771 /* Account this value into our bucket */
772 fsinfo
->bucket
[length
]++;
778 * Callback function to get distribution of size of all extended
779 * attributes for a given file system.
781 static errno_t
fsinfo_xattr_size_callback(__unused
struct hfsmount
*hfsmp
,
782 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
784 if (record
->attr_record
.recordType
== kHFSPlusAttrInlineData
) {
785 /* Inline attribute */
786 hfs_fsinfo_data_add(data
, record
->attr_record
.attrData
.attrSize
);
787 } else if (record
->attr_record
.recordType
== kHFSPlusAttrForkData
) {
788 /* Larger attributes with extents information */
789 hfs_fsinfo_data_add(data
, record
->attr_record
.forkData
.theFork
.logicalSize
);
796 * Callback function to get distribution of free space extents for a given file system.
798 static void fsinfo_free_extents_callback(void *data
, off_t free_extent_size
)
800 // Assume a minimum of 4 KB block size
801 hfs_fsinfo_data_add(data
, free_extent_size
/ 4096);
805 * Function to get distribution of free space extents for a given file system.
807 static errno_t
hfs_fsinfo_free_extents(struct hfsmount
*hfsmp
, struct hfs_fsinfo_data
*fsinfo
)
809 return hfs_find_free_extents(hfsmp
, &fsinfo_free_extents_callback
, fsinfo
);
813 * Callback function to get distribution of symblock link sizes (in bytes)
814 * for all user files in given file system. Note that this only
815 * accounts for data fork, no resource fork.
817 static errno_t
fsinfo_symlink_size_callback(__unused
struct hfsmount
*hfsmp
,
818 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
820 if (record
->file_record
.recordType
== kHFSPlusFileRecord
) {
821 /* Record of interest, account for the size in the bucket */
822 if (S_ISLNK(record
->file_record
.bsdInfo
.fileMode
))
823 hfs_fsinfo_data_add((struct hfs_fsinfo_data
*)data
, record
->file_record
.dataFork
.logicalSize
);
830 * Callback function to get total number of files/directories
831 * for each content protection class
833 static int fsinfo_cprotect_count_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
,
834 HFSPlusRecord
*record
, void *data
)
836 struct hfs_fsinfo_cprotect
*fsinfo
= (struct hfs_fsinfo_cprotect
*)data
;
837 static const uint16_t cp_xattrname_utf16
[] = CONTENT_PROTECTION_XATTR_NAME_CHARS
;
839 * NOTE: cp_xattrname_utf16_len is the number of UTF-16 code units in
840 * the EA name string.
842 static const size_t cp_xattrname_utf16_len
= sizeof(cp_xattrname_utf16
)/2;
843 struct cp_xattr_v5
*xattr
;
844 size_t xattr_len
= sizeof(struct cp_xattr_v5
);
845 struct cprotect cp_entry
;
846 struct cprotect
*cp_entryp
= &cp_entry
;
849 /* Content protect xattrs are inline attributes only, so skip all others */
850 if (record
->attr_record
.recordType
!= kHFSPlusAttrInlineData
)
853 /* We only look at content protection xattrs */
854 if ((key
->attr_key
.attrNameLen
!= cp_xattrname_utf16_len
) ||
855 (bcmp(key
->attr_key
.attrName
, cp_xattrname_utf16
, 2 * cp_xattrname_utf16_len
))) {
859 xattr
= (struct cp_xattr_v5
*)((void *)(record
->attr_record
.attrData
.attrData
));
860 error
= cp_read_xattr_v5(hfsmp
, xattr
, xattr_len
, (cprotect_t
*)&cp_entryp
,
861 CP_GET_XATTR_BASIC_INFO
);
865 /* No key present, skip this record */
866 if (!ISSET(cp_entry
.cp_flags
, CP_HAS_A_KEY
))
869 /* Now account for the persistent class */
870 switch (CP_CLASS(cp_entry
.cp_pclass
)) {
871 case PROTECTION_CLASS_A
:
874 case PROTECTION_CLASS_B
:
877 case PROTECTION_CLASS_C
:
880 case PROTECTION_CLASS_D
:
883 case PROTECTION_CLASS_E
:
886 case PROTECTION_CLASS_F
: