]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_fsinfo.c
xnu-3247.1.106.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_fsinfo.c
1 /*
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. 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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
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>
34 #include <sys/stat.h>
35
36 #include "hfs.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"
42
43 #include <hfs/hfs_cprotect.h>
44
45
46 union HFSPlusRecord {
47 HFSPlusCatalogFolder folder_record;
48 HFSPlusCatalogFile file_record;
49 HFSPlusCatalogThread thread_record;
50 HFSPlusExtentRecord extent_record;
51 HFSPlusAttrRecord attr_record;
52 };
53 typedef union HFSPlusRecord HFSPlusRecord;
54
55 union HFSPlusKey {
56 HFSPlusExtentKey extent_key;
57 HFSPlusAttrKey attr_key;
58 };
59 typedef union HFSPlusKey HFSPlusKey;
60
61 typedef enum traverse_btree_flag {
62
63 //If set, extents btree will also be traversed along with catalog btree, so grab correct locks upfront
64 TRAVERSE_BTREE_EXTENTS = 1,
65
66 // Getting content-protection attributes, allocate enough space to accomodate the records.
67 TRAVERSE_BTREE_XATTR_CPROTECT = 2,
68
69 } traverse_btree_flag_t;
70
71
72
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);
87 #if CONFIG_PROTECT
88 static errno_t fsinfo_cprotect_count_callback(struct hfsmount *hfsmp, HFSPlusKey *key, HFSPlusRecord *record, void *data);
89 #endif
90 static errno_t fsinfo_symlink_size_callback(struct hfsmount *hfsmp, HFSPlusKey *key, HFSPlusRecord *record, void *data);
91
92 /*
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
96 * the caller.
97 */
98 __private_extern__
99 errno_t hfs_get_fsinfo(struct hfsmount *hfsmp, void *a_data)
100 {
101 int error = 0;
102 hfs_fsinfo *fsinfo_union;
103 uint32_t request_type;
104 uint32_t header_len = sizeof(hfs_fsinfo_header_t);
105
106 fsinfo_union = (hfs_fsinfo *)a_data;
107 request_type = fsinfo_union->header.request_type;
108
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);
111
112 switch (request_type) {
113 case HFS_FSINFO_METADATA_BLOCKS_INFO:
114 error = hfs_fsinfo_metadata_blocks(hfsmp, &(fsinfo_union->metadata));
115 break;
116
117 case HFS_FSINFO_METADATA_EXTENTS:
118 error = hfs_fsinfo_metadata_extents(hfsmp, &(fsinfo_union->metadata));
119 break;
120
121 case HFS_FSINFO_METADATA_PERCENTFREE:
122 error = hfs_fsinfo_metadata_percentfree(hfsmp, &(fsinfo_union->metadata));
123 break;
124
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);
128 break;
129
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);
133 if (error) {
134 break;
135 }
136 /* Traverse the overflow extents btree now */
137 error = traverse_btree(hfsmp, kHFSExtentsFileID, 0, &(fsinfo_union->data), &fsinfo_file_extent_size_overflow_callback);
138 break;
139
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);
143 break;
144
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);
148 break;
149
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);
153 break;
154
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);
158 break;
159
160 case HFS_FSINFO_FREE_EXTENTS:
161 error = hfs_fsinfo_free_extents(hfsmp, &(fsinfo_union->data));
162 break;
163
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);
167 break;
168
169 #if CONFIG_PROTECT
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);
173 break;
174 #endif
175
176 default:
177 return ENOTSUP;
178 };
179
180 return error;
181 }
182
183 /*
184 * This function provides information about total number of allocation blocks
185 * for each individual metadata file.
186 */
187 static errno_t
188 hfs_fsinfo_metadata_blocks(struct hfsmount *hfsmp, struct hfs_fsinfo_metadata *fsinfo)
189 {
190 int lockflags = 0;
191 int ret_lockflags = 0;
192
193 /*
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
197 */
198 lockflags = SFL_CATALOG | SFL_EXTENTS | SFL_BITMAP | SFL_ATTRIBUTE;
199 ret_lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_SHARED_LOCK);
200
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;
207 else
208 fsinfo->attribute = 0;
209
210 /* Done with btrees, give up the locks */
211 hfs_systemfile_unlock(hfsmp, ret_lockflags);
212
213 /* Get information about journal file */
214 fsinfo->journal = howmany(hfsmp->jnl_size, hfsmp->blockSize);
215
216 return 0;
217 }
218
219 /*
220 * Helper function to count the number of valid extents in a file fork structure
221 */
222 static uint32_t
223 hfs_count_extents_fp(struct filefork *ff)
224 {
225 int i;
226 uint32_t count = 0;
227 for (i = 0; i < kHFSPlusExtentDensity; i++) {
228 if (ff->ff_data.cf_extents[i].blockCount == 0) {
229 break;
230 }
231 count++;
232 }
233 return count;
234 }
235
236
237 /*
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
241 */
242 static errno_t
243 hfs_count_overflow_extents(struct hfsmount *hfsmp, uint32_t fileID, uint32_t *num_extents)
244 {
245 int error;
246 FCB *fcb;
247 struct BTreeIterator *iterator = NULL;
248 FSBufferDescriptor btdata;
249 HFSPlusExtentKey *extentKey;
250 HFSPlusExtentRecord extentData;
251 uint32_t extent_count = 0;
252 int i;
253
254 fcb = VTOF(hfsmp->hfs_extents_vp);
255 MALLOC(iterator, struct BTreeIterator *, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK | M_ZERO);
256
257 extentKey = (HFSPlusExtentKey *) &iterator->key;
258 extentKey->keyLength = kHFSPlusExtentKeyMaximumLength;
259 extentKey->forkType = kHFSDataForkType;
260 extentKey->fileID = fileID;
261 extentKey->startBlock = 0;
262
263 btdata.bufferAddress = &extentData;
264 btdata.itemSize = sizeof(HFSPlusExtentRecord);
265 btdata.itemCount = 1;
266
267 /* Search for overflow extent record */
268 error = BTSearchRecord(fcb, iterator, &btdata, NULL, iterator);
269
270 /*
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).
274 */
275 if (error && error != fsBTRecordNotFoundErr && error != fsBTEndOfIterationErr)
276 goto out;
277 error = 0;
278
279 for (;;) {
280
281 if (msleep(NULL, NULL, PINOD | PCATCH,
282 "hfs_fsinfo", NULL) == EINTR) {
283 error = EINTR;
284 break;
285 }
286
287 error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
288 if (error != 0) {
289 /* These are expected errors, so mask them */
290 if (error == fsBTRecordNotFoundErr || error == fsBTEndOfIterationErr) {
291 error = 0;
292 }
293 break;
294 }
295
296 /* If we encounter different fileID, stop the iteration */
297 if (extentKey->fileID != fileID) {
298 break;
299 }
300
301 if (extentKey->forkType != kHFSDataForkType)
302 break;
303
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) {
307 break;
308 }
309 extent_count++;
310 }
311 }
312
313 out:
314 FREE(iterator, M_TEMP);
315
316 if (error == 0) {
317 *num_extents = extent_count;
318 }
319 return MacToVFSError(error);
320 }
321
322 /*
323 * This function provides information about total number of extents (including
324 * extents from overflow extents btree, if any) for each individual metadata
325 * file.
326 */
327 static errno_t
328 hfs_fsinfo_metadata_extents(struct hfsmount *hfsmp, struct hfs_fsinfo_metadata *fsinfo)
329 {
330 int error = 0;
331 int lockflags = 0;
332 int ret_lockflags = 0;
333 uint32_t overflow_count;
334
335 /*
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
339 */
340 lockflags = SFL_CATALOG | SFL_EXTENTS | SFL_BITMAP | SFL_ATTRIBUTE;
341 ret_lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_SHARED_LOCK);
342
343 /* Get number of extents for extents overflow btree */
344 fsinfo->extents = hfs_count_extents_fp(hfsmp->hfs_extents_cp->c_datafork);
345
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);
350 if (error) {
351 goto out;
352 }
353 fsinfo->catalog += overflow_count;
354 }
355
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);
360 if (error) {
361 goto out;
362 }
363 fsinfo->allocation += overflow_count;
364 }
365
366 /*
367 * Get number of extents for attribute btree.
368 * hfs_attribute_cp might be NULL.
369 */
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);
374 if (error) {
375 goto out;
376 }
377 fsinfo->attribute += overflow_count;
378 }
379 }
380 /* Journal always has one extent */
381 fsinfo->journal = 1;
382 out:
383 hfs_systemfile_unlock(hfsmp, ret_lockflags);
384 return error;
385 }
386
387 /*
388 * Helper function to calculate percentage i.e. X is what percent of Y?
389 */
390 static inline uint32_t
391 hfs_percent(uint32_t X, uint32_t Y)
392 {
393 return (X * 100ll) / Y;
394 }
395
396 /*
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.
401 */
402 static errno_t
403 hfs_fsinfo_metadata_percentfree(struct hfsmount *hfsmp, struct hfs_fsinfo_metadata *fsinfo)
404 {
405 int lockflags = 0;
406 int ret_lockflags = 0;
407 BTreeControlBlockPtr btreePtr;
408 uint32_t free_nodes, total_nodes;
409
410 /*
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
414 */
415 lockflags = SFL_CATALOG | SFL_EXTENTS | SFL_BITMAP | SFL_ATTRIBUTE;
416 ret_lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_SHARED_LOCK);
417
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);
423
424 /* Catalog btree */
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);
429
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);
436 }
437
438 hfs_systemfile_unlock(hfsmp, ret_lockflags);
439 return 0;
440 }
441
442 /*
443 * Helper function to calculate log base 2 for given number
444 */
445 static inline int
446 hfs_log2(uint64_t entry)
447 {
448 return (63 - __builtin_clzll(entry|1));
449 }
450
451 /*
452 * Helper function to account for input entry into the data
453 * array based on its log base 2 value
454 */
455 __private_extern__
456 void hfs_fsinfo_data_add(struct hfs_fsinfo_data *fsinfo, uint64_t entry)
457 {
458 /*
459 * From hfs_fsctl.h -
460 *
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)
466 *
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.
470 *
471 * For example,
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
477 */
478 uint32_t bucket;
479
480 if (entry) {
481 /*
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
486 */
487 bucket = MIN(hfs_log2(entry) + 1, HFS_FSINFO_DATA_MAX_BUCKETS-1);
488 ++fsinfo->bucket[bucket];
489 } else {
490 /* Entry is zero, so account it in 0th offset */
491 fsinfo->bucket[0]++;
492 }
493 }
494
495 /*
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
500 * by the caller.
501 *
502 * Note: It might drop and reacquire the locks during execution.
503 */
504 static errno_t
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 *))
507 {
508 int error = 0;
509 int lockflags = 0;
510 int ret_lockflags = 0;
511 FCB *fcb;
512 struct BTreeIterator *iterator = NULL;
513 struct FSBufferDescriptor btdata;
514 int btree_operation;
515 HFSPlusRecord record;
516 HFSPlusKey *key;
517 uint64_t start, timeout_abs;
518
519 switch(btree_fileID) {
520 case kHFSExtentsFileID:
521 fcb = VTOF(hfsmp->hfs_extents_vp);
522 lockflags = SFL_EXTENTS;
523 break;
524 case kHFSCatalogFileID:
525 fcb = VTOF(hfsmp->hfs_catalog_vp);
526 lockflags = SFL_CATALOG;
527 break;
528 case kHFSAttributesFileID:
529 // Attributes file doesn’t exist, There are no records to iterate.
530 if (hfsmp->hfs_attribute_vp == NULL)
531 return error;
532 fcb = VTOF(hfsmp->hfs_attribute_vp);
533 lockflags = SFL_ATTRIBUTE;
534 break;
535
536 default:
537 return EINVAL;
538 }
539
540 MALLOC(iterator, struct BTreeIterator *, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK | M_ZERO);
541
542 /* The key is initialized to zero because we are traversing entire btree */
543 key = (HFSPlusKey *)&iterator->key;
544
545 if (flags & TRAVERSE_BTREE_EXTENTS) {
546 lockflags |= SFL_EXTENTS;
547 }
548
549 btdata.bufferAddress = &record;
550 btdata.itemSize = sizeof(HFSPlusRecord);
551 btdata.itemCount = 1;
552
553 /* Lock btree for duration of traversal */
554 ret_lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_SHARED_LOCK);
555 btree_operation = kBTreeFirstRecord;
556
557 nanoseconds_to_absolutetime(HFS_FSINFO_MAX_LOCKHELD_TIME, &timeout_abs);
558 start = mach_absolute_time();
559
560 while (1) {
561
562 if (msleep(NULL, NULL, PINOD | PCATCH,
563 "hfs_fsinfo", NULL) == EINTR) {
564 error = EINTR;
565 break;
566 }
567
568 error = BTIterateRecord(fcb, btree_operation, iterator, &btdata, NULL);
569 if (error != 0) {
570 if (error == fsBTRecordNotFoundErr || error == fsBTEndOfIterationErr) {
571 error = 0;
572 }
573 break;
574 }
575 /* Lookup next btree record on next call to BTIterateRecord() */
576 btree_operation = kBTreeNextRecord;
577
578 /* Call our callback function and stop iteration if there are any errors */
579 error = callback(hfsmp, key, &record, fsinfo);
580 if (error) {
581 break;
582 }
583
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) {
586
587 /* release b-tree locks and let someone else get the lock */
588 hfs_systemfile_unlock (hfsmp, ret_lockflags);
589
590 /* add tsleep here to force context switch and fairness */
591 tsleep((caddr_t)hfsmp, PRIBIO, "hfs_fsinfo", 1);
592
593 /*
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.
600 */
601
602 ret_lockflags = hfs_systemfile_lock (hfsmp, lockflags, HFS_SHARED_LOCK);
603
604 /*
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.
610 */
611
612 start = mach_absolute_time();
613
614 /* loop back around and get another record */
615 }
616 }
617
618 hfs_systemfile_unlock(hfsmp, ret_lockflags);
619 FREE (iterator, M_TEMP);
620 return MacToVFSError(error);
621 }
622
623 /*
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.
627 */
628 static errno_t
629 fsinfo_file_extent_count_callback(struct hfsmount *hfsmp,
630 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
631 {
632 int i;
633 int error = 0;
634 uint32_t num_extents = 0;
635 uint32_t num_overflow = 0;
636 uint32_t blockCount;
637
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) {
643 break;
644 }
645 num_extents++;
646 }
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);
651 if (error) {
652 goto out;
653 }
654 num_extents += num_overflow;
655 }
656 hfs_fsinfo_data_add(data, num_extents);
657 }
658 out:
659 return error;
660 }
661
662 /*
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
666 * fork.
667 */
668 static errno_t fsinfo_file_extent_size_catalog_callback(__unused struct hfsmount *hfsmp,
669 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
670 {
671 int i;
672 uint32_t blockCount;
673 uint64_t extent_size;
674
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) {
680 break;
681 }
682 extent_size = hfs_blk_to_bytes(blockCount, hfsmp->blockSize);
683 hfs_fsinfo_data_add(data, extent_size);
684 }
685 }
686 return 0;
687 }
688
689 /*
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,
693 * no resource fork.
694 */
695 static errno_t fsinfo_file_extent_size_overflow_callback(__unused struct hfsmount *hfsmp,
696 HFSPlusKey *key, HFSPlusRecord *record, void *data)
697 {
698 int i;
699 uint32_t blockCount;
700 uint64_t extent_size;
701
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) {
708 break;
709 }
710 extent_size = hfs_blk_to_bytes(blockCount, hfsmp->blockSize);
711 hfs_fsinfo_data_add(data, extent_size);
712 }
713 }
714 }
715 return 0;
716 }
717
718 /*
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.
722 */
723 static errno_t fsinfo_file_size_callback(__unused struct hfsmount *hfsmp,
724 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
725 {
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);
729 }
730 return 0;
731 }
732
733 /*
734 * Callback function to get distribution of directory valence
735 * for all directories in the given file system.
736 */
737 static errno_t fsinfo_dir_valence_callback(__unused struct hfsmount *hfsmp,
738 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
739 {
740 if (record->folder_record.recordType == kHFSPlusFolderRecord) {
741 hfs_fsinfo_data_add(data, record->folder_record.valence);
742 }
743 return 0;
744 }
745
746 /*
747 * Callback function to get distribution of number of unicode
748 * characters in name for all files and directories for a given
749 * file system.
750 */
751 static errno_t fsinfo_name_size_callback(__unused struct hfsmount *hfsmp,
752 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
753 {
754 struct hfs_fsinfo_name *fsinfo = (struct hfs_fsinfo_name *)data;
755 uint32_t length;
756
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) {
762 return EIO;
763 }
764
765 // sanity check for a name length of zero, which isn't valid on disk.
766 if (length == 0)
767 return EIO;
768
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]++;
773 }
774 return 0;
775 }
776
777 /*
778 * Callback function to get distribution of size of all extended
779 * attributes for a given file system.
780 */
781 static errno_t fsinfo_xattr_size_callback(__unused struct hfsmount *hfsmp,
782 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
783 {
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);
790 }
791 return 0;
792 }
793
794
795 /*
796 * Callback function to get distribution of free space extents for a given file system.
797 */
798 static void fsinfo_free_extents_callback(void *data, off_t free_extent_size)
799 {
800 // Assume a minimum of 4 KB block size
801 hfs_fsinfo_data_add(data, free_extent_size / 4096);
802 }
803
804 /*
805 * Function to get distribution of free space extents for a given file system.
806 */
807 static errno_t hfs_fsinfo_free_extents(struct hfsmount *hfsmp, struct hfs_fsinfo_data *fsinfo)
808 {
809 return hfs_find_free_extents(hfsmp, &fsinfo_free_extents_callback, fsinfo);
810 }
811
812 /*
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.
816 */
817 static errno_t fsinfo_symlink_size_callback(__unused struct hfsmount *hfsmp,
818 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
819 {
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);
824 }
825 return 0;
826 }
827
828 #if CONFIG_PROTECT
829 /*
830 * Callback function to get total number of files/directories
831 * for each content protection class
832 */
833 static int fsinfo_cprotect_count_callback(struct hfsmount *hfsmp, HFSPlusKey *key,
834 HFSPlusRecord *record, void *data)
835 {
836 struct hfs_fsinfo_cprotect *fsinfo = (struct hfs_fsinfo_cprotect *)data;
837 static const uint16_t cp_xattrname_utf16[] = CONTENT_PROTECTION_XATTR_NAME_CHARS;
838 /*
839 * NOTE: cp_xattrname_utf16_len is the number of UTF-16 code units in
840 * the EA name string.
841 */
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;
847 int error = 0;
848
849 /* Content protect xattrs are inline attributes only, so skip all others */
850 if (record->attr_record.recordType != kHFSPlusAttrInlineData)
851 return 0;
852
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))) {
856 return 0;
857 }
858
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);
862 if (error)
863 return 0;
864
865 /* No key present, skip this record */
866 if (!ISSET(cp_entry.cp_flags, CP_HAS_A_KEY))
867 return 0;
868
869 /* Now account for the persistent class */
870 switch (CP_CLASS(cp_entry.cp_pclass)) {
871 case PROTECTION_CLASS_A:
872 fsinfo->class_A++;
873 break;
874 case PROTECTION_CLASS_B:
875 fsinfo->class_B++;
876 break;
877 case PROTECTION_CLASS_C:
878 fsinfo->class_C++;
879 break;
880 case PROTECTION_CLASS_D:
881 fsinfo->class_D++;
882 break;
883 case PROTECTION_CLASS_E:
884 fsinfo->class_E++;
885 break;
886 case PROTECTION_CLASS_F:
887 fsinfo->class_F++;
888 break;
889 };
890
891 return 0;
892 }
893 #endif