2 * Copyright (c) 2008-2018 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@
30 /* We need these symbols even though compression is turned off */
32 #define UNUSED_SYMBOL(x) asm(".global _" #x "\n.set _" #x ", 0\n");
34 UNUSED_SYMBOL(register_decmpfs_decompressor
)
35 UNUSED_SYMBOL(unregister_decmpfs_decompressor
)
36 UNUSED_SYMBOL(decmpfs_init
)
37 UNUSED_SYMBOL(decmpfs_read_compressed
)
38 UNUSED_SYMBOL(decmpfs_cnode_cmp_type
)
39 UNUSED_SYMBOL(decmpfs_cnode_get_vnode_state
)
40 UNUSED_SYMBOL(decmpfs_cnode_get_vnode_cached_size
)
41 UNUSED_SYMBOL(decmpfs_cnode_get_vnode_cached_nchildren
)
42 UNUSED_SYMBOL(decmpfs_cnode_get_vnode_cached_total_size
)
43 UNUSED_SYMBOL(decmpfs_lock_compressed_data
)
44 UNUSED_SYMBOL(decmpfs_cnode_free
)
45 UNUSED_SYMBOL(decmpfs_cnode_alloc
)
46 UNUSED_SYMBOL(decmpfs_cnode_destroy
)
47 UNUSED_SYMBOL(decmpfs_decompress_file
)
48 UNUSED_SYMBOL(decmpfs_unlock_compressed_data
)
49 UNUSED_SYMBOL(decmpfs_cnode_init
)
50 UNUSED_SYMBOL(decmpfs_cnode_set_vnode_state
)
51 UNUSED_SYMBOL(decmpfs_hides_xattr
)
52 UNUSED_SYMBOL(decmpfs_ctx
)
53 UNUSED_SYMBOL(decmpfs_file_is_compressed
)
54 UNUSED_SYMBOL(decmpfs_update_attributes
)
55 UNUSED_SYMBOL(decmpfs_hides_rsrc
)
56 UNUSED_SYMBOL(decmpfs_pagein_compressed
)
57 UNUSED_SYMBOL(decmpfs_validate_compressed_file
)
59 #else /* FS_COMPRESSION */
60 #include <sys/kernel.h>
61 #include <sys/vnode_internal.h>
62 #include <sys/file_internal.h>
64 #include <sys/fcntl.h>
65 #include <sys/xattr.h>
66 #include <sys/namei.h>
68 #include <sys/mount_internal.h>
70 #include <sys/decmpfs.h>
71 #include <sys/uio_internal.h>
72 #include <libkern/OSByteOrder.h>
73 #include <libkern/section_keywords.h>
77 #pragma mark --- debugging ---
79 #define COMPRESSION_DEBUG 0
80 #define COMPRESSION_DEBUG_VERBOSE 0
81 #define MALLOC_DEBUG 0
85 vnpath(vnode_t vp
, char *path
, int len
)
89 vn_getpath(vp
, path
, &len
);
90 path
[origlen
- 1] = 0;
95 #define ErrorLog(x, args...) \
96 printf("%s:%d:%s: " x, __FILE_NAME__, __LINE__, __FUNCTION__, ## args)
98 #define ErrorLogWithPath(x, args...) do { \
99 char *path = zalloc(ZV_NAMEI); \
100 printf("%s:%d:%s: %s: " x, __FILE_NAME__, __LINE__, __FUNCTION__, \
101 vnpath(vp, path, PATH_MAX), ## args); \
102 zfree(ZV_NAMEI, path); \
105 #define ErrorLogWithPath(x, args...) do { \
107 printf("%s:%d:%s: %s: " x, __FILE_NAME__, __LINE__, __FUNCTION__, \
108 "<private>", ## args); \
112 #if COMPRESSION_DEBUG
113 #define DebugLog ErrorLog
114 #define DebugLogWithPath ErrorLogWithPath
116 #define DebugLog(x...) do { } while(0)
117 #define DebugLogWithPath(x...) do { } while(0)
120 #if COMPRESSION_DEBUG_VERBOSE
121 #define VerboseLog ErrorLog
122 #define VerboseLogWithPath ErrorLogWithPath
124 #define VerboseLog(x...) do { } while(0)
125 #define VerboseLogWithPath(x...) do { } while(0)
128 #pragma mark --- globals ---
130 static LCK_GRP_DECLARE(decmpfs_lockgrp
, "VFSCOMP");
131 static LCK_RW_DECLARE(decompressorsLock
, &decmpfs_lockgrp
);
132 static LCK_MTX_DECLARE(decompress_channel_mtx
, &decmpfs_lockgrp
);
134 static const decmpfs_registration
*decompressors
[CMP_MAX
]; /* the registered compressors */
135 static int decompress_channel
; /* channel used by decompress_file to wake up waiters */
137 vfs_context_t decmpfs_ctx
;
139 #pragma mark --- decmp_get_func ---
141 #define offsetof_func(func) ((uintptr_t)offsetof(decmpfs_registration, func))
144 _func_from_offset(uint32_t type
, uintptr_t offset
, uint32_t discriminator
)
146 /* get the function at the given offset in the registration for the given type */
147 const decmpfs_registration
*reg
= decompressors
[type
];
149 switch (reg
->decmpfs_registration
) {
150 case DECMPFS_REGISTRATION_VERSION_V1
:
151 if (offset
> offsetof_func(free_data
)) {
155 case DECMPFS_REGISTRATION_VERSION_V3
:
156 if (offset
> offsetof_func(get_flags
)) {
164 void *ptr
= *(void * const *)((const void *)reg
+ offset
);
166 /* Resign as a function-in-void* */
167 ptr
= ptrauth_auth_and_resign(ptr
, ptrauth_key_asia
, discriminator
, ptrauth_key_asia
, 0);
172 extern void IOServicePublishResource( const char * property
, boolean_t value
);
173 extern boolean_t
IOServiceWaitForMatchingResource( const char * property
, uint64_t timeout
);
174 extern boolean_t
IOCatalogueMatchingDriversPresent( const char * property
);
177 _decmp_get_func(vnode_t vp
, uint32_t type
, uintptr_t offset
, uint32_t discriminator
)
180 * this function should be called while holding a shared lock to decompressorsLock,
181 * and will return with the lock held
184 if (type
>= CMP_MAX
) {
188 if (decompressors
[type
] != NULL
) {
189 // the compressor has already registered but the function might be null
190 return _func_from_offset(type
, offset
, discriminator
);
193 // does IOKit know about a kext that is supposed to provide this type?
194 char providesName
[80];
195 snprintf(providesName
, sizeof(providesName
), "com.apple.AppleFSCompression.providesType%u", type
);
196 if (IOCatalogueMatchingDriversPresent(providesName
)) {
197 // there is a kext that says it will register for this type, so let's wait for it
198 char resourceName
[80];
199 uint64_t delay
= 10000000ULL; // 10 milliseconds.
200 snprintf(resourceName
, sizeof(resourceName
), "com.apple.AppleFSCompression.Type%u", type
);
201 ErrorLogWithPath("waiting for %s\n", resourceName
);
202 while (decompressors
[type
] == NULL
) {
203 lck_rw_unlock_shared(&decompressorsLock
); // we have to unlock to allow the kext to register
204 if (IOServiceWaitForMatchingResource(resourceName
, delay
)) {
205 lck_rw_lock_shared(&decompressorsLock
);
208 if (!IOCatalogueMatchingDriversPresent(providesName
)) {
210 ErrorLogWithPath("the kext with %s is no longer present\n", providesName
);
211 lck_rw_lock_shared(&decompressorsLock
);
214 ErrorLogWithPath("still waiting for %s\n", resourceName
);
216 lck_rw_lock_shared(&decompressorsLock
);
218 // IOKit says the kext is loaded, so it should be registered too!
219 if (decompressors
[type
] == NULL
) {
220 ErrorLogWithPath("we found %s, but the type still isn't registered\n", providesName
);
223 // it's now registered, so let's return the function
224 return _func_from_offset(type
, offset
, discriminator
);
227 // the compressor hasn't registered, so it never will unless someone manually kextloads it
228 ErrorLogWithPath("tried to access a compressed file of unregistered type %d\n", type
);
232 #define decmp_get_func(vp, type, func) (typeof(decompressors[0]->func))_decmp_get_func(vp, type, offsetof_func(func), ptrauth_function_pointer_type_discriminator(typeof(decompressors[0]->func)))
234 #pragma mark --- utilities ---
236 #if COMPRESSION_DEBUG
238 vnsize(vnode_t vp
, uint64_t *size
)
240 struct vnode_attr va
;
242 VATTR_WANTED(&va
, va_data_size
);
243 int error
= vnode_getattr(vp
, &va
, decmpfs_ctx
);
245 ErrorLogWithPath("vnode_getattr err %d\n", error
);
248 *size
= va
.va_data_size
;
251 #endif /* COMPRESSION_DEBUG */
253 #pragma mark --- cnode routines ---
255 ZONE_DECLARE(decmpfs_cnode_zone
, "decmpfs_cnode",
256 sizeof(struct decmpfs_cnode
), ZC_NONE
);
259 decmpfs_cnode_alloc(void)
261 return zalloc(decmpfs_cnode_zone
);
265 decmpfs_cnode_free(decmpfs_cnode
*dp
)
267 zfree(decmpfs_cnode_zone
, dp
);
271 decmpfs_cnode_init(decmpfs_cnode
*cp
)
273 memset(cp
, 0, sizeof(*cp
));
274 lck_rw_init(&cp
->compressed_data_lock
, &decmpfs_lockgrp
, NULL
);
278 decmpfs_cnode_destroy(decmpfs_cnode
*cp
)
280 lck_rw_destroy(&cp
->compressed_data_lock
, &decmpfs_lockgrp
);
284 decmpfs_trylock_compressed_data(decmpfs_cnode
*cp
, int exclusive
)
286 void *thread
= current_thread();
289 if (cp
->lockowner
== thread
) {
290 /* this thread is already holding an exclusive lock, so bump the count */
293 } else if (exclusive
) {
294 if ((retval
= lck_rw_try_lock_exclusive(&cp
->compressed_data_lock
))) {
295 cp
->lockowner
= thread
;
299 if ((retval
= lck_rw_try_lock_shared(&cp
->compressed_data_lock
))) {
300 cp
->lockowner
= (void *)-1;
307 decmpfs_lock_compressed_data(decmpfs_cnode
*cp
, int exclusive
)
309 void *thread
= current_thread();
311 if (cp
->lockowner
== thread
) {
312 /* this thread is already holding an exclusive lock, so bump the count */
314 } else if (exclusive
) {
315 lck_rw_lock_exclusive(&cp
->compressed_data_lock
);
316 cp
->lockowner
= thread
;
319 lck_rw_lock_shared(&cp
->compressed_data_lock
);
320 cp
->lockowner
= (void *)-1;
325 decmpfs_unlock_compressed_data(decmpfs_cnode
*cp
, __unused
int exclusive
)
327 void *thread
= current_thread();
329 if (cp
->lockowner
== thread
) {
330 /* this thread is holding an exclusive lock, so decrement the count */
331 if ((--cp
->lockcount
) > 0) {
332 /* the caller still has outstanding locks, so we're done */
335 cp
->lockowner
= NULL
;
338 lck_rw_done(&cp
->compressed_data_lock
);
342 decmpfs_cnode_get_vnode_state(decmpfs_cnode
*cp
)
344 return cp
->cmp_state
;
348 decmpfs_cnode_set_vnode_state(decmpfs_cnode
*cp
, uint32_t state
, int skiplock
)
351 decmpfs_lock_compressed_data(cp
, 1);
353 cp
->cmp_state
= (uint8_t)state
;
354 if (state
== FILE_TYPE_UNKNOWN
) {
355 /* clear out the compression type too */
359 decmpfs_unlock_compressed_data(cp
, 1);
364 decmpfs_cnode_set_vnode_cmp_type(decmpfs_cnode
*cp
, uint32_t cmp_type
, int skiplock
)
367 decmpfs_lock_compressed_data(cp
, 1);
369 cp
->cmp_type
= cmp_type
;
371 decmpfs_unlock_compressed_data(cp
, 1);
376 decmpfs_cnode_set_vnode_minimal_xattr(decmpfs_cnode
*cp
, int minimal_xattr
, int skiplock
)
379 decmpfs_lock_compressed_data(cp
, 1);
381 cp
->cmp_minimal_xattr
= !!minimal_xattr
;
383 decmpfs_unlock_compressed_data(cp
, 1);
388 decmpfs_cnode_get_vnode_cached_size(decmpfs_cnode
*cp
)
390 return cp
->uncompressed_size
;
394 decmpfs_cnode_get_vnode_cached_nchildren(decmpfs_cnode
*cp
)
396 return cp
->nchildren
;
400 decmpfs_cnode_get_vnode_cached_total_size(decmpfs_cnode
*cp
)
402 return cp
->total_size
;
406 decmpfs_cnode_set_vnode_cached_size(decmpfs_cnode
*cp
, uint64_t size
)
409 uint64_t old
= cp
->uncompressed_size
;
410 if (OSCompareAndSwap64(old
, size
, (UInt64
*)&cp
->uncompressed_size
)) {
413 /* failed to write our value, so loop */
419 decmpfs_cnode_set_vnode_cached_nchildren(decmpfs_cnode
*cp
, uint64_t nchildren
)
422 uint64_t old
= cp
->nchildren
;
423 if (OSCompareAndSwap64(old
, nchildren
, (UInt64
*)&cp
->nchildren
)) {
426 /* failed to write our value, so loop */
432 decmpfs_cnode_set_vnode_cached_total_size(decmpfs_cnode
*cp
, uint64_t total_sz
)
435 uint64_t old
= cp
->total_size
;
436 if (OSCompareAndSwap64(old
, total_sz
, (UInt64
*)&cp
->total_size
)) {
439 /* failed to write our value, so loop */
445 decmpfs_cnode_get_decompression_flags(decmpfs_cnode
*cp
)
447 return cp
->decompression_flags
;
451 decmpfs_cnode_set_decompression_flags(decmpfs_cnode
*cp
, uint64_t flags
)
454 uint64_t old
= cp
->decompression_flags
;
455 if (OSCompareAndSwap64(old
, flags
, (UInt64
*)&cp
->decompression_flags
)) {
458 /* failed to write our value, so loop */
464 decmpfs_cnode_cmp_type(decmpfs_cnode
*cp
)
469 #pragma mark --- decmpfs state routines ---
472 decmpfs_fetch_compressed_header(vnode_t vp
, decmpfs_cnode
*cp
, decmpfs_header
**hdrOut
, int returnInvalid
, size_t *hdr_size
)
475 * fetches vp's compression xattr, converting it into a decmpfs_header; returns 0 or errno
476 * if returnInvalid == 1, returns the header even if the type was invalid (out of range),
477 * and return ERANGE in that case
480 size_t read_size
= 0;
481 size_t attr_size
= 0;
482 size_t alloc_size
= 0;
483 uio_t attr_uio
= NULL
;
486 const bool no_additional_data
= ((cp
!= NULL
)
487 && (cp
->cmp_type
!= 0)
488 && (cp
->cmp_minimal_xattr
!= 0));
489 char uio_buf
[UIO_SIZEOF(1)];
490 decmpfs_header
*hdr
= NULL
;
493 * Trace the following parameters on entry with event-id 0x03120004
495 * @vp->v_id: vnode-id for which to fetch compressed header.
496 * @no_additional_data: If set true then xattr didn't have any extra data.
497 * @returnInvalid: return the header even though the type is out of range.
499 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FETCH_COMPRESSED_HEADER
, vp
->v_id
,
500 no_additional_data
, returnInvalid
);
502 if (no_additional_data
) {
503 /* this file's xattr didn't have any extra data when we fetched it, so we can synthesize a header from the data in the cnode */
505 alloc_size
= sizeof(decmpfs_header
);
506 data
= kheap_alloc(KHEAP_TEMP
, alloc_size
, Z_WAITOK
);
511 hdr
= (decmpfs_header
*)data
;
512 hdr
->attr_size
= sizeof(decmpfs_disk_header
);
513 hdr
->compression_magic
= DECMPFS_MAGIC
;
514 hdr
->compression_type
= cp
->cmp_type
;
515 if (hdr
->compression_type
== DATALESS_PKG_CMPFS_TYPE
) {
516 if (!vnode_isdir(vp
)) {
520 hdr
->_size
.value
= DECMPFS_PKG_VALUE_FROM_SIZE_COUNT(
521 decmpfs_cnode_get_vnode_cached_size(cp
),
522 decmpfs_cnode_get_vnode_cached_nchildren(cp
));
523 } else if (vnode_isdir(vp
)) {
524 hdr
->_size
.value
= decmpfs_cnode_get_vnode_cached_nchildren(cp
);
526 hdr
->_size
.value
= decmpfs_cnode_get_vnode_cached_size(cp
);
529 /* figure out how big the xattr is on disk */
530 err
= vn_getxattr(vp
, DECMPFS_XATTR_NAME
, NULL
, &attr_size
, XATTR_NOSECURITY
, decmpfs_ctx
);
534 alloc_size
= attr_size
+ sizeof(hdr
->attr_size
);
536 if (attr_size
< sizeof(decmpfs_disk_header
) || attr_size
> MAX_DECMPFS_XATTR_SIZE
) {
541 /* allocation includes space for the extra attr_size field of a compressed_header */
542 data
= kheap_alloc(KHEAP_TEMP
, alloc_size
, Z_WAITOK
);
548 /* read the xattr into our buffer, skipping over the attr_size field at the beginning */
549 attr_uio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
, &uio_buf
[0], sizeof(uio_buf
));
550 uio_addiov(attr_uio
, CAST_USER_ADDR_T(data
+ sizeof(hdr
->attr_size
)), attr_size
);
552 err
= vn_getxattr(vp
, DECMPFS_XATTR_NAME
, attr_uio
, &read_size
, XATTR_NOSECURITY
, decmpfs_ctx
);
556 if (read_size
!= attr_size
) {
560 hdr
= (decmpfs_header
*)data
;
561 hdr
->attr_size
= (uint32_t)attr_size
;
562 /* swap the fields to native endian */
563 hdr
->compression_magic
= OSSwapLittleToHostInt32(hdr
->compression_magic
);
564 hdr
->compression_type
= OSSwapLittleToHostInt32(hdr
->compression_type
);
565 hdr
->uncompressed_size
= OSSwapLittleToHostInt64(hdr
->uncompressed_size
);
568 if (hdr
->compression_magic
!= DECMPFS_MAGIC
) {
569 ErrorLogWithPath("invalid compression_magic 0x%08x, should be 0x%08x\n", hdr
->compression_magic
, DECMPFS_MAGIC
);
575 * Special-case the DATALESS compressor here; that is a valid type,
576 * even through there will never be an entry in the decompressor
577 * handler table for it. If we don't do this, then the cmp_state
578 * for this cnode will end up being marked NOT_COMPRESSED, and
579 * we'll be stuck in limbo.
581 if (hdr
->compression_type
>= CMP_MAX
&& !decmpfs_type_is_dataless(hdr
->compression_type
)) {
583 /* return the header even though the type is out of range */
586 ErrorLogWithPath("compression_type %d out of range\n", hdr
->compression_type
);
593 if (err
&& (err
!= ERANGE
)) {
594 DebugLogWithPath("err %d\n", err
);
595 kheap_free(KHEAP_TEMP
, data
, alloc_size
);
599 *hdr_size
= alloc_size
;
602 * Trace the following parameters on return with event-id 0x03120004.
604 * @vp->v_id: vnode-id for which to fetch compressed header.
605 * @err: value returned from this function.
607 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FETCH_COMPRESSED_HEADER
, vp
->v_id
, err
);
612 decmpfs_fast_get_state(decmpfs_cnode
*cp
)
615 * return the cached state
616 * this should *only* be called when we know that decmpfs_file_is_compressed has already been called,
617 * because this implies that the cached state is valid
619 int cmp_state
= decmpfs_cnode_get_vnode_state(cp
);
622 case FILE_IS_NOT_COMPRESSED
:
623 case FILE_IS_COMPRESSED
:
624 case FILE_IS_CONVERTING
:
626 case FILE_TYPE_UNKNOWN
:
628 * we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
629 * which should not be possible
631 ErrorLog("decmpfs_fast_get_state called on unknown file\n");
632 return FILE_IS_NOT_COMPRESSED
;
635 ErrorLog("unknown cmp_state %d\n", cmp_state
);
636 return FILE_IS_NOT_COMPRESSED
;
641 decmpfs_fast_file_is_compressed(decmpfs_cnode
*cp
)
643 int cmp_state
= decmpfs_cnode_get_vnode_state(cp
);
646 case FILE_IS_NOT_COMPRESSED
:
648 case FILE_IS_COMPRESSED
:
649 case FILE_IS_CONVERTING
:
651 case FILE_TYPE_UNKNOWN
:
653 * we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
654 * which should not be possible
656 ErrorLog("decmpfs_fast_get_state called on unknown file\n");
660 ErrorLog("unknown cmp_state %d\n", cmp_state
);
666 decmpfs_validate_compressed_file(vnode_t vp
, decmpfs_cnode
*cp
)
668 /* give a compressor a chance to indicate that a compressed file is invalid */
669 decmpfs_header
*hdr
= NULL
;
670 size_t alloc_size
= 0;
671 errno_t err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0, &alloc_size
);
674 /* we couldn't get the header */
675 if (decmpfs_fast_get_state(cp
) == FILE_IS_NOT_COMPRESSED
) {
676 /* the file is no longer compressed, so return success */
682 if (!decmpfs_type_is_dataless(hdr
->compression_type
)) {
683 lck_rw_lock_shared(&decompressorsLock
);
684 decmpfs_validate_compressed_file_func validate
= decmp_get_func(vp
, hdr
->compression_type
, validate
);
685 if (validate
) { /* make sure this validation function is valid */
686 /* is the data okay? */
687 err
= validate(vp
, decmpfs_ctx
, hdr
);
688 } else if (decmp_get_func(vp
, hdr
->compression_type
, fetch
) == NULL
) {
689 /* the type isn't registered */
692 /* no validate registered, so nothing to do */
695 lck_rw_unlock_shared(&decompressorsLock
);
699 kheap_free(KHEAP_TEMP
, hdr
, alloc_size
);
701 #if COMPRESSION_DEBUG
703 DebugLogWithPath("decmpfs_validate_compressed_file ret %d, vp->v_flag %d\n", err
, vp
->v_flag
);
710 decmpfs_file_is_compressed(vnode_t vp
, decmpfs_cnode
*cp
)
713 * determines whether vp points to a compressed file
715 * to speed up this operation, we cache the result in the cnode, and do as little as possible
716 * in the case where the cnode already has a valid cached state
723 struct vnode_attr va_fetch
;
724 decmpfs_header
*hdr
= NULL
;
725 size_t alloc_size
= 0;
727 int cnode_locked
= 0;
728 int saveInvalid
= 0; // save the header data even though the type was out of range
729 uint64_t decompression_flags
= 0;
730 bool is_mounted
, is_local_fs
;
732 if (vnode_isnamedstream(vp
)) {
734 * named streams can't be compressed
735 * since named streams of the same file share the same cnode,
736 * we don't want to get/set the state in the cnode, just return 0
741 /* examine the cached a state in this cnode */
742 cmp_state
= decmpfs_cnode_get_vnode_state(cp
);
744 case FILE_IS_NOT_COMPRESSED
:
746 case FILE_IS_COMPRESSED
:
748 case FILE_IS_CONVERTING
:
749 /* treat the file as compressed, because this gives us a way to block future reads until decompression is done */
751 case FILE_TYPE_UNKNOWN
:
752 /* the first time we encountered this vnode, so we need to check it out */
755 /* unknown state, assume file is not compressed */
756 ErrorLogWithPath("unknown cmp_state %d\n", cmp_state
);
762 mp
= vnode_mount(vp
);
767 is_local_fs
= ((mp
->mnt_flag
& MNT_LOCAL
));
770 * Trace the following parameters on entry with event-id 0x03120014.
772 * @vp->v_id: vnode-id of the file being queried.
773 * @is_mounted: set to true if @vp belongs to a mounted fs.
774 * @is_local_fs: set to true if @vp belongs to local fs.
776 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FILE_IS_COMPRESSED
, vp
->v_id
,
777 is_mounted
, is_local_fs
);
781 * this should only be true before we mount the root filesystem
782 * we short-cut this return to avoid the call to getattr below, which
783 * will fail before root is mounted
785 ret
= FILE_IS_NOT_COMPRESSED
;
790 /* compression only supported on local filesystems */
791 ret
= FILE_IS_NOT_COMPRESSED
;
795 /* lock our cnode data so that another caller doesn't change the state under us */
796 decmpfs_lock_compressed_data(cp
, 1);
799 VATTR_INIT(&va_fetch
);
800 VATTR_WANTED(&va_fetch
, va_flags
);
801 error
= vnode_getattr(vp
, &va_fetch
, decmpfs_ctx
);
803 /* failed to get the bsd flags so the file is not compressed */
804 ret
= FILE_IS_NOT_COMPRESSED
;
807 if (va_fetch
.va_flags
& UF_COMPRESSED
) {
808 /* UF_COMPRESSED is on, make sure the file has the DECMPFS_XATTR_NAME xattr */
809 error
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 1, &alloc_size
);
810 if ((hdr
!= NULL
) && (error
== ERANGE
)) {
814 /* failed to get the xattr so the file is not compressed */
815 ret
= FILE_IS_NOT_COMPRESSED
;
819 * We got the xattr, so the file is at least tagged compressed.
820 * For DATALESS, regular files and directories can be "compressed".
821 * For all other types, only files are allowed.
823 if (!vnode_isreg(vp
) &&
824 !(decmpfs_type_is_dataless(hdr
->compression_type
) && vnode_isdir(vp
))) {
825 ret
= FILE_IS_NOT_COMPRESSED
;
828 ret
= FILE_IS_COMPRESSED
;
831 /* UF_COMPRESSED isn't on, so the file isn't compressed */
832 ret
= FILE_IS_NOT_COMPRESSED
;
835 if (((ret
== FILE_IS_COMPRESSED
) || saveInvalid
) && hdr
) {
837 * cache the uncompressed size away in the cnode
842 * we should never get here since the only place ret is set to FILE_IS_COMPRESSED
843 * is after the call to decmpfs_lock_compressed_data above
845 decmpfs_lock_compressed_data(cp
, 1);
849 if (vnode_isdir(vp
)) {
850 decmpfs_cnode_set_vnode_cached_size(cp
, 64);
851 decmpfs_cnode_set_vnode_cached_nchildren(cp
, decmpfs_get_directory_entries(hdr
));
852 if (hdr
->compression_type
== DATALESS_PKG_CMPFS_TYPE
) {
853 decmpfs_cnode_set_vnode_cached_total_size(cp
, DECMPFS_PKG_SIZE(hdr
->_size
));
856 decmpfs_cnode_set_vnode_cached_size(cp
, hdr
->uncompressed_size
);
858 decmpfs_cnode_set_vnode_state(cp
, ret
, 1);
859 decmpfs_cnode_set_vnode_cmp_type(cp
, hdr
->compression_type
, 1);
860 /* remember if the xattr's size was equal to the minimal xattr */
861 if (hdr
->attr_size
== sizeof(decmpfs_disk_header
)) {
862 decmpfs_cnode_set_vnode_minimal_xattr(cp
, 1, 1);
864 if (ret
== FILE_IS_COMPRESSED
) {
865 /* update the ubc's size for this file */
866 ubc_setsize(vp
, hdr
->uncompressed_size
);
868 /* update the decompression flags in the decmpfs cnode */
869 lck_rw_lock_shared(&decompressorsLock
);
870 decmpfs_get_decompression_flags_func get_flags
= decmp_get_func(vp
, hdr
->compression_type
, get_flags
);
872 decompression_flags
= get_flags(vp
, decmpfs_ctx
, hdr
);
874 lck_rw_unlock_shared(&decompressorsLock
);
875 decmpfs_cnode_set_decompression_flags(cp
, decompression_flags
);
878 /* we might have already taken the lock above; if so, skip taking it again by passing cnode_locked as the skiplock parameter */
879 decmpfs_cnode_set_vnode_state(cp
, ret
, cnode_locked
);
883 decmpfs_unlock_compressed_data(cp
, 1);
887 kheap_free(KHEAP_TEMP
, hdr
, alloc_size
);
891 * Trace the following parameters on return with event-id 0x03120014.
893 * @vp->v_id: vnode-id of the file being queried.
894 * @return: set to 1 is file is compressed.
897 case FILE_IS_NOT_COMPRESSED
:
898 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED
, vp
->v_id
, 0);
900 case FILE_IS_COMPRESSED
:
901 case FILE_IS_CONVERTING
:
902 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED
, vp
->v_id
, 1);
905 /* unknown state, assume file is not compressed */
906 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED
, vp
->v_id
, 0);
907 ErrorLogWithPath("unknown ret %d\n", ret
);
913 decmpfs_update_attributes(vnode_t vp
, struct vnode_attr
*vap
)
917 if (VATTR_IS_ACTIVE(vap
, va_flags
)) {
918 /* the BSD flags are being updated */
919 if (vap
->va_flags
& UF_COMPRESSED
) {
920 /* the compressed bit is being set, did it change? */
921 struct vnode_attr va_fetch
;
923 VATTR_INIT(&va_fetch
);
924 VATTR_WANTED(&va_fetch
, va_flags
);
925 error
= vnode_getattr(vp
, &va_fetch
, decmpfs_ctx
);
930 old_flags
= va_fetch
.va_flags
;
932 if (!(old_flags
& UF_COMPRESSED
)) {
934 * Compression bit was turned on, make sure the file has the DECMPFS_XATTR_NAME attribute.
935 * This precludes anyone from using the UF_COMPRESSED bit for anything else, and it enforces
936 * an order of operation -- you must first do the setxattr and then the chflags.
939 if (VATTR_IS_ACTIVE(vap
, va_data_size
)) {
941 * don't allow the caller to set the BSD flag and the size in the same call
942 * since this doesn't really make sense
944 vap
->va_flags
&= ~UF_COMPRESSED
;
948 decmpfs_header
*hdr
= NULL
;
949 size_t alloc_size
= 0;
950 error
= decmpfs_fetch_compressed_header(vp
, NULL
, &hdr
, 1, &alloc_size
);
953 * Allow the flag to be set since the decmpfs attribute
956 * If we're creating a dataless file we do not want to
957 * truncate it to zero which allows the file resolver to
958 * have more control over when truncation should happen.
959 * All other types of compressed files are truncated to
962 if (!decmpfs_type_is_dataless(hdr
->compression_type
)) {
963 VATTR_SET_ACTIVE(vap
, va_data_size
);
964 vap
->va_data_size
= 0;
966 } else if (error
== ERANGE
) {
967 /* the file had a decmpfs attribute but the type was out of range, so don't muck with the file's data size */
969 /* no DECMPFS_XATTR_NAME attribute, so deny the update */
970 vap
->va_flags
&= ~UF_COMPRESSED
;
973 kheap_free(KHEAP_TEMP
, hdr
, alloc_size
);
983 wait_for_decompress(decmpfs_cnode
*cp
)
986 lck_mtx_lock(&decompress_channel_mtx
);
988 state
= decmpfs_fast_get_state(cp
);
989 if (state
!= FILE_IS_CONVERTING
) {
990 /* file is not decompressing */
991 lck_mtx_unlock(&decompress_channel_mtx
);
994 msleep((caddr_t
)&decompress_channel
, &decompress_channel_mtx
, PINOD
, "wait_for_decompress", NULL
);
998 #pragma mark --- decmpfs hide query routines ---
1001 decmpfs_hides_rsrc(vfs_context_t ctx
, decmpfs_cnode
*cp
)
1005 * callers may (and do) pass NULL for ctx, so we should only use it
1006 * for this equality comparison
1008 * This routine should only be called after a file has already been through decmpfs_file_is_compressed
1011 if (ctx
== decmpfs_ctx
) {
1015 if (!decmpfs_fast_file_is_compressed(cp
)) {
1019 /* all compressed files hide their resource fork */
1024 decmpfs_hides_xattr(vfs_context_t ctx
, decmpfs_cnode
*cp
, const char *xattr
)
1028 * callers may (and do) pass NULL for ctx, so we should only use it
1029 * for this equality comparison
1031 * This routine should only be called after a file has already been through decmpfs_file_is_compressed
1034 if (ctx
== decmpfs_ctx
) {
1037 if (strncmp(xattr
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
) - 1) == 0) {
1038 return decmpfs_hides_rsrc(ctx
, cp
);
1040 if (!decmpfs_fast_file_is_compressed(cp
)) {
1041 /* file is not compressed, so don't hide this xattr */
1044 if (strncmp(xattr
, DECMPFS_XATTR_NAME
, sizeof(DECMPFS_XATTR_NAME
) - 1) == 0) {
1045 /* it's our xattr, so hide it */
1048 /* don't hide this xattr */
1052 #pragma mark --- registration/validation routines ---
1055 registration_valid(const decmpfs_registration
*registration
)
1057 return registration
&& ((registration
->decmpfs_registration
== DECMPFS_REGISTRATION_VERSION_V1
) || (registration
->decmpfs_registration
== DECMPFS_REGISTRATION_VERSION_V3
));
1061 register_decmpfs_decompressor(uint32_t compression_type
, const decmpfs_registration
*registration
)
1063 /* called by kexts to register decompressors */
1067 char resourceName
[80];
1069 if ((compression_type
>= CMP_MAX
) || !registration_valid(registration
)) {
1074 lck_rw_lock_exclusive(&decompressorsLock
); locked
= 1;
1076 /* make sure the registration for this type is zero */
1077 if (decompressors
[compression_type
] != NULL
) {
1081 decompressors
[compression_type
] = registration
;
1082 snprintf(resourceName
, sizeof(resourceName
), "com.apple.AppleFSCompression.Type%u", compression_type
);
1083 IOServicePublishResource(resourceName
, TRUE
);
1087 lck_rw_unlock_exclusive(&decompressorsLock
);
1093 unregister_decmpfs_decompressor(uint32_t compression_type
, decmpfs_registration
*registration
)
1095 /* called by kexts to unregister decompressors */
1099 char resourceName
[80];
1101 if ((compression_type
>= CMP_MAX
) || !registration_valid(registration
)) {
1106 lck_rw_lock_exclusive(&decompressorsLock
); locked
= 1;
1107 if (decompressors
[compression_type
] != registration
) {
1111 decompressors
[compression_type
] = NULL
;
1112 snprintf(resourceName
, sizeof(resourceName
), "com.apple.AppleFSCompression.Type%u", compression_type
);
1113 IOServicePublishResource(resourceName
, FALSE
);
1117 lck_rw_unlock_exclusive(&decompressorsLock
);
1123 compression_type_valid(vnode_t vp
, decmpfs_header
*hdr
)
1125 /* fast pre-check to determine if the given compressor has checked in */
1128 /* every compressor must have at least a fetch function */
1129 lck_rw_lock_shared(&decompressorsLock
);
1130 if (decmp_get_func(vp
, hdr
->compression_type
, fetch
) != NULL
) {
1133 lck_rw_unlock_shared(&decompressorsLock
);
1138 #pragma mark --- compression/decompression routines ---
1141 decmpfs_fetch_uncompressed_data(vnode_t vp
, decmpfs_cnode
*cp
, decmpfs_header
*hdr
, off_t offset
, user_ssize_t size
, int nvec
, decmpfs_vector
*vec
, uint64_t *bytes_read
)
1143 /* get the uncompressed bytes for the specified region of vp by calling out to the registered compressor */
1149 if (offset
>= (off_t
)hdr
->uncompressed_size
) {
1150 /* reading past end of file; nothing to do */
1155 /* tried to read from before start of file */
1159 if (hdr
->uncompressed_size
- offset
< size
) {
1160 /* adjust size so we don't read past the end of the file */
1161 size
= (user_ssize_t
)(hdr
->uncompressed_size
- offset
);
1164 /* nothing to read */
1170 * Trace the following parameters on entry with event-id 0x03120008.
1172 * @vp->v_id: vnode-id of the file being decompressed.
1173 * @hdr->compression_type: compression type.
1174 * @offset: offset from where to fetch uncompressed data.
1175 * @size: amount of uncompressed data to fetch.
1177 * Please NOTE: @offset and @size can overflow in theory but
1180 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FETCH_UNCOMPRESSED_DATA
, vp
->v_id
,
1181 hdr
->compression_type
, (int)offset
, (int)size
);
1182 lck_rw_lock_shared(&decompressorsLock
);
1183 decmpfs_fetch_uncompressed_data_func fetch
= decmp_get_func(vp
, hdr
->compression_type
, fetch
);
1185 err
= fetch(vp
, decmpfs_ctx
, hdr
, offset
, size
, nvec
, vec
, bytes_read
);
1186 lck_rw_unlock_shared(&decompressorsLock
);
1188 uint64_t decompression_flags
= decmpfs_cnode_get_decompression_flags(cp
);
1189 if (decompression_flags
& DECMPFS_FLAGS_FORCE_FLUSH_ON_DECOMPRESS
) {
1190 #if !defined(__i386__) && !defined(__x86_64__)
1192 for (i
= 0; i
< nvec
; i
++) {
1193 assert(vec
[i
].size
>= 0 && vec
[i
].size
<= UINT_MAX
);
1194 flush_dcache64((addr64_t
)(uintptr_t)vec
[i
].buf
, (unsigned int)vec
[i
].size
, FALSE
);
1201 lck_rw_unlock_shared(&decompressorsLock
);
1204 * Trace the following parameters on return with event-id 0x03120008.
1206 * @vp->v_id: vnode-id of the file being decompressed.
1207 * @bytes_read: amount of uncompressed bytes fetched in bytes.
1208 * @err: value returned from this function.
1210 * Please NOTE: @bytes_read can overflow in theory but here it is safe.
1212 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FETCH_UNCOMPRESSED_DATA
, vp
->v_id
,
1213 (int)*bytes_read
, err
);
1218 static kern_return_t
1219 commit_upl(upl_t upl
, upl_offset_t pl_offset
, size_t uplSize
, int flags
, int abort
)
1221 kern_return_t kr
= 0;
1224 upl_unmark_decmp(upl
);
1225 #endif /* CONFIG_IOSCHED */
1227 /* commit the upl pages */
1229 VerboseLog("aborting upl, flags 0x%08x\n", flags
);
1230 kr
= ubc_upl_abort_range(upl
, pl_offset
, (upl_size_t
)uplSize
, flags
);
1231 if (kr
!= KERN_SUCCESS
) {
1232 ErrorLog("ubc_upl_abort_range error %d\n", (int)kr
);
1235 VerboseLog("committing upl, flags 0x%08x\n", flags
| UPL_COMMIT_CLEAR_DIRTY
);
1236 kr
= ubc_upl_commit_range(upl
, pl_offset
, (upl_size_t
)uplSize
, flags
| UPL_COMMIT_CLEAR_DIRTY
| UPL_COMMIT_WRITTEN_BY_KERNEL
);
1237 if (kr
!= KERN_SUCCESS
) {
1238 ErrorLog("ubc_upl_commit_range error %d\n", (int)kr
);
1246 decmpfs_pagein_compressed(struct vnop_pagein_args
*ap
, int *is_compressed
, decmpfs_cnode
*cp
)
1248 /* handles a page-in request from vfs for a compressed file */
1251 vnode_t vp
= ap
->a_vp
;
1252 upl_t pl
= ap
->a_pl
;
1253 upl_offset_t pl_offset
= ap
->a_pl_offset
;
1254 off_t f_offset
= ap
->a_f_offset
;
1255 size_t size
= ap
->a_size
;
1256 int flags
= ap
->a_flags
;
1258 user_ssize_t uplSize
= 0;
1259 size_t verify_block_size
= 0;
1261 decmpfs_header
*hdr
= NULL
;
1262 size_t alloc_size
= 0;
1263 uint64_t cachedSize
= 0;
1264 int cmpdata_locked
= 0;
1265 bool file_tail_page_valid
= false;
1266 int num_valid_pages
= 0;
1267 int num_invalid_pages
= 0;
1269 if (!decmpfs_trylock_compressed_data(cp
, 0)) {
1275 if (flags
& ~(UPL_IOSYNC
| UPL_NOCOMMIT
| UPL_NORDAHEAD
)) {
1276 DebugLogWithPath("pagein: unknown flags 0x%08x\n", (flags
& ~(UPL_IOSYNC
| UPL_NOCOMMIT
| UPL_NORDAHEAD
)));
1279 err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0, &alloc_size
);
1284 cachedSize
= hdr
->uncompressed_size
;
1286 if (!compression_type_valid(vp
, hdr
)) {
1287 /* compressor not registered */
1293 * If the verify block size is larger than the page size, the UPL needs
1294 * to be aligned to it, Since the UPL has been created by the filesystem,
1295 * we will only check if the passed in UPL length conforms to the
1296 * alignment requirements.
1298 err
= VNOP_VERIFY(vp
, f_offset
, NULL
, 0, &verify_block_size
,
1299 VNODE_VERIFY_DEFAULT
, NULL
);
1302 } else if (verify_block_size
) {
1303 if (verify_block_size
& (verify_block_size
- 1)) {
1304 ErrorLogWithPath("verify block size is not power of 2, no verification will be done\n");
1306 } else if (size
% verify_block_size
) {
1307 ErrorLogWithPath("upl size is not a multiple of verify block size\n");
1316 /* Mark the UPL as the requesting UPL for decompression */
1318 #endif /* CONFIG_IOSCHED */
1320 /* map the upl so we can fetch into it */
1321 kern_return_t kr
= ubc_upl_map(pl
, (vm_offset_t
*)&data
);
1322 if ((kr
!= KERN_SUCCESS
) || (data
== NULL
)) {
1326 upl_unmark_decmp(pl
);
1327 #endif /* CONFIG_IOSCHED */
1334 /* clip the size to the size of the file */
1335 if ((uint64_t)uplPos
+ uplSize
> cachedSize
) {
1336 /* truncate the read to the size of the file */
1337 uplSize
= (user_ssize_t
)(cachedSize
- uplPos
);
1344 /* the mapped data pointer points to the first page of the page list, so we want to start filling in at an offset of pl_offset */
1345 vec
= (decmpfs_vector
) {
1346 .buf
= (char*)data
+ pl_offset
,
1350 uint64_t did_read
= 0;
1351 if (decmpfs_fast_get_state(cp
) == FILE_IS_CONVERTING
) {
1352 ErrorLogWithPath("unexpected pagein during decompress\n");
1354 * if the file is converting, this must be a recursive call to pagein from underneath a call to decmpfs_decompress_file;
1355 * pretend that it succeeded but don't do anything since we're just going to write over the pages anyway
1359 if (!verify_block_size
|| (verify_block_size
<= PAGE_SIZE
)) {
1360 err
= decmpfs_fetch_uncompressed_data(vp
, cp
, hdr
, uplPos
, uplSize
, 1, &vec
, &did_read
);
1362 off_t l_uplPos
= uplPos
;
1363 off_t l_pl_offset
= pl_offset
;
1364 user_ssize_t l_uplSize
= uplSize
;
1365 upl_page_info_t
*pl_info
= ubc_upl_pageinfo(pl
);
1369 * When the system page size is less than the "verify block size",
1370 * the UPL passed may not consist solely of absent pages.
1371 * We have to detect the "absent" pages and only decompress
1372 * into those absent/invalid page ranges.
1374 * Things that will change in each iteration of the loop :
1376 * l_pl_offset = where we are inside the UPL [0, caller_upl_created_size)
1377 * l_uplPos = the file offset the l_pl_offset corresponds to.
1378 * l_uplSize = the size of the upl still unprocessed;
1380 * In this picture, we have to do the transfer on 2 ranges
1381 * (One 2 page range and one 3 page range) and the loop
1382 * below will skip the first two pages and then identify
1383 * the next two as invalid and fill those in and
1384 * then skip the next one and then do the last pages.
1386 * uplPos(file_offset)
1388 * 0 V<--------------> file_size
1389 * |--------------------------------------------------->
1390 * | | |V|V|I|I|V|I|I|I|
1393 * <------------------->
1397 * uplSize will be clipped in case the UPL range exceeds
1402 uint64_t l_did_read
= 0;
1403 int pl_offset_pg
= (int)(l_pl_offset
/ PAGE_SIZE
);
1404 int pages_left_in_upl
;
1409 * l_uplSize may start off less than the size of the upl,
1410 * we have to round it up to PAGE_SIZE to calculate
1411 * how many more pages are left.
1413 pages_left_in_upl
= (int)(round_page((vm_offset_t
)l_uplSize
) / PAGE_SIZE
);
1416 * scan from the beginning of the upl looking for the first
1417 * non-valid page.... this will become the first page in
1418 * the request we're going to make to
1419 * 'decmpfs_fetch_uncompressed_data'... if all
1420 * of the pages are valid, we won't call through
1421 * to 'decmpfs_fetch_uncompressed_data'
1423 for (start_pg
= 0; start_pg
< pages_left_in_upl
; start_pg
++) {
1424 if (!upl_valid_page(pl_info
, pl_offset_pg
+ start_pg
)) {
1429 num_valid_pages
+= start_pg
;
1432 * scan from the starting invalid page looking for
1433 * a valid page before the end of the upl is
1434 * reached, if we find one, then it will be the
1435 * last page of the request to 'decmpfs_fetch_uncompressed_data'
1437 for (last_pg
= start_pg
; last_pg
< pages_left_in_upl
; last_pg
++) {
1438 if (upl_valid_page(pl_info
, pl_offset_pg
+ last_pg
)) {
1443 if (start_pg
< last_pg
) {
1444 off_t inval_offset
= start_pg
* PAGE_SIZE
;
1445 int inval_pages
= last_pg
- start_pg
;
1446 int inval_size
= inval_pages
* PAGE_SIZE
;
1447 decmpfs_vector l_vec
;
1449 num_invalid_pages
+= inval_pages
;
1451 did_read
+= inval_offset
;
1452 l_pl_offset
+= inval_offset
;
1453 l_uplPos
+= inval_offset
;
1454 l_uplSize
-= inval_offset
;
1457 l_vec
= (decmpfs_vector
) {
1458 .buf
= (char*)data
+ l_pl_offset
,
1462 err
= decmpfs_fetch_uncompressed_data(vp
, cp
, hdr
, l_uplPos
,
1463 MIN(l_uplSize
, inval_size
), 1, &l_vec
, &l_did_read
);
1465 if (!err
&& (l_did_read
!= inval_size
) && (l_uplSize
> inval_size
)) {
1466 ErrorLogWithPath("Unexpected size fetch of decompressed data, l_uplSize = %d, l_did_read = %d, inval_size = %d\n",
1467 (int)l_uplSize
, (int)l_did_read
, (int)inval_size
);
1471 /* no invalid pages left */
1472 l_did_read
= l_uplSize
;
1473 if (uplSize
< size
) {
1474 file_tail_page_valid
= true;
1482 did_read
+= l_did_read
;
1483 l_pl_offset
+= l_did_read
;
1484 l_uplPos
+= l_did_read
;
1485 l_uplSize
-= l_did_read
;
1490 DebugLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err
);
1491 int cmp_state
= decmpfs_fast_get_state(cp
);
1492 if (cmp_state
== FILE_IS_CONVERTING
) {
1493 DebugLogWithPath("cmp_state == FILE_IS_CONVERTING\n");
1494 cmp_state
= wait_for_decompress(cp
);
1495 if (cmp_state
== FILE_IS_COMPRESSED
) {
1496 DebugLogWithPath("cmp_state == FILE_IS_COMPRESSED\n");
1497 /* a decompress was attempted but it failed, let's try calling fetch again */
1501 if (cmp_state
== FILE_IS_NOT_COMPRESSED
) {
1502 DebugLogWithPath("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1503 /* the file was decompressed after we started reading it */
1504 *is_compressed
= 0; /* instruct caller to fall back to its normal path */
1508 /* zero out whatever we didn't read, and zero out the end of the last page(s) */
1509 uint64_t total_size
= (size
+ (PAGE_SIZE
- 1)) & ~(PAGE_SIZE
- 1);
1510 if (did_read
< total_size
&& !(verify_block_size
&& err
)) {
1511 uint64_t rounded_up_did_read
= file_tail_page_valid
? (uint64_t)(round_page((vm_offset_t
)did_read
)) : did_read
;
1512 memset((char*)vec
.buf
+ rounded_up_did_read
, 0, (size_t)(total_size
- rounded_up_did_read
));
1515 if (!err
&& verify_block_size
) {
1516 size_t cur_verify_block_size
= verify_block_size
;
1518 if ((err
= VNOP_VERIFY(vp
, uplPos
, vec
.buf
, size
, &cur_verify_block_size
, 0, NULL
))) {
1519 ErrorLogWithPath("Verification failed with error %d, uplPos = %lld, uplSize = %d, did_read = %d, total_size = %d, valid_pages = %d, invalid_pages = %d, tail_page_valid = %d\n",
1520 err
, (long long)uplPos
, (int)uplSize
, (int)did_read
, (int)total_size
, num_valid_pages
, num_invalid_pages
, file_tail_page_valid
);
1522 /* XXX : If the verify block size changes, redo the read */
1526 upl_unmark_decmp(pl
);
1527 #endif /* CONFIG_IOSCHED */
1529 kr
= ubc_upl_unmap(pl
); data
= NULL
; /* make sure to set data to NULL so we don't try to unmap again below */
1530 if (kr
!= KERN_SUCCESS
) {
1531 ErrorLogWithPath("ubc_upl_unmap error %d\n", (int)kr
);
1534 /* commit our pages */
1535 kr
= commit_upl(pl
, pl_offset
, (size_t)total_size
, UPL_COMMIT_FREE_ON_EMPTY
, 0);
1544 kheap_free(KHEAP_TEMP
, hdr
, alloc_size
);
1546 if (cmpdata_locked
) {
1547 decmpfs_unlock_compressed_data(cp
, 0);
1551 if (err
!= ENXIO
&& err
!= ENOSPC
) {
1552 char *path
= zalloc(ZV_NAMEI
);
1553 panic("%s: decmpfs_pagein_compressed: err %d", vnpath(vp
, path
, PATH_MAX
), err
);
1554 zfree(ZV_NAMEI
, path
);
1557 ErrorLogWithPath("err %d\n", err
);
1563 decmpfs_read_compressed(struct vnop_read_args
*ap
, int *is_compressed
, decmpfs_cnode
*cp
)
1565 /* handles a read request from vfs for a compressed file */
1567 uio_t uio
= ap
->a_uio
;
1568 vnode_t vp
= ap
->a_vp
;
1572 user_ssize_t uplSize
= 0;
1573 user_ssize_t uplRemaining
= 0;
1574 off_t curUplPos
= 0;
1575 user_ssize_t curUplSize
= 0;
1576 kern_return_t kr
= KERN_SUCCESS
;
1579 uint64_t did_read
= 0;
1581 upl_page_info_t
*pli
= NULL
;
1582 decmpfs_header
*hdr
= NULL
;
1583 size_t alloc_size
= 0;
1584 uint64_t cachedSize
= 0;
1586 user_ssize_t uioRemaining
= 0;
1587 size_t verify_block_size
= 0;
1588 size_t alignment_size
= PAGE_SIZE
;
1589 int cmpdata_locked
= 0;
1591 decmpfs_lock_compressed_data(cp
, 0); cmpdata_locked
= 1;
1593 uplPos
= uio_offset(uio
);
1594 uplSize
= uio_resid(uio
);
1595 VerboseLogWithPath("uplPos %lld uplSize %lld\n", uplPos
, uplSize
);
1597 cachedSize
= decmpfs_cnode_get_vnode_cached_size(cp
);
1599 if ((uint64_t)uplPos
+ uplSize
> cachedSize
) {
1600 /* truncate the read to the size of the file */
1601 uplSize
= (user_ssize_t
)(cachedSize
- uplPos
);
1604 /* give the cluster layer a chance to fill in whatever it already has */
1605 countInt
= (uplSize
> INT_MAX
) ? INT_MAX
: (int)uplSize
;
1606 err
= cluster_copy_ubc_data(vp
, uio
, &countInt
, 0);
1611 /* figure out what's left */
1612 uioPos
= uio_offset(uio
);
1613 uioRemaining
= uio_resid(uio
);
1614 if ((uint64_t)uioPos
+ uioRemaining
> cachedSize
) {
1615 /* truncate the read to the size of the file */
1616 uioRemaining
= (user_ssize_t
)(cachedSize
- uioPos
);
1619 if (uioRemaining
<= 0) {
1624 err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0, &alloc_size
);
1628 if (!compression_type_valid(vp
, hdr
)) {
1634 uplSize
= uioRemaining
;
1635 #if COMPRESSION_DEBUG
1636 DebugLogWithPath("uplPos %lld uplSize %lld\n", (uint64_t)uplPos
, (uint64_t)uplSize
);
1639 lck_rw_lock_shared(&decompressorsLock
);
1640 decmpfs_adjust_fetch_region_func adjust_fetch
= decmp_get_func(vp
, hdr
->compression_type
, adjust_fetch
);
1642 /* give the compressor a chance to adjust the portion of the file that we read */
1643 adjust_fetch(vp
, decmpfs_ctx
, hdr
, &uplPos
, &uplSize
);
1644 VerboseLogWithPath("adjusted uplPos %lld uplSize %lld\n", (uint64_t)uplPos
, (uint64_t)uplSize
);
1646 lck_rw_unlock_shared(&decompressorsLock
);
1648 /* clip the adjusted size to the size of the file */
1649 if ((uint64_t)uplPos
+ uplSize
> cachedSize
) {
1650 /* truncate the read to the size of the file */
1651 uplSize
= (user_ssize_t
)(cachedSize
- uplPos
);
1660 * since we're going to create a upl for the given region of the file,
1661 * make sure we're on page boundaries
1664 /* If the verify block size is larger than the page size, the UPL needs to aligned to it */
1665 err
= VNOP_VERIFY(vp
, uplPos
, NULL
, 0, &verify_block_size
, VNODE_VERIFY_DEFAULT
, NULL
);
1668 } else if (verify_block_size
) {
1669 if (verify_block_size
& (verify_block_size
- 1)) {
1670 ErrorLogWithPath("verify block size is not power of 2, no verification will be done\n");
1671 verify_block_size
= 0;
1672 } else if (verify_block_size
> PAGE_SIZE
) {
1673 alignment_size
= verify_block_size
;
1677 if (uplPos
& (alignment_size
- 1)) {
1678 /* round position down to page boundary */
1679 uplSize
+= (uplPos
& (alignment_size
- 1));
1680 uplPos
&= ~(alignment_size
- 1);
1683 /* round size up to alignement_size multiple */
1684 uplSize
= (uplSize
+ (alignment_size
- 1)) & ~(alignment_size
- 1);
1686 VerboseLogWithPath("new uplPos %lld uplSize %lld\n", (uint64_t)uplPos
, (uint64_t)uplSize
);
1688 uplRemaining
= uplSize
;
1692 while (uplRemaining
> 0) {
1693 /* start after the last upl */
1694 curUplPos
+= curUplSize
;
1696 /* clip to max upl size */
1697 curUplSize
= uplRemaining
;
1698 if (curUplSize
> MAX_UPL_SIZE_BYTES
) {
1699 curUplSize
= MAX_UPL_SIZE_BYTES
;
1702 /* create the upl */
1703 kr
= ubc_create_upl_kernel(vp
, curUplPos
, (int)curUplSize
, &upl
, &pli
, UPL_SET_LITE
, VM_KERN_MEMORY_FILE
);
1704 if (kr
!= KERN_SUCCESS
) {
1705 ErrorLogWithPath("ubc_create_upl error %d\n", (int)kr
);
1709 VerboseLogWithPath("curUplPos %lld curUplSize %lld\n", (uint64_t)curUplPos
, (uint64_t)curUplSize
);
1712 /* Mark the UPL as the requesting UPL for decompression */
1713 upl_mark_decmp(upl
);
1714 #endif /* CONFIG_IOSCHED */
1717 kr
= ubc_upl_map(upl
, (vm_offset_t
*)&data
);
1718 if (kr
!= KERN_SUCCESS
) {
1719 commit_upl(upl
, 0, curUplSize
, UPL_ABORT_FREE_ON_EMPTY
, 1);
1721 char *path
= zalloc(ZV_NAMEI
);
1722 panic("%s: decmpfs_read_compressed: ubc_upl_map error %d", vnpath(vp
, path
, PATH_MAX
), (int)kr
);
1723 zfree(ZV_NAMEI
, path
);
1725 ErrorLogWithPath("ubc_upl_map kr=0x%x\n", (int)kr
);
1731 /* make sure the map succeeded */
1733 commit_upl(upl
, 0, curUplSize
, UPL_ABORT_FREE_ON_EMPTY
, 1);
1735 ErrorLogWithPath("ubc_upl_map mapped null\n");
1740 /* fetch uncompressed data into the mapped upl */
1743 vec
= (decmpfs_vector
){ .buf
= data
, .size
= curUplSize
};
1744 err
= decmpfs_fetch_uncompressed_data(vp
, cp
, hdr
, curUplPos
, curUplSize
, 1, &vec
, &did_read
);
1746 ErrorLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err
);
1748 /* maybe the file is converting to decompressed */
1749 int cmp_state
= decmpfs_fast_get_state(cp
);
1750 if (cmp_state
== FILE_IS_CONVERTING
) {
1751 ErrorLogWithPath("cmp_state == FILE_IS_CONVERTING\n");
1752 cmp_state
= wait_for_decompress(cp
);
1753 if (cmp_state
== FILE_IS_COMPRESSED
) {
1754 ErrorLogWithPath("cmp_state == FILE_IS_COMPRESSED\n");
1755 /* a decompress was attempted but it failed, let's try fetching again */
1759 if (cmp_state
== FILE_IS_NOT_COMPRESSED
) {
1760 ErrorLogWithPath("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1761 /* the file was decompressed after we started reading it */
1762 abort_read
= 1; /* we're not going to commit our data */
1763 *is_compressed
= 0; /* instruct caller to fall back to its normal path */
1769 /* zero out the remainder of the last page */
1770 memset((char*)data
+ did_read
, 0, (size_t)(curUplSize
- did_read
));
1771 if (!err
&& verify_block_size
) {
1772 size_t cur_verify_block_size
= verify_block_size
;
1774 if ((err
= VNOP_VERIFY(vp
, curUplPos
, data
, curUplSize
, &cur_verify_block_size
, 0, NULL
))) {
1775 ErrorLogWithPath("Verification failed with error %d\n", err
);
1778 /* XXX : If the verify block size changes, redo the read */
1781 kr
= ubc_upl_unmap(upl
);
1782 if (kr
== KERN_SUCCESS
) {
1784 kr
= commit_upl(upl
, 0, curUplSize
, UPL_ABORT_FREE_ON_EMPTY
, 1);
1786 VerboseLogWithPath("uioPos %lld uioRemaining %lld\n", (uint64_t)uioPos
, (uint64_t)uioRemaining
);
1788 off_t uplOff
= uioPos
- curUplPos
;
1790 ErrorLogWithPath("uplOff %lld should never be negative\n", (int64_t)uplOff
);
1792 } else if (uplOff
> INT_MAX
) {
1793 ErrorLogWithPath("uplOff %lld too large\n", (int64_t)uplOff
);
1796 off_t count
= curUplPos
+ curUplSize
- uioPos
;
1798 /* this upl is entirely before the uio */
1800 if (count
> uioRemaining
) {
1801 count
= uioRemaining
;
1803 int icount
= (count
> INT_MAX
) ? INT_MAX
: (int)count
;
1804 int io_resid
= icount
;
1805 err
= cluster_copy_upl_data(uio
, upl
, (int)uplOff
, &io_resid
);
1806 int copied
= icount
- io_resid
;
1807 VerboseLogWithPath("uplOff %lld count %lld copied %lld\n", (uint64_t)uplOff
, (uint64_t)count
, (uint64_t)copied
);
1809 ErrorLogWithPath("cluster_copy_upl_data err %d\n", err
);
1812 uioRemaining
-= copied
;
1816 kr
= commit_upl(upl
, 0, curUplSize
, UPL_COMMIT_FREE_ON_EMPTY
| UPL_COMMIT_INACTIVATE
, 0);
1822 ErrorLogWithPath("ubc_upl_unmap error %d\n", (int)kr
);
1825 uplRemaining
-= curUplSize
;
1831 kheap_free(KHEAP_TEMP
, hdr
, alloc_size
);
1833 if (cmpdata_locked
) {
1834 decmpfs_unlock_compressed_data(cp
, 0);
1836 if (err
) {/* something went wrong */
1837 ErrorLogWithPath("err %d\n", err
);
1841 #if COMPRESSION_DEBUG
1842 uplSize
= uio_resid(uio
);
1844 VerboseLogWithPath("still %lld bytes to copy\n", uplSize
);
1851 decmpfs_free_compressed_data(vnode_t vp
, decmpfs_cnode
*cp
)
1854 * call out to the decompressor to free remove any data associated with this compressed file
1855 * then delete the file's compression xattr
1857 decmpfs_header
*hdr
= NULL
;
1858 size_t alloc_size
= 0;
1861 * Trace the following parameters on entry with event-id 0x03120010.
1863 * @vp->v_id: vnode-id of the file for which to free compressed data.
1865 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FREE_COMPRESSED_DATA
, vp
->v_id
);
1867 int err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0, &alloc_size
);
1869 ErrorLogWithPath("decmpfs_fetch_compressed_header err %d\n", err
);
1871 lck_rw_lock_shared(&decompressorsLock
);
1872 decmpfs_free_compressed_data_func free_data
= decmp_get_func(vp
, hdr
->compression_type
, free_data
);
1874 err
= free_data(vp
, decmpfs_ctx
, hdr
);
1876 /* nothing to do, so no error */
1879 lck_rw_unlock_shared(&decompressorsLock
);
1882 ErrorLogWithPath("decompressor err %d\n", err
);
1886 * Trace the following parameters on return with event-id 0x03120010.
1888 * @vp->v_id: vnode-id of the file for which to free compressed data.
1889 * @err: value returned from this function.
1891 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FREE_COMPRESSED_DATA
, vp
->v_id
, err
);
1893 /* delete the xattr */
1894 err
= vn_removexattr(vp
, DECMPFS_XATTR_NAME
, 0, decmpfs_ctx
);
1897 kheap_free(KHEAP_TEMP
, hdr
, alloc_size
);
1902 #pragma mark --- file conversion routines ---
1905 unset_compressed_flag(vnode_t vp
)
1908 struct vnode_attr va
;
1909 int new_bsdflags
= 0;
1912 VATTR_WANTED(&va
, va_flags
);
1913 err
= vnode_getattr(vp
, &va
, decmpfs_ctx
);
1916 ErrorLogWithPath("vnode_getattr err %d\n", err
);
1918 new_bsdflags
= va
.va_flags
& ~UF_COMPRESSED
;
1921 VATTR_SET(&va
, va_flags
, new_bsdflags
);
1922 err
= vnode_setattr(vp
, &va
, decmpfs_ctx
);
1924 ErrorLogWithPath("vnode_setattr err %d\n", err
);
1931 decmpfs_decompress_file(vnode_t vp
, decmpfs_cnode
*cp
, off_t toSize
, int truncate_okay
, int skiplock
)
1933 /* convert a compressed file to an uncompressed file */
1939 uint32_t old_state
= 0;
1940 uint32_t new_state
= 0;
1941 int update_file_state
= 0;
1942 size_t allocSize
= 0;
1943 decmpfs_header
*hdr
= NULL
;
1944 size_t hdr_size
= 0;
1945 int cmpdata_locked
= 0;
1946 off_t remaining
= 0;
1947 uint64_t uncompressed_size
= 0;
1950 * Trace the following parameters on entry with event-id 0x03120000.
1952 * @vp->v_id: vnode-id of the file being decompressed.
1953 * @toSize: uncompress given bytes of the file.
1954 * @truncate_okay: on error it is OK to truncate.
1955 * @skiplock: compressed data is locked, skip locking again.
1957 * Please NOTE: @toSize can overflow in theory but here it is safe.
1959 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_DECOMPRESS_FILE
, vp
->v_id
,
1960 (int)toSize
, truncate_okay
, skiplock
);
1963 decmpfs_lock_compressed_data(cp
, 1); cmpdata_locked
= 1;
1967 old_state
= decmpfs_fast_get_state(cp
);
1969 switch (old_state
) {
1970 case FILE_IS_NOT_COMPRESSED
:
1972 /* someone else decompressed the file */
1977 case FILE_TYPE_UNKNOWN
:
1979 /* the file is in an unknown state, so update the state and retry */
1980 (void)decmpfs_file_is_compressed(vp
, cp
);
1986 case FILE_IS_COMPRESSED
:
1988 /* the file is compressed, so decompress it */
1995 * this shouldn't happen since multiple calls to decmpfs_decompress_file lock each other out,
1996 * and when decmpfs_decompress_file returns, the state should be always be set back to
1997 * FILE_IS_NOT_COMPRESSED or FILE_IS_UNKNOWN
2004 err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0, &hdr_size
);
2009 uncompressed_size
= hdr
->uncompressed_size
;
2011 toSize
= hdr
->uncompressed_size
;
2015 /* special case truncating the file to zero bytes */
2017 } else if ((uint64_t)toSize
> hdr
->uncompressed_size
) {
2018 /* the caller is trying to grow the file, so we should decompress all the data */
2019 toSize
= hdr
->uncompressed_size
;
2022 allocSize
= MIN(64 * 1024, (size_t)toSize
);
2023 data
= kheap_alloc(KHEAP_TEMP
, allocSize
, Z_WAITOK
);
2029 uio_w
= uio_create(1, 0LL, UIO_SYSSPACE
, UIO_WRITE
);
2034 uio_w
->uio_flags
|= UIO_FLAGS_IS_COMPRESSED_FILE
;
2038 /* tell the buffer cache that this is an empty file */
2041 /* if we got here, we need to decompress the file */
2042 decmpfs_cnode_set_vnode_state(cp
, FILE_IS_CONVERTING
, 1);
2044 while (remaining
> 0) {
2045 /* loop decompressing data from the file and writing it into the data fork */
2047 uint64_t bytes_read
= 0;
2048 decmpfs_vector vec
= { .buf
= data
, .size
= (user_ssize_t
)MIN(allocSize
, remaining
) };
2049 err
= decmpfs_fetch_uncompressed_data(vp
, cp
, hdr
, offset
, vec
.size
, 1, &vec
, &bytes_read
);
2051 ErrorLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err
);
2055 if (bytes_read
== 0) {
2056 /* we're done reading data */
2060 uio_reset(uio_w
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
2061 err
= uio_addiov(uio_w
, CAST_USER_ADDR_T(data
), (user_size_t
)bytes_read
);
2063 ErrorLogWithPath("uio_addiov err %d\n", err
);
2068 err
= VNOP_WRITE(vp
, uio_w
, 0, decmpfs_ctx
);
2070 /* if the write failed, truncate the file to zero bytes */
2071 ErrorLogWithPath("VNOP_WRITE err %d\n", err
);
2074 offset
+= bytes_read
;
2075 remaining
-= bytes_read
;
2079 if (offset
!= toSize
) {
2080 ErrorLogWithPath("file decompressed to %lld instead of %lld\n", offset
, toSize
);
2087 /* sync the data and metadata */
2088 err
= VNOP_FSYNC(vp
, MNT_WAIT
, decmpfs_ctx
);
2090 ErrorLogWithPath("VNOP_FSYNC err %d\n", err
);
2096 /* write, setattr, or fsync failed */
2097 ErrorLogWithPath("aborting decompress, err %d\n", err
);
2098 if (truncate_okay
) {
2099 /* truncate anything we might have written */
2100 int error
= vnode_setsize(vp
, 0, 0, decmpfs_ctx
);
2101 ErrorLogWithPath("vnode_setsize err %d\n", error
);
2107 /* if we're truncating the file to zero bytes, we'll skip ahead to here */
2109 /* unset the compressed flag */
2110 unset_compressed_flag(vp
);
2112 /* free the compressed data associated with this file */
2113 err
= decmpfs_free_compressed_data(vp
, cp
);
2115 ErrorLogWithPath("decmpfs_free_compressed_data err %d\n", err
);
2119 * even if free_compressed_data or vnode_getattr/vnode_setattr failed, return success
2120 * since we succeeded in writing all of the file data to the data fork
2124 /* if we got this far, the file was successfully decompressed */
2125 update_file_state
= 1;
2126 new_state
= FILE_IS_NOT_COMPRESSED
;
2128 #if COMPRESSION_DEBUG
2130 uint64_t filesize
= 0;
2131 vnsize(vp
, &filesize
);
2132 DebugLogWithPath("new file size %lld\n", filesize
);
2138 kheap_free(KHEAP_TEMP
, hdr
, hdr_size
);
2140 kheap_free(KHEAP_TEMP
, data
, allocSize
);
2146 /* if there was a failure, reset compression flags to unknown and clear the buffer cache data */
2147 update_file_state
= 1;
2148 new_state
= FILE_TYPE_UNKNOWN
;
2149 if (uncompressed_size
) {
2151 ubc_setsize(vp
, uncompressed_size
);
2155 if (update_file_state
) {
2156 lck_mtx_lock(&decompress_channel_mtx
);
2157 decmpfs_cnode_set_vnode_state(cp
, new_state
, 1);
2158 wakeup((caddr_t
)&decompress_channel
); /* wake up anyone who might have been waiting for decompression */
2159 lck_mtx_unlock(&decompress_channel_mtx
);
2162 if (cmpdata_locked
) {
2163 decmpfs_unlock_compressed_data(cp
, 1);
2166 * Trace the following parameters on return with event-id 0x03120000.
2168 * @vp->v_id: vnode-id of the file being decompressed.
2169 * @err: value returned from this function.
2171 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_DECOMPRESS_FILE
, vp
->v_id
, err
);
2175 #pragma mark --- Type1 compressor ---
2178 * The "Type1" compressor stores the data fork directly in the compression xattr
2182 decmpfs_validate_compressed_file_Type1(__unused vnode_t vp
, __unused vfs_context_t ctx
, decmpfs_header
*hdr
)
2186 if (hdr
->uncompressed_size
+ sizeof(decmpfs_disk_header
) != (uint64_t)hdr
->attr_size
) {
2195 decmpfs_fetch_uncompressed_data_Type1(__unused vnode_t vp
, __unused vfs_context_t ctx
, decmpfs_header
*hdr
, off_t offset
, user_ssize_t size
, int nvec
, decmpfs_vector
*vec
, uint64_t *bytes_read
)
2199 user_ssize_t remaining
;
2201 if (hdr
->uncompressed_size
+ sizeof(decmpfs_disk_header
) != (uint64_t)hdr
->attr_size
) {
2206 #if COMPRESSION_DEBUG
2207 static int dummy
= 0; // prevent syslog from coalescing printfs
2208 DebugLogWithPath("%d memcpy %lld at %lld\n", dummy
++, size
, (uint64_t)offset
);
2212 for (i
= 0; (i
< nvec
) && (remaining
> 0); i
++) {
2213 user_ssize_t curCopy
= vec
[i
].size
;
2214 if (curCopy
> remaining
) {
2215 curCopy
= remaining
;
2217 memcpy(vec
[i
].buf
, hdr
->attr_bytes
+ offset
, curCopy
);
2219 remaining
-= curCopy
;
2222 if ((bytes_read
) && (err
== 0)) {
2223 *bytes_read
= (size
- remaining
);
2230 SECURITY_READ_ONLY_EARLY(static decmpfs_registration
) Type1Reg
=
2232 .decmpfs_registration
= DECMPFS_REGISTRATION_VERSION
,
2233 .validate
= decmpfs_validate_compressed_file_Type1
,
2234 .adjust_fetch
= NULL
,/* no adjust necessary */
2235 .fetch
= decmpfs_fetch_uncompressed_data_Type1
,
2236 .free_data
= NULL
,/* no free necessary */
2237 .get_flags
= NULL
/* no flags */
2240 #pragma mark --- decmpfs initialization ---
2245 static int done
= 0;
2250 decmpfs_ctx
= vfs_context_create(vfs_context_kernel());
2252 register_decmpfs_decompressor(CMP_Type1
, &Type1Reg
);
2256 #endif /* FS_COMPRESSION */