]> git.saurik.com Git - apple/hfs.git/blob - core/hfs_fsinfo.c
hfs-522.100.5.tar.gz
[apple/hfs.git] / core / hfs_fsinfo.c
1 /*
2 * Copyright (c) 2014-2015 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/xattr.h>
30 #include <sys/utfconv.h>
31 #include <libkern/OSByteOrder.h>
32 #include <sys/stat.h>
33
34 #include "hfs.h"
35 #include "hfs_fsctl.h"
36 #include "hfs_endian.h"
37 #include "BTreesInternal.h"
38 #include "BTreesPrivate.h"
39 #include "FileMgrInternal.h"
40
41 #include "hfs_cprotect.h"
42
43
44 union HFSPlusRecord {
45 HFSPlusCatalogFolder folder_record;
46 HFSPlusCatalogFile file_record;
47 HFSPlusCatalogThread thread_record;
48 HFSPlusExtentRecord extent_record;
49 HFSPlusAttrRecord attr_record;
50 };
51 typedef union HFSPlusRecord HFSPlusRecord;
52
53 union HFSPlusKey {
54 HFSPlusExtentKey extent_key;
55 HFSPlusAttrKey attr_key;
56 };
57 typedef union HFSPlusKey HFSPlusKey;
58
59 typedef enum traverse_btree_flag {
60
61 //If set, extents btree will also be traversed along with catalog btree, so grab correct locks upfront
62 TRAVERSE_BTREE_EXTENTS = 1,
63
64 // Getting content-protection attributes, allocate enough space to accomodate the records.
65 TRAVERSE_BTREE_XATTR_CPROTECT = 2,
66
67 } traverse_btree_flag_t;
68
69
70
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);
85 #if CONFIG_PROTECT
86 static errno_t fsinfo_cprotect_count_callback(struct hfsmount *hfsmp, HFSPlusKey *key, HFSPlusRecord *record, void *data);
87 #endif
88 static errno_t fsinfo_symlink_size_callback(struct hfsmount *hfsmp, HFSPlusKey *key, HFSPlusRecord *record, void *data);
89
90 /*
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
94 * the caller.
95 */
96 errno_t hfs_get_fsinfo(struct hfsmount *hfsmp, void *a_data)
97 {
98 int error = 0;
99 hfs_fsinfo *fsinfo_union;
100 uint32_t request_type;
101 uint32_t header_len = sizeof(hfs_fsinfo_header_t);
102
103 fsinfo_union = (hfs_fsinfo *)a_data;
104 request_type = fsinfo_union->header.request_type;
105
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);
108
109 switch (request_type) {
110 case HFS_FSINFO_METADATA_BLOCKS_INFO:
111 error = hfs_fsinfo_metadata_blocks(hfsmp, &(fsinfo_union->metadata));
112 break;
113
114 case HFS_FSINFO_METADATA_EXTENTS:
115 error = hfs_fsinfo_metadata_extents(hfsmp, &(fsinfo_union->metadata));
116 break;
117
118 case HFS_FSINFO_METADATA_PERCENTFREE:
119 error = hfs_fsinfo_metadata_percentfree(hfsmp, &(fsinfo_union->metadata));
120 break;
121
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);
125 break;
126
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);
130 if (error) {
131 break;
132 }
133 /* Traverse the overflow extents btree now */
134 error = traverse_btree(hfsmp, kHFSExtentsFileID, 0, &(fsinfo_union->data), &fsinfo_file_extent_size_overflow_callback);
135 break;
136
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);
140 break;
141
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);
145 break;
146
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);
150 break;
151
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);
155 break;
156
157 case HFS_FSINFO_FREE_EXTENTS:
158 error = hfs_fsinfo_free_extents(hfsmp, &(fsinfo_union->data));
159 break;
160
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);
164 break;
165
166 #if CONFIG_PROTECT
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);
170 break;
171 #endif
172
173 default:
174 return ENOTSUP;
175 };
176
177 return error;
178 }
179
180 /*
181 * This function provides information about total number of allocation blocks
182 * for each individual metadata file.
183 */
184 static errno_t
185 hfs_fsinfo_metadata_blocks(struct hfsmount *hfsmp, struct hfs_fsinfo_metadata *fsinfo)
186 {
187 int lockflags = 0;
188 int ret_lockflags = 0;
189
190 /*
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
194 */
195 lockflags = SFL_CATALOG | SFL_EXTENTS | SFL_BITMAP | SFL_ATTRIBUTE;
196 ret_lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_SHARED_LOCK);
197
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;
204 else
205 fsinfo->attribute = 0;
206
207 /* Done with btrees, give up the locks */
208 hfs_systemfile_unlock(hfsmp, ret_lockflags);
209
210 /* Get information about journal file */
211 fsinfo->journal = howmany(hfsmp->jnl_size, hfsmp->blockSize);
212
213 return 0;
214 }
215
216 /*
217 * Helper function to count the number of valid extents in a file fork structure
218 */
219 static uint32_t
220 hfs_count_extents_fp(struct filefork *ff)
221 {
222 int i;
223 uint32_t count = 0;
224 for (i = 0; i < kHFSPlusExtentDensity; i++) {
225 if (ff->ff_data.cf_extents[i].blockCount == 0) {
226 break;
227 }
228 count++;
229 }
230 return count;
231 }
232
233
234 /*
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
238 */
239 static errno_t
240 hfs_count_overflow_extents(struct hfsmount *hfsmp, uint32_t fileID, uint32_t *num_extents)
241 {
242 int error;
243 FCB *fcb;
244 struct BTreeIterator *iterator = NULL;
245 FSBufferDescriptor btdata;
246 HFSPlusExtentKey *extentKey;
247 HFSPlusExtentRecord extentData;
248 uint32_t extent_count = 0;
249 int i;
250
251 fcb = VTOF(hfsmp->hfs_extents_vp);
252 iterator = hfs_mallocz(sizeof(struct BTreeIterator));
253
254 extentKey = (HFSPlusExtentKey *) &iterator->key;
255 extentKey->keyLength = kHFSPlusExtentKeyMaximumLength;
256 extentKey->forkType = kHFSDataForkType;
257 extentKey->fileID = fileID;
258 extentKey->startBlock = 0;
259
260 btdata.bufferAddress = &extentData;
261 btdata.itemSize = sizeof(HFSPlusExtentRecord);
262 btdata.itemCount = 1;
263
264 /* Search for overflow extent record */
265 error = BTSearchRecord(fcb, iterator, &btdata, NULL, iterator);
266
267 /*
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).
271 */
272 if (error && error != fsBTRecordNotFoundErr && error != fsBTEndOfIterationErr)
273 goto out;
274 error = 0;
275
276 for (;;) {
277
278 if (msleep(NULL, NULL, PINOD | PCATCH,
279 "hfs_fsinfo", NULL) == EINTR) {
280 error = EINTR;
281 break;
282 }
283
284 error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
285 if (error != 0) {
286 /* These are expected errors, so mask them */
287 if (error == fsBTRecordNotFoundErr || error == fsBTEndOfIterationErr) {
288 error = 0;
289 }
290 break;
291 }
292
293 /* If we encounter different fileID, stop the iteration */
294 if (extentKey->fileID != fileID) {
295 break;
296 }
297
298 if (extentKey->forkType != kHFSDataForkType)
299 break;
300
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) {
304 break;
305 }
306 extent_count++;
307 }
308 }
309
310 out:
311 hfs_free(iterator, sizeof(*iterator));
312
313 if (error == 0) {
314 *num_extents = extent_count;
315 }
316 return MacToVFSError(error);
317 }
318
319 /*
320 * This function provides information about total number of extents (including
321 * extents from overflow extents btree, if any) for each individual metadata
322 * file.
323 */
324 static errno_t
325 hfs_fsinfo_metadata_extents(struct hfsmount *hfsmp, struct hfs_fsinfo_metadata *fsinfo)
326 {
327 int error = 0;
328 int lockflags = 0;
329 int ret_lockflags = 0;
330 uint32_t overflow_count;
331
332 /*
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
336 */
337 lockflags = SFL_CATALOG | SFL_EXTENTS | SFL_BITMAP | SFL_ATTRIBUTE;
338 ret_lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_SHARED_LOCK);
339
340 /* Get number of extents for extents overflow btree */
341 fsinfo->extents = hfs_count_extents_fp(hfsmp->hfs_extents_cp->c_datafork);
342
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);
347 if (error) {
348 goto out;
349 }
350 fsinfo->catalog += overflow_count;
351 }
352
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);
357 if (error) {
358 goto out;
359 }
360 fsinfo->allocation += overflow_count;
361 }
362
363 /*
364 * Get number of extents for attribute btree.
365 * hfs_attribute_cp might be NULL.
366 */
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);
371 if (error) {
372 goto out;
373 }
374 fsinfo->attribute += overflow_count;
375 }
376 }
377 /* Journal always has one extent */
378 fsinfo->journal = 1;
379 out:
380 hfs_systemfile_unlock(hfsmp, ret_lockflags);
381 return error;
382 }
383
384 /*
385 * Helper function to calculate percentage i.e. X is what percent of Y?
386 */
387 static inline uint32_t
388 hfs_percent(uint32_t X, uint32_t Y)
389 {
390 return (X * 100ll) / Y;
391 }
392
393 /*
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.
398 */
399 static errno_t
400 hfs_fsinfo_metadata_percentfree(struct hfsmount *hfsmp, struct hfs_fsinfo_metadata *fsinfo)
401 {
402 int lockflags = 0;
403 int ret_lockflags = 0;
404 BTreeControlBlockPtr btreePtr;
405 uint32_t free_nodes, total_nodes;
406
407 /*
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
411 */
412 lockflags = SFL_CATALOG | SFL_EXTENTS | SFL_BITMAP | SFL_ATTRIBUTE;
413 ret_lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_SHARED_LOCK);
414
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);
420
421 /* Catalog btree */
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);
426
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);
433 }
434
435 hfs_systemfile_unlock(hfsmp, ret_lockflags);
436 return 0;
437 }
438
439 /*
440 * Helper function to calculate log base 2 for given number
441 */
442 static inline int
443 hfs_log2(uint64_t entry)
444 {
445 return (63 - __builtin_clzll(entry|1));
446 }
447
448 /*
449 * Helper function to account for input entry into the data
450 * array based on its log base 2 value
451 */
452 void hfs_fsinfo_data_add(struct hfs_fsinfo_data *fsinfo, uint64_t entry)
453 {
454 /*
455 * From hfs_fsctl.h -
456 *
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)
462 *
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.
466 *
467 * For example,
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
473 */
474 uint32_t bucket;
475
476 if (entry) {
477 /*
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
482 */
483 bucket = MIN(hfs_log2(entry) + 1, HFS_FSINFO_DATA_MAX_BUCKETS-1);
484 ++fsinfo->bucket[bucket];
485 } else {
486 /* Entry is zero, so account it in 0th offset */
487 fsinfo->bucket[0]++;
488 }
489 }
490
491 /*
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
496 * by the caller.
497 *
498 * Note: It might drop and reacquire the locks during execution.
499 */
500 static errno_t
501 traverse_btree(struct hfsmount *hfsmp, uint32_t btree_fileID, int flags,
502 void *fsinfo, int (*callback)(struct hfsmount *, HFSPlusKey *, HFSPlusRecord *, void *))
503 {
504 int error = 0;
505 int lockflags = 0;
506 int ret_lockflags = 0;
507 FCB *fcb;
508 struct BTreeIterator *iterator = NULL;
509 struct FSBufferDescriptor btdata;
510 int btree_operation;
511 HFSPlusRecord record;
512 HFSPlusKey *key;
513 uint64_t start, timeout_abs;
514
515 switch(btree_fileID) {
516 case kHFSExtentsFileID:
517 fcb = VTOF(hfsmp->hfs_extents_vp);
518 lockflags = SFL_EXTENTS;
519 break;
520 case kHFSCatalogFileID:
521 fcb = VTOF(hfsmp->hfs_catalog_vp);
522 lockflags = SFL_CATALOG;
523 break;
524 case kHFSAttributesFileID:
525 // Attributes file doesn’t exist, There are no records to iterate.
526 if (hfsmp->hfs_attribute_vp == NULL)
527 return error;
528 fcb = VTOF(hfsmp->hfs_attribute_vp);
529 lockflags = SFL_ATTRIBUTE;
530 break;
531
532 default:
533 return EINVAL;
534 }
535
536 iterator = hfs_mallocz(sizeof(struct BTreeIterator));
537
538 /* The key is initialized to zero because we are traversing entire btree */
539 key = (HFSPlusKey *)&iterator->key;
540
541 if (flags & TRAVERSE_BTREE_EXTENTS) {
542 lockflags |= SFL_EXTENTS;
543 }
544
545 btdata.bufferAddress = &record;
546 btdata.itemSize = sizeof(HFSPlusRecord);
547 btdata.itemCount = 1;
548
549 /* Lock btree for duration of traversal */
550 ret_lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_SHARED_LOCK);
551 btree_operation = kBTreeFirstRecord;
552
553 nanoseconds_to_absolutetime(HFS_FSINFO_MAX_LOCKHELD_TIME, &timeout_abs);
554 start = mach_absolute_time();
555
556 while (1) {
557
558 if (msleep(NULL, NULL, PINOD | PCATCH,
559 "hfs_fsinfo", NULL) == EINTR) {
560 error = EINTR;
561 break;
562 }
563
564 error = BTIterateRecord(fcb, btree_operation, iterator, &btdata, NULL);
565 if (error != 0) {
566 if (error == fsBTRecordNotFoundErr || error == fsBTEndOfIterationErr) {
567 error = 0;
568 }
569 break;
570 }
571 /* Lookup next btree record on next call to BTIterateRecord() */
572 btree_operation = kBTreeNextRecord;
573
574 /* Call our callback function and stop iteration if there are any errors */
575 error = callback(hfsmp, key, &record, fsinfo);
576 if (error) {
577 break;
578 }
579
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) {
582
583 /* release b-tree locks and let someone else get the lock */
584 hfs_systemfile_unlock (hfsmp, ret_lockflags);
585
586 /* add tsleep here to force context switch and fairness */
587 tsleep((caddr_t)hfsmp, PRIBIO, "hfs_fsinfo", 1);
588
589 /*
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.
596 */
597
598 ret_lockflags = hfs_systemfile_lock (hfsmp, lockflags, HFS_SHARED_LOCK);
599
600 /*
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.
606 */
607
608 start = mach_absolute_time();
609
610 /* loop back around and get another record */
611 }
612 }
613
614 hfs_systemfile_unlock(hfsmp, ret_lockflags);
615 hfs_free(iterator, sizeof(*iterator));
616 return MacToVFSError(error);
617 }
618
619 /*
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.
623 */
624 static errno_t
625 fsinfo_file_extent_count_callback(struct hfsmount *hfsmp,
626 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
627 {
628 int i;
629 int error = 0;
630 uint32_t num_extents = 0;
631 uint32_t num_overflow = 0;
632 uint32_t blockCount;
633
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) {
639 break;
640 }
641 num_extents++;
642 }
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);
647 if (error) {
648 goto out;
649 }
650 num_extents += num_overflow;
651 }
652 hfs_fsinfo_data_add(data, num_extents);
653 }
654 out:
655 return error;
656 }
657
658 /*
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
662 * fork.
663 */
664 static errno_t fsinfo_file_extent_size_catalog_callback(struct hfsmount *hfsmp,
665 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
666 {
667 int i;
668 uint32_t blockCount;
669 uint64_t extent_size;
670
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) {
676 break;
677 }
678 extent_size = hfs_blk_to_bytes(blockCount, hfsmp->blockSize);
679 hfs_fsinfo_data_add(data, extent_size);
680 }
681 }
682 return 0;
683 }
684
685 /*
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,
689 * no resource fork.
690 */
691 static errno_t fsinfo_file_extent_size_overflow_callback(struct hfsmount *hfsmp,
692 HFSPlusKey *key, HFSPlusRecord *record, void *data)
693 {
694 int i;
695 uint32_t blockCount;
696 uint64_t extent_size;
697
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) {
704 break;
705 }
706 extent_size = hfs_blk_to_bytes(blockCount, hfsmp->blockSize);
707 hfs_fsinfo_data_add(data, extent_size);
708 }
709 }
710 }
711 return 0;
712 }
713
714 /*
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.
718 */
719 static errno_t fsinfo_file_size_callback(__unused struct hfsmount *hfsmp,
720 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
721 {
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);
725 }
726 return 0;
727 }
728
729 /*
730 * Callback function to get distribution of directory valence
731 * for all directories in the given file system.
732 */
733 static errno_t fsinfo_dir_valence_callback(__unused struct hfsmount *hfsmp,
734 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
735 {
736 if (record->folder_record.recordType == kHFSPlusFolderRecord) {
737 hfs_fsinfo_data_add(data, record->folder_record.valence);
738 }
739 return 0;
740 }
741
742 /*
743 * Callback function to get distribution of number of unicode
744 * characters in name for all files and directories for a given
745 * file system.
746 */
747 static errno_t fsinfo_name_size_callback(__unused struct hfsmount *hfsmp,
748 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
749 {
750 struct hfs_fsinfo_name *fsinfo = (struct hfs_fsinfo_name *)data;
751 uint32_t length;
752
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) {
758 return EIO;
759 }
760
761 // sanity check for a name length of zero, which isn't valid on disk.
762 if (length == 0)
763 return EIO;
764
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]++;
769 }
770 return 0;
771 }
772
773 /*
774 * Callback function to get distribution of size of all extended
775 * attributes for a given file system.
776 */
777 static errno_t fsinfo_xattr_size_callback(__unused struct hfsmount *hfsmp,
778 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
779 {
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);
786 }
787 return 0;
788 }
789
790
791 /*
792 * Callback function to get distribution of free space extents for a given file system.
793 */
794 static void fsinfo_free_extents_callback(void *data, off_t free_extent_size)
795 {
796 // Assume a minimum of 4 KB block size
797 hfs_fsinfo_data_add(data, free_extent_size / 4096);
798 }
799
800 /*
801 * Function to get distribution of free space extents for a given file system.
802 */
803 static errno_t hfs_fsinfo_free_extents(struct hfsmount *hfsmp, struct hfs_fsinfo_data *fsinfo)
804 {
805 return hfs_find_free_extents(hfsmp, &fsinfo_free_extents_callback, fsinfo);
806 }
807
808 /*
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.
812 */
813 static errno_t fsinfo_symlink_size_callback(__unused struct hfsmount *hfsmp,
814 __unused HFSPlusKey *key, HFSPlusRecord *record, void *data)
815 {
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);
820 }
821 return 0;
822 }
823
824 #if CONFIG_PROTECT
825 /*
826 * Callback function to get total number of files/directories
827 * for each content protection class
828 */
829 static int fsinfo_cprotect_count_callback(struct hfsmount *hfsmp, HFSPlusKey *key,
830 HFSPlusRecord *record, void *data)
831 {
832 struct hfs_fsinfo_cprotect *fsinfo = (struct hfs_fsinfo_cprotect *)data;
833 static const uint16_t cp_xattrname_utf16[] = CONTENT_PROTECTION_XATTR_NAME_CHARS;
834 /*
835 * NOTE: cp_xattrname_utf16_len is the number of UTF-16 code units in
836 * the EA name string.
837 */
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;
843 int error = 0;
844
845 /* Content protect xattrs are inline attributes only, so skip all others */
846 if (record->attr_record.recordType != kHFSPlusAttrInlineData)
847 return 0;
848
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))) {
852 return 0;
853 }
854
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);
858 if (error)
859 return 0;
860
861 /* No key present, skip this record */
862 if (!ISSET(cp_entry.cp_flags, CP_HAS_A_KEY))
863 return 0;
864
865 /* Now account for the persistent class */
866 switch (CP_CLASS(cp_entry.cp_pclass)) {
867 case PROTECTION_CLASS_A:
868 fsinfo->class_A++;
869 break;
870 case PROTECTION_CLASS_B:
871 fsinfo->class_B++;
872 break;
873 case PROTECTION_CLASS_C:
874 fsinfo->class_C++;
875 break;
876 case PROTECTION_CLASS_D:
877 fsinfo->class_D++;
878 break;
879 case PROTECTION_CLASS_E:
880 fsinfo->class_E++;
881 break;
882 case PROTECTION_CLASS_F:
883 fsinfo->class_F++;
884 break;
885 };
886
887 return 0;
888 }
889 #endif