2 * Copyright (c) 2014-2015 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/xattr.h>
30 #include <sys/utfconv.h>
31 #include <libkern/OSByteOrder.h>
35 #include "hfs_fsctl.h"
36 #include "hfs_endian.h"
37 #include "BTreesInternal.h"
38 #include "BTreesPrivate.h"
39 #include "FileMgrInternal.h"
41 #include "hfs_cprotect.h"
45 HFSPlusCatalogFolder folder_record
;
46 HFSPlusCatalogFile file_record
;
47 HFSPlusCatalogThread thread_record
;
48 HFSPlusExtentRecord extent_record
;
49 HFSPlusAttrRecord attr_record
;
51 typedef union HFSPlusRecord HFSPlusRecord
;
54 HFSPlusExtentKey extent_key
;
55 HFSPlusAttrKey attr_key
;
57 typedef union HFSPlusKey HFSPlusKey
;
59 typedef enum traverse_btree_flag
{
61 //If set, extents btree will also be traversed along with catalog btree, so grab correct locks upfront
62 TRAVERSE_BTREE_EXTENTS
= 1,
64 // Getting content-protection attributes, allocate enough space to accomodate the records.
65 TRAVERSE_BTREE_XATTR_CPROTECT
= 2,
67 } traverse_btree_flag_t
;
71 static errno_t
hfs_fsinfo_metadata_blocks(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
);
72 static errno_t
hfs_fsinfo_metadata_extents(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
);
73 static errno_t
hfs_fsinfo_metadata_percentfree(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
);
74 static errno_t
fsinfo_file_extent_count_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
75 static errno_t
fsinfo_file_extent_size_catalog_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
76 static errno_t
fsinfo_file_extent_size_overflow_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
77 static errno_t
fsinfo_file_size_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
78 static errno_t
fsinfo_dir_valence_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
79 static errno_t
fsinfo_name_size_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
80 static errno_t
fsinfo_xattr_size_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
81 static errno_t
traverse_btree(struct hfsmount
*hfsmp
, uint32_t btree_fileID
, int flags
, void *fsinfo
,
82 int (*callback
)(struct hfsmount
*, HFSPlusKey
*, HFSPlusRecord
*, void *));
83 static errno_t
hfs_fsinfo_free_extents(struct hfsmount
*hfsmp
, struct hfs_fsinfo_data
*fsinfo
);
84 static void fsinfo_free_extents_callback(void *data
, off_t free_extent_size
);
86 static errno_t
fsinfo_cprotect_count_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
88 static errno_t
fsinfo_symlink_size_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
);
91 * Entry function for all the fsinfo requests from hfs_vnop_ioctl()
92 * Depending on the type of request, this function will call the
93 * appropriate sub-function and return success or failure back to
96 errno_t
hfs_get_fsinfo(struct hfsmount
*hfsmp
, void *a_data
)
99 hfs_fsinfo
*fsinfo_union
;
100 uint32_t request_type
;
101 uint32_t header_len
= sizeof(hfs_fsinfo_header_t
);
103 fsinfo_union
= (hfs_fsinfo
*)a_data
;
104 request_type
= fsinfo_union
->header
.request_type
;
106 // Zero out output fields to fsinfo_union, keep the user input fields intact.
107 bzero((char *)fsinfo_union
+ header_len
, sizeof(hfs_fsinfo
) - header_len
);
109 switch (request_type
) {
110 case HFS_FSINFO_METADATA_BLOCKS_INFO
:
111 error
= hfs_fsinfo_metadata_blocks(hfsmp
, &(fsinfo_union
->metadata
));
114 case HFS_FSINFO_METADATA_EXTENTS
:
115 error
= hfs_fsinfo_metadata_extents(hfsmp
, &(fsinfo_union
->metadata
));
118 case HFS_FSINFO_METADATA_PERCENTFREE
:
119 error
= hfs_fsinfo_metadata_percentfree(hfsmp
, &(fsinfo_union
->metadata
));
122 case HFS_FSINFO_FILE_EXTENT_COUNT
:
123 /* Traverse catalog btree and invoke callback for all records */
124 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, TRAVERSE_BTREE_EXTENTS
, &(fsinfo_union
->data
), fsinfo_file_extent_count_callback
);
127 case HFS_FSINFO_FILE_EXTENT_SIZE
:
128 /* Traverse the catalog btree first */
129 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, 0, &(fsinfo_union
->data
), &fsinfo_file_extent_size_catalog_callback
);
133 /* Traverse the overflow extents btree now */
134 error
= traverse_btree(hfsmp
, kHFSExtentsFileID
, 0, &(fsinfo_union
->data
), &fsinfo_file_extent_size_overflow_callback
);
137 case HFS_FSINFO_FILE_SIZE
:
138 /* Traverse catalog btree and invoke callback for all records */
139 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, 0, &(fsinfo_union
->data
), &fsinfo_file_size_callback
);
142 case HFS_FSINFO_DIR_VALENCE
:
143 /* Traverse catalog btree and invoke callback for all records */
144 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, 0, &(fsinfo_union
->data
), &fsinfo_dir_valence_callback
);
147 case HFS_FSINFO_NAME_SIZE
:
148 /* Traverse catalog btree and invoke callback for all records */
149 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, 0, &(fsinfo_union
->name
), &fsinfo_name_size_callback
);
152 case HFS_FSINFO_XATTR_SIZE
:
153 /* Traverse attribute btree and invoke callback for all records */
154 error
= traverse_btree(hfsmp
, kHFSAttributesFileID
, 0, &(fsinfo_union
->data
), &fsinfo_xattr_size_callback
);
157 case HFS_FSINFO_FREE_EXTENTS
:
158 error
= hfs_fsinfo_free_extents(hfsmp
, &(fsinfo_union
->data
));
161 case HFS_FSINFO_SYMLINK_SIZE
:
162 /* Traverse catalog btree and invoke callback for all records */
163 error
= traverse_btree(hfsmp
, kHFSCatalogFileID
, 0, &(fsinfo_union
->data
), &fsinfo_symlink_size_callback
);
167 case HFS_FSINFO_FILE_CPROTECT_COUNT
:
168 /* Traverse attribute btree and invoke callback for all records */
169 error
= traverse_btree(hfsmp
, kHFSAttributesFileID
, TRAVERSE_BTREE_XATTR_CPROTECT
, &(fsinfo_union
->cprotect
), &fsinfo_cprotect_count_callback
);
181 * This function provides information about total number of allocation blocks
182 * for each individual metadata file.
185 hfs_fsinfo_metadata_blocks(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
)
188 int ret_lockflags
= 0;
191 * Getting number of allocation blocks for all metadata files
192 * should be a relatively quick operation, so we grab locks for all
193 * the btrees at the same time
195 lockflags
= SFL_CATALOG
| SFL_EXTENTS
| SFL_BITMAP
| SFL_ATTRIBUTE
;
196 ret_lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_SHARED_LOCK
);
198 /* Get information about all the btrees */
199 fsinfo
->extents
= hfsmp
->hfs_extents_cp
->c_datafork
->ff_blocks
;
200 fsinfo
->catalog
= hfsmp
->hfs_catalog_cp
->c_datafork
->ff_blocks
;
201 fsinfo
->allocation
= hfsmp
->hfs_allocation_cp
->c_datafork
->ff_blocks
;
202 if (hfsmp
->hfs_attribute_cp
)
203 fsinfo
->attribute
= hfsmp
->hfs_attribute_cp
->c_datafork
->ff_blocks
;
205 fsinfo
->attribute
= 0;
207 /* Done with btrees, give up the locks */
208 hfs_systemfile_unlock(hfsmp
, ret_lockflags
);
210 /* Get information about journal file */
211 fsinfo
->journal
= howmany(hfsmp
->jnl_size
, hfsmp
->blockSize
);
217 * Helper function to count the number of valid extents in a file fork structure
220 hfs_count_extents_fp(struct filefork
*ff
)
224 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
225 if (ff
->ff_data
.cf_extents
[i
].blockCount
== 0) {
235 * This is a helper function that counts the total number of valid
236 * extents in all the overflow extent records for given fileID
237 * in overflow extents btree
240 hfs_count_overflow_extents(struct hfsmount
*hfsmp
, uint32_t fileID
, uint32_t *num_extents
)
244 struct BTreeIterator
*iterator
= NULL
;
245 FSBufferDescriptor btdata
;
246 HFSPlusExtentKey
*extentKey
;
247 HFSPlusExtentRecord extentData
;
248 uint32_t extent_count
= 0;
251 fcb
= VTOF(hfsmp
->hfs_extents_vp
);
252 iterator
= hfs_mallocz(sizeof(struct BTreeIterator
));
254 extentKey
= (HFSPlusExtentKey
*) &iterator
->key
;
255 extentKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
256 extentKey
->forkType
= kHFSDataForkType
;
257 extentKey
->fileID
= fileID
;
258 extentKey
->startBlock
= 0;
260 btdata
.bufferAddress
= &extentData
;
261 btdata
.itemSize
= sizeof(HFSPlusExtentRecord
);
262 btdata
.itemCount
= 1;
264 /* Search for overflow extent record */
265 error
= BTSearchRecord(fcb
, iterator
, &btdata
, NULL
, iterator
);
268 * We used startBlock of zero, so we will not find any records and errors
269 * are expected. It will also position the iterator just before the first
270 * overflow extent record for given fileID (if any).
272 if (error
&& error
!= fsBTRecordNotFoundErr
&& error
!= fsBTEndOfIterationErr
)
278 if (msleep(NULL
, NULL
, PINOD
| PCATCH
,
279 "hfs_fsinfo", NULL
) == EINTR
) {
284 error
= BTIterateRecord(fcb
, kBTreeNextRecord
, iterator
, &btdata
, NULL
);
286 /* These are expected errors, so mask them */
287 if (error
== fsBTRecordNotFoundErr
|| error
== fsBTEndOfIterationErr
) {
293 /* If we encounter different fileID, stop the iteration */
294 if (extentKey
->fileID
!= fileID
) {
298 if (extentKey
->forkType
!= kHFSDataForkType
)
301 /* This is our record of interest; only count the datafork extents. */
302 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
303 if (extentData
[i
].blockCount
== 0) {
311 hfs_free(iterator
, sizeof(*iterator
));
314 *num_extents
= extent_count
;
316 return MacToVFSError(error
);
320 * This function provides information about total number of extents (including
321 * extents from overflow extents btree, if any) for each individual metadata
325 hfs_fsinfo_metadata_extents(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
)
329 int ret_lockflags
= 0;
330 uint32_t overflow_count
;
333 * Counting the number of extents for all metadata files should
334 * be a relatively quick operation, so we grab locks for all the
335 * btrees at the same time
337 lockflags
= SFL_CATALOG
| SFL_EXTENTS
| SFL_BITMAP
| SFL_ATTRIBUTE
;
338 ret_lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_SHARED_LOCK
);
340 /* Get number of extents for extents overflow btree */
341 fsinfo
->extents
= hfs_count_extents_fp(hfsmp
->hfs_extents_cp
->c_datafork
);
343 /* Get number of extents for catalog btree */
344 fsinfo
->catalog
= hfs_count_extents_fp(hfsmp
->hfs_catalog_cp
->c_datafork
);
345 if (fsinfo
->catalog
>= kHFSPlusExtentDensity
) {
346 error
= hfs_count_overflow_extents(hfsmp
, kHFSCatalogFileID
, &overflow_count
);
350 fsinfo
->catalog
+= overflow_count
;
353 /* Get number of extents for allocation file */
354 fsinfo
->allocation
= hfs_count_extents_fp(hfsmp
->hfs_allocation_cp
->c_datafork
);
355 if (fsinfo
->allocation
>= kHFSPlusExtentDensity
) {
356 error
= hfs_count_overflow_extents(hfsmp
, kHFSAllocationFileID
, &overflow_count
);
360 fsinfo
->allocation
+= overflow_count
;
364 * Get number of extents for attribute btree.
365 * hfs_attribute_cp might be NULL.
367 if (hfsmp
->hfs_attribute_cp
) {
368 fsinfo
->attribute
= hfs_count_extents_fp(hfsmp
->hfs_attribute_cp
->c_datafork
);
369 if (fsinfo
->attribute
>= kHFSPlusExtentDensity
) {
370 error
= hfs_count_overflow_extents(hfsmp
, kHFSAttributesFileID
, &overflow_count
);
374 fsinfo
->attribute
+= overflow_count
;
377 /* Journal always has one extent */
380 hfs_systemfile_unlock(hfsmp
, ret_lockflags
);
385 * Helper function to calculate percentage i.e. X is what percent of Y?
387 static inline uint32_t
388 hfs_percent(uint32_t X
, uint32_t Y
)
390 return (X
* 100ll) / Y
;
394 * This function provides percentage of free nodes vs total nodes for each
395 * individual metadata btrees, i.e. for catalog, overflow extents and
396 * attributes btree. This information is not applicable for allocation
397 * file and journal file.
400 hfs_fsinfo_metadata_percentfree(struct hfsmount
*hfsmp
, struct hfs_fsinfo_metadata
*fsinfo
)
403 int ret_lockflags
= 0;
404 BTreeControlBlockPtr btreePtr
;
405 uint32_t free_nodes
, total_nodes
;
408 * Getting total and used nodes for all metadata btrees should
409 * be a relatively quick operation, so we grab locks for all the
410 * btrees at the same time
412 lockflags
= SFL_CATALOG
| SFL_EXTENTS
| SFL_BITMAP
| SFL_ATTRIBUTE
;
413 ret_lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_SHARED_LOCK
);
415 /* Overflow extents btree */
416 btreePtr
= VTOF(hfsmp
->hfs_extents_vp
)->fcbBTCBPtr
;
417 total_nodes
= btreePtr
->totalNodes
;
418 free_nodes
= btreePtr
->freeNodes
;
419 fsinfo
->extents
= hfs_percent(free_nodes
, total_nodes
);
422 btreePtr
= VTOF(hfsmp
->hfs_catalog_vp
)->fcbBTCBPtr
;
423 total_nodes
= btreePtr
->totalNodes
;
424 free_nodes
= btreePtr
->freeNodes
;
425 fsinfo
->catalog
= hfs_percent(free_nodes
, total_nodes
);
427 /* Attributes btree */
428 if (hfsmp
->hfs_attribute_vp
) {
429 btreePtr
= VTOF(hfsmp
->hfs_attribute_vp
)->fcbBTCBPtr
;
430 total_nodes
= btreePtr
->totalNodes
;
431 free_nodes
= btreePtr
->freeNodes
;
432 fsinfo
->attribute
= hfs_percent(free_nodes
, total_nodes
);
435 hfs_systemfile_unlock(hfsmp
, ret_lockflags
);
440 * Helper function to calculate log base 2 for given number
443 hfs_log2(uint64_t entry
)
445 return (63 - __builtin_clzll(entry
|1));
449 * Helper function to account for input entry into the data
450 * array based on its log base 2 value
452 void hfs_fsinfo_data_add(struct hfs_fsinfo_data
*fsinfo
, uint64_t entry
)
457 * hfs_fsinfo_data is generic data structure to aggregate information like sizes
458 * or counts in buckets of power of 2. Each bucket represents a range of values
459 * that is determined based on its index in the array. Specifically, buckets[i]
460 * represents values that are greater than or equal to 2^(i-1) and less than 2^i,
461 * except the last bucket which represents range greater than or equal to 2^(i-1)
463 * The current maximum number of buckets is 41, so we can represent range from
464 * 0 up to 1TB in increments of power of 2, and then a catch-all bucket of
465 * anything that is greater than or equal to 1TB.
468 * bucket[0] -> greater than or equal to 0 and less than 1
469 * bucket[1] -> greater than or equal to 1 and less than 2
470 * bucket[10] -> greater than or equal to 2^(10-1) = 512 and less than 2^10 = 1024
471 * bucket[20] -> greater than or equal to 2^(20-1) = 512KB and less than 2^20 = 1MB
472 * bucket[41] -> greater than or equal to 2^(41-1) = 1TB
478 * Calculate log base 2 value for the entry.
479 * Account for this value in the appropriate bucket.
480 * The last bucket is a catch-all bucket of
481 * anything that is greater than or equal to 1TB
483 bucket
= MIN(hfs_log2(entry
) + 1, HFS_FSINFO_DATA_MAX_BUCKETS
-1);
484 ++fsinfo
->bucket
[bucket
];
486 /* Entry is zero, so account it in 0th offset */
492 * Function to traverse all the records of a btree and then call caller-provided
493 * callback function for every record found. The type of btree is chosen based
494 * on the fileID provided by the caller. This fuction grabs the correct locks
495 * depending on the type of btree it will be traversing and flags provided
498 * Note: It might drop and reacquire the locks during execution.
501 traverse_btree(struct hfsmount
*hfsmp
, uint32_t btree_fileID
, int flags
,
502 void *fsinfo
, int (*callback
)(struct hfsmount
*, HFSPlusKey
*, HFSPlusRecord
*, void *))
506 int ret_lockflags
= 0;
508 struct BTreeIterator
*iterator
= NULL
;
509 struct FSBufferDescriptor btdata
;
511 HFSPlusRecord record
;
513 uint64_t start
, timeout_abs
;
515 switch(btree_fileID
) {
516 case kHFSExtentsFileID
:
517 fcb
= VTOF(hfsmp
->hfs_extents_vp
);
518 lockflags
= SFL_EXTENTS
;
520 case kHFSCatalogFileID
:
521 fcb
= VTOF(hfsmp
->hfs_catalog_vp
);
522 lockflags
= SFL_CATALOG
;
524 case kHFSAttributesFileID
:
525 // Attributes file doesn’t exist, There are no records to iterate.
526 if (hfsmp
->hfs_attribute_vp
== NULL
)
528 fcb
= VTOF(hfsmp
->hfs_attribute_vp
);
529 lockflags
= SFL_ATTRIBUTE
;
536 iterator
= hfs_mallocz(sizeof(struct BTreeIterator
));
538 /* The key is initialized to zero because we are traversing entire btree */
539 key
= (HFSPlusKey
*)&iterator
->key
;
541 if (flags
& TRAVERSE_BTREE_EXTENTS
) {
542 lockflags
|= SFL_EXTENTS
;
545 btdata
.bufferAddress
= &record
;
546 btdata
.itemSize
= sizeof(HFSPlusRecord
);
547 btdata
.itemCount
= 1;
549 /* Lock btree for duration of traversal */
550 ret_lockflags
= hfs_systemfile_lock(hfsmp
, lockflags
, HFS_SHARED_LOCK
);
551 btree_operation
= kBTreeFirstRecord
;
553 nanoseconds_to_absolutetime(HFS_FSINFO_MAX_LOCKHELD_TIME
, &timeout_abs
);
554 start
= mach_absolute_time();
558 if (msleep(NULL
, NULL
, PINOD
| PCATCH
,
559 "hfs_fsinfo", NULL
) == EINTR
) {
564 error
= BTIterateRecord(fcb
, btree_operation
, iterator
, &btdata
, NULL
);
566 if (error
== fsBTRecordNotFoundErr
|| error
== fsBTEndOfIterationErr
) {
571 /* Lookup next btree record on next call to BTIterateRecord() */
572 btree_operation
= kBTreeNextRecord
;
574 /* Call our callback function and stop iteration if there are any errors */
575 error
= callback(hfsmp
, key
, &record
, fsinfo
);
580 /* let someone else use the tree after we've processed over HFS_FSINFO_MAX_LOCKHELD_TIME */
581 if ((mach_absolute_time() - start
) >= timeout_abs
) {
583 /* release b-tree locks and let someone else get the lock */
584 hfs_systemfile_unlock (hfsmp
, ret_lockflags
);
586 /* add tsleep here to force context switch and fairness */
587 tsleep((caddr_t
)hfsmp
, PRIBIO
, "hfs_fsinfo", 1);
590 * re-acquire the locks in the same way that we wanted them originally.
591 * note: it is subtle but worth pointing out that in between the time that we
592 * released and now want to re-acquire these locks that the b-trees may have shifted
593 * slightly but significantly. For example, the catalog or other b-tree could have grown
594 * past 8 extents and now requires the extents lock to be held in order to be safely
595 * manipulated. We can't be sure of the state of the b-tree from where we last left off.
598 ret_lockflags
= hfs_systemfile_lock (hfsmp
, lockflags
, HFS_SHARED_LOCK
);
601 * It's highly likely that the search key we stashed away before dropping lock
602 * no longer points to an existing item. Iterator's IterateRecord is able to
603 * re-position itself and process the next record correctly. With lock dropped,
604 * there might be records missed for statistic gathering, which is ok. The
605 * point is to get aggregate values.
608 start
= mach_absolute_time();
610 /* loop back around and get another record */
614 hfs_systemfile_unlock(hfsmp
, ret_lockflags
);
615 hfs_free(iterator
, sizeof(*iterator
));
616 return MacToVFSError(error
);
620 * Callback function to get distribution of number of extents
621 * for all user files in given file system. Note that this only
622 * accounts for data fork, no resource fork.
625 fsinfo_file_extent_count_callback(struct hfsmount
*hfsmp
,
626 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
630 uint32_t num_extents
= 0;
631 uint32_t num_overflow
= 0;
634 if (record
->file_record
.recordType
== kHFSPlusFileRecord
) {
635 /* Count total number of extents for this file */
636 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
637 blockCount
= record
->file_record
.dataFork
.extents
[i
].blockCount
;
638 if (blockCount
== 0) {
643 /* This file has overflow extent records, so search overflow btree */
644 if (num_extents
>= kHFSPlusExtentDensity
) {
645 /* The caller also hold extents overflow btree lock */
646 error
= hfs_count_overflow_extents(hfsmp
, record
->file_record
.fileID
, &num_overflow
);
650 num_extents
+= num_overflow
;
652 hfs_fsinfo_data_add(data
, num_extents
);
659 * Callback function to get distribution of individual extent sizes
660 * (in bytes) for all user files in given file system from catalog
661 * btree only. Note that this only accounts for data fork, no resource
664 static errno_t
fsinfo_file_extent_size_catalog_callback(struct hfsmount
*hfsmp
,
665 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
669 uint64_t extent_size
;
671 if (record
->file_record
.recordType
== kHFSPlusFileRecord
) {
672 /* Traverse through all valid extents */
673 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
674 blockCount
= record
->file_record
.dataFork
.extents
[i
].blockCount
;
675 if (blockCount
== 0) {
678 extent_size
= hfs_blk_to_bytes(blockCount
, hfsmp
->blockSize
);
679 hfs_fsinfo_data_add(data
, extent_size
);
686 * Callback function to get distribution of individual extent sizes
687 * (in bytes) for all user files in given file system from overflow
688 * extents btree only. Note that this only accounts for data fork,
691 static errno_t
fsinfo_file_extent_size_overflow_callback(struct hfsmount
*hfsmp
,
692 HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
696 uint64_t extent_size
;
698 if (key
->extent_key
.fileID
>= kHFSFirstUserCatalogNodeID
) {
699 // Only count the data fork extents.
700 if (key
->extent_key
.forkType
== kHFSDataForkType
) {
701 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
702 blockCount
= record
->extent_record
[i
].blockCount
;
703 if (blockCount
== 0) {
706 extent_size
= hfs_blk_to_bytes(blockCount
, hfsmp
->blockSize
);
707 hfs_fsinfo_data_add(data
, extent_size
);
715 * Callback function to get distribution of file sizes (in bytes)
716 * for all user files in given file system. Note that this only
717 * accounts for data fork, no resource fork.
719 static errno_t
fsinfo_file_size_callback(__unused
struct hfsmount
*hfsmp
,
720 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
722 if (record
->file_record
.recordType
== kHFSPlusFileRecord
) {
723 /* Record of interest, account for the size in the bucket */
724 hfs_fsinfo_data_add(data
, record
->file_record
.dataFork
.logicalSize
);
730 * Callback function to get distribution of directory valence
731 * for all directories in the given file system.
733 static errno_t
fsinfo_dir_valence_callback(__unused
struct hfsmount
*hfsmp
,
734 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
736 if (record
->folder_record
.recordType
== kHFSPlusFolderRecord
) {
737 hfs_fsinfo_data_add(data
, record
->folder_record
.valence
);
743 * Callback function to get distribution of number of unicode
744 * characters in name for all files and directories for a given
747 static errno_t
fsinfo_name_size_callback(__unused
struct hfsmount
*hfsmp
,
748 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
750 struct hfs_fsinfo_name
*fsinfo
= (struct hfs_fsinfo_name
*)data
;
753 if ((record
->folder_record
.recordType
== kHFSPlusFolderThreadRecord
) ||
754 (record
->folder_record
.recordType
== kHFSPlusFileThreadRecord
)) {
755 length
= record
->thread_record
.nodeName
.length
;
756 /* Make sure that the nodeName is bounded, otherwise return error */
757 if (length
> kHFSPlusMaxFileNameChars
) {
761 // sanity check for a name length of zero, which isn't valid on disk.
765 /* Round it down to nearest multiple of 5 to match our buckets granularity */
766 length
= (length
- 1)/ 5;
767 /* Account this value into our bucket */
768 fsinfo
->bucket
[length
]++;
774 * Callback function to get distribution of size of all extended
775 * attributes for a given file system.
777 static errno_t
fsinfo_xattr_size_callback(__unused
struct hfsmount
*hfsmp
,
778 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
780 if (record
->attr_record
.recordType
== kHFSPlusAttrInlineData
) {
781 /* Inline attribute */
782 hfs_fsinfo_data_add(data
, record
->attr_record
.attrData
.attrSize
);
783 } else if (record
->attr_record
.recordType
== kHFSPlusAttrForkData
) {
784 /* Larger attributes with extents information */
785 hfs_fsinfo_data_add(data
, record
->attr_record
.forkData
.theFork
.logicalSize
);
792 * Callback function to get distribution of free space extents for a given file system.
794 static void fsinfo_free_extents_callback(void *data
, off_t free_extent_size
)
796 // Assume a minimum of 4 KB block size
797 hfs_fsinfo_data_add(data
, free_extent_size
/ 4096);
801 * Function to get distribution of free space extents for a given file system.
803 static errno_t
hfs_fsinfo_free_extents(struct hfsmount
*hfsmp
, struct hfs_fsinfo_data
*fsinfo
)
805 return hfs_find_free_extents(hfsmp
, &fsinfo_free_extents_callback
, fsinfo
);
809 * Callback function to get distribution of symblock link sizes (in bytes)
810 * for all user files in given file system. Note that this only
811 * accounts for data fork, no resource fork.
813 static errno_t
fsinfo_symlink_size_callback(__unused
struct hfsmount
*hfsmp
,
814 __unused HFSPlusKey
*key
, HFSPlusRecord
*record
, void *data
)
816 if (record
->file_record
.recordType
== kHFSPlusFileRecord
) {
817 /* Record of interest, account for the size in the bucket */
818 if (S_ISLNK(record
->file_record
.bsdInfo
.fileMode
))
819 hfs_fsinfo_data_add((struct hfs_fsinfo_data
*)data
, record
->file_record
.dataFork
.logicalSize
);
826 * Callback function to get total number of files/directories
827 * for each content protection class
829 static int fsinfo_cprotect_count_callback(struct hfsmount
*hfsmp
, HFSPlusKey
*key
,
830 HFSPlusRecord
*record
, void *data
)
832 struct hfs_fsinfo_cprotect
*fsinfo
= (struct hfs_fsinfo_cprotect
*)data
;
833 static const uint16_t cp_xattrname_utf16
[] = CONTENT_PROTECTION_XATTR_NAME_CHARS
;
835 * NOTE: cp_xattrname_utf16_len is the number of UTF-16 code units in
836 * the EA name string.
838 static const size_t cp_xattrname_utf16_len
= sizeof(cp_xattrname_utf16
)/2;
839 struct cp_xattr_v5
*xattr
;
840 size_t xattr_len
= sizeof(struct cp_xattr_v5
);
841 struct cprotect cp_entry
;
842 struct cprotect
*cp_entryp
= &cp_entry
;
845 /* Content protect xattrs are inline attributes only, so skip all others */
846 if (record
->attr_record
.recordType
!= kHFSPlusAttrInlineData
)
849 /* We only look at content protection xattrs */
850 if ((key
->attr_key
.attrNameLen
!= cp_xattrname_utf16_len
) ||
851 (bcmp(key
->attr_key
.attrName
, cp_xattrname_utf16
, 2 * cp_xattrname_utf16_len
))) {
855 xattr
= (struct cp_xattr_v5
*)((void *)(record
->attr_record
.attrData
.attrData
));
856 error
= cp_read_xattr_v5(hfsmp
, xattr
, xattr_len
, (cprotect_t
*)&cp_entryp
,
857 CP_GET_XATTR_BASIC_INFO
);
861 /* No key present, skip this record */
862 if (!ISSET(cp_entry
.cp_flags
, CP_HAS_A_KEY
))
865 /* Now account for the persistent class */
866 switch (CP_CLASS(cp_entry
.cp_pclass
)) {
867 case PROTECTION_CLASS_A
:
870 case PROTECTION_CLASS_B
:
873 case PROTECTION_CLASS_C
:
876 case PROTECTION_CLASS_D
:
879 case PROTECTION_CLASS_E
:
882 case PROTECTION_CLASS_F
: