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>
75 #pragma mark --- debugging ---
77 #define COMPRESSION_DEBUG 0
78 #define COMPRESSION_DEBUG_VERBOSE 0
79 #define MALLOC_DEBUG 0
82 baseName(const char *path
)
87 const char *ret
= path
;
89 for (i
= 0; path
[i
] != 0; i
++) {
98 vnpath(vnode_t vp
, char *path
, int len
)
102 vn_getpath(vp
, path
, &len
);
103 path
[origlen
- 1] = 0;
107 #define ErrorLog(x, args...) printf("%s:%d:%s: " x, baseName(__FILE__), __LINE__, __FUNCTION__, ## args)
108 #define ErrorLogWithPath(x, args...) do { char *path; MALLOC(path, char *, PATH_MAX, M_TEMP, M_WAITOK); printf("%s:%d:%s: %s: " x, baseName(__FILE__), __LINE__, __FUNCTION__, vnpath(vp, path, PATH_MAX), ## args); FREE(path, M_TEMP); } while(0)
110 #if COMPRESSION_DEBUG
111 #define DebugLog ErrorLog
112 #define DebugLogWithPath ErrorLogWithPath
114 #define DebugLog(x...) do { } while(0)
115 #define DebugLogWithPath(x...) do { } while(0)
118 #if COMPRESSION_DEBUG_VERBOSE
119 #define VerboseLog ErrorLog
120 #define VerboseLogWithPath ErrorLogWithPath
122 #define VerboseLog(x...) do { } while(0)
123 #define VerboseLogWithPath(x...) do { } while(0)
128 static SInt32 totalAlloc
;
138 _malloc(uint32_t sz
, __unused
int type
, __unused
int flags
, const char *file
, int line
)
140 uint32_t allocSz
= sz
+ 2 * sizeof(allocated
);
142 allocated
*alloc
= NULL
;
143 MALLOC(alloc
, allocated
*, allocSz
, type
, flags
);
145 ErrorLog("malloc failed\n");
149 char *ret
= (char*)&alloc
[1];
150 allocated
*alloc2
= (allocated
*)(ret
+ sz
);
152 alloc
->allocSz
= allocSz
;
153 alloc
->magic
= 0xdadadada;
159 int s
= OSAddAtomic(sz
, &totalAlloc
);
160 ErrorLog("malloc(%d) -> %p, total allocations %d\n", sz
, ret
, s
+ sz
);
166 _free(char *ret
, __unused
int type
, const char *file
, int line
)
169 ErrorLog("freeing null\n");
172 allocated
*alloc
= (allocated
*)ret
;
174 uint32_t sz
= alloc
->allocSz
- 2 * sizeof(allocated
);
175 allocated
*alloc2
= (allocated
*)(ret
+ sz
);
177 if (alloc
->magic
!= 0xdadadada) {
178 panic("freeing bad pointer");
181 if (memcmp(alloc
, alloc2
, sizeof(*alloc
)) != 0) {
182 panic("clobbered data");
185 memset(ret
, 0xce, sz
);
189 int s
= OSAddAtomic(-sz
, &totalAlloc
);
190 ErrorLog("free(%p,%d) -> total allocations %d\n", ret
, sz
, s
- sz
);
195 #define MALLOC(space, cast, size, type, flags) (space) = (cast)_malloc(size, type, flags, __FILE__, __LINE__)
196 #define FREE(addr, type) _free((void *)addr, type, __FILE__, __LINE__)
198 #endif /* MALLOC_DEBUG */
200 #pragma mark --- globals ---
202 static lck_grp_t
*decmpfs_lockgrp
;
204 SECURITY_READ_ONLY_EARLY(static decmpfs_registration
*) decompressors
[CMP_MAX
]; /* the registered compressors */
205 static lck_rw_t
* decompressorsLock
;
206 static int decompress_channel
; /* channel used by decompress_file to wake up waiters */
207 static lck_mtx_t
*decompress_channel_mtx
;
209 vfs_context_t decmpfs_ctx
;
211 #pragma mark --- decmp_get_func ---
213 #define offsetof_func(func) ((uintptr_t)(&(((decmpfs_registration*)NULL)->func)))
216 _func_from_offset(uint32_t type
, uintptr_t offset
)
218 /* get the function at the given offset in the registration for the given type */
219 const decmpfs_registration
*reg
= decompressors
[type
];
220 const char *regChar
= (const char*)reg
;
221 const char *func
= ®Char
[offset
];
222 void * const * funcPtr
= (void * const *) func
;
224 switch (reg
->decmpfs_registration
) {
225 case DECMPFS_REGISTRATION_VERSION_V1
:
226 if (offset
> offsetof_func(free_data
)) {
230 case DECMPFS_REGISTRATION_VERSION_V3
:
231 if (offset
> offsetof_func(get_flags
)) {
242 extern void IOServicePublishResource( const char * property
, boolean_t value
);
243 extern boolean_t
IOServiceWaitForMatchingResource( const char * property
, uint64_t timeout
);
244 extern boolean_t
IOCatalogueMatchingDriversPresent( const char * property
);
247 _decmp_get_func(vnode_t vp
, uint32_t type
, uintptr_t offset
)
250 * this function should be called while holding a shared lock to decompressorsLock,
251 * and will return with the lock held
254 if (type
>= CMP_MAX
) {
258 if (decompressors
[type
] != NULL
) {
259 // the compressor has already registered but the function might be null
260 return _func_from_offset(type
, offset
);
263 // does IOKit know about a kext that is supposed to provide this type?
264 char providesName
[80];
265 snprintf(providesName
, sizeof(providesName
), "com.apple.AppleFSCompression.providesType%u", type
);
266 if (IOCatalogueMatchingDriversPresent(providesName
)) {
267 // there is a kext that says it will register for this type, so let's wait for it
268 char resourceName
[80];
269 uint64_t delay
= 10000000ULL; // 10 milliseconds.
270 snprintf(resourceName
, sizeof(resourceName
), "com.apple.AppleFSCompression.Type%u", type
);
271 ErrorLogWithPath("waiting for %s\n", resourceName
);
272 while (decompressors
[type
] == NULL
) {
273 lck_rw_unlock_shared(decompressorsLock
); // we have to unlock to allow the kext to register
274 if (IOServiceWaitForMatchingResource(resourceName
, delay
)) {
275 lck_rw_lock_shared(decompressorsLock
);
278 if (!IOCatalogueMatchingDriversPresent(providesName
)) {
280 ErrorLogWithPath("the kext with %s is no longer present\n", providesName
);
281 lck_rw_lock_shared(decompressorsLock
);
284 ErrorLogWithPath("still waiting for %s\n", resourceName
);
286 lck_rw_lock_shared(decompressorsLock
);
288 // IOKit says the kext is loaded, so it should be registered too!
289 if (decompressors
[type
] == NULL
) {
290 ErrorLogWithPath("we found %s, but the type still isn't registered\n", providesName
);
293 // it's now registered, so let's return the function
294 return _func_from_offset(type
, offset
);
297 // the compressor hasn't registered, so it never will unless someone manually kextloads it
298 ErrorLogWithPath("tried to access a compressed file of unregistered type %d\n", type
);
302 #define decmp_get_func(vp, type, func) ((typeof(((decmpfs_registration*)NULL)->func))_decmp_get_func(vp, type, offsetof_func(func)))
304 #pragma mark --- utilities ---
306 #if COMPRESSION_DEBUG
308 vnsize(vnode_t vp
, uint64_t *size
)
310 struct vnode_attr va
;
312 VATTR_WANTED(&va
, va_data_size
);
313 int error
= vnode_getattr(vp
, &va
, decmpfs_ctx
);
315 ErrorLogWithPath("vnode_getattr err %d\n", error
);
318 *size
= va
.va_data_size
;
321 #endif /* COMPRESSION_DEBUG */
323 #pragma mark --- cnode routines ---
326 decmpfs_cnode_alloc(void)
329 MALLOC_ZONE(dp
, decmpfs_cnode
*, sizeof(decmpfs_cnode
), M_DECMPFS_CNODE
, M_WAITOK
);
334 decmpfs_cnode_free(decmpfs_cnode
*dp
)
336 FREE_ZONE(dp
, sizeof(*dp
), M_DECMPFS_CNODE
);
340 decmpfs_cnode_init(decmpfs_cnode
*cp
)
342 memset(cp
, 0, sizeof(*cp
));
343 lck_rw_init(&cp
->compressed_data_lock
, decmpfs_lockgrp
, NULL
);
347 decmpfs_cnode_destroy(decmpfs_cnode
*cp
)
349 lck_rw_destroy(&cp
->compressed_data_lock
, decmpfs_lockgrp
);
353 decmpfs_trylock_compressed_data(decmpfs_cnode
*cp
, int exclusive
)
355 void *thread
= current_thread();
358 if (cp
->lockowner
== thread
) {
359 /* this thread is already holding an exclusive lock, so bump the count */
362 } else if (exclusive
) {
363 if ((retval
= lck_rw_try_lock_exclusive(&cp
->compressed_data_lock
))) {
364 cp
->lockowner
= thread
;
368 if ((retval
= lck_rw_try_lock_shared(&cp
->compressed_data_lock
))) {
369 cp
->lockowner
= (void *)-1;
376 decmpfs_lock_compressed_data(decmpfs_cnode
*cp
, int exclusive
)
378 void *thread
= current_thread();
380 if (cp
->lockowner
== thread
) {
381 /* this thread is already holding an exclusive lock, so bump the count */
383 } else if (exclusive
) {
384 lck_rw_lock_exclusive(&cp
->compressed_data_lock
);
385 cp
->lockowner
= thread
;
388 lck_rw_lock_shared(&cp
->compressed_data_lock
);
389 cp
->lockowner
= (void *)-1;
394 decmpfs_unlock_compressed_data(decmpfs_cnode
*cp
, __unused
int exclusive
)
396 void *thread
= current_thread();
398 if (cp
->lockowner
== thread
) {
399 /* this thread is holding an exclusive lock, so decrement the count */
400 if ((--cp
->lockcount
) > 0) {
401 /* the caller still has outstanding locks, so we're done */
404 cp
->lockowner
= NULL
;
407 lck_rw_done(&cp
->compressed_data_lock
);
411 decmpfs_cnode_get_vnode_state(decmpfs_cnode
*cp
)
413 return cp
->cmp_state
;
417 decmpfs_cnode_set_vnode_state(decmpfs_cnode
*cp
, uint32_t state
, int skiplock
)
420 decmpfs_lock_compressed_data(cp
, 1);
422 cp
->cmp_state
= state
;
423 if (state
== FILE_TYPE_UNKNOWN
) {
424 /* clear out the compression type too */
428 decmpfs_unlock_compressed_data(cp
, 1);
433 decmpfs_cnode_set_vnode_cmp_type(decmpfs_cnode
*cp
, uint32_t cmp_type
, int skiplock
)
436 decmpfs_lock_compressed_data(cp
, 1);
438 cp
->cmp_type
= cmp_type
;
440 decmpfs_unlock_compressed_data(cp
, 1);
445 decmpfs_cnode_set_vnode_minimal_xattr(decmpfs_cnode
*cp
, int minimal_xattr
, int skiplock
)
448 decmpfs_lock_compressed_data(cp
, 1);
450 cp
->cmp_minimal_xattr
= minimal_xattr
;
452 decmpfs_unlock_compressed_data(cp
, 1);
457 decmpfs_cnode_get_vnode_cached_size(decmpfs_cnode
*cp
)
459 return cp
->uncompressed_size
;
463 decmpfs_cnode_get_vnode_cached_nchildren(decmpfs_cnode
*cp
)
465 return cp
->nchildren
;
469 decmpfs_cnode_get_vnode_cached_total_size(decmpfs_cnode
*cp
)
471 return cp
->total_size
;
475 decmpfs_cnode_set_vnode_cached_size(decmpfs_cnode
*cp
, uint64_t size
)
478 uint64_t old
= cp
->uncompressed_size
;
479 if (OSCompareAndSwap64(old
, size
, (UInt64
*)&cp
->uncompressed_size
)) {
482 /* failed to write our value, so loop */
488 decmpfs_cnode_set_vnode_cached_nchildren(decmpfs_cnode
*cp
, uint64_t nchildren
)
491 uint64_t old
= cp
->nchildren
;
492 if (OSCompareAndSwap64(old
, nchildren
, (UInt64
*)&cp
->nchildren
)) {
495 /* failed to write our value, so loop */
501 decmpfs_cnode_set_vnode_cached_total_size(decmpfs_cnode
*cp
, uint64_t total_sz
)
504 uint64_t old
= cp
->total_size
;
505 if (OSCompareAndSwap64(old
, total_sz
, (UInt64
*)&cp
->total_size
)) {
508 /* failed to write our value, so loop */
514 decmpfs_cnode_get_decompression_flags(decmpfs_cnode
*cp
)
516 return cp
->decompression_flags
;
520 decmpfs_cnode_set_decompression_flags(decmpfs_cnode
*cp
, uint64_t flags
)
523 uint64_t old
= cp
->decompression_flags
;
524 if (OSCompareAndSwap64(old
, flags
, (UInt64
*)&cp
->decompression_flags
)) {
527 /* failed to write our value, so loop */
533 decmpfs_cnode_cmp_type(decmpfs_cnode
*cp
)
538 #pragma mark --- decmpfs state routines ---
541 decmpfs_fetch_compressed_header(vnode_t vp
, decmpfs_cnode
*cp
, decmpfs_header
**hdrOut
, int returnInvalid
)
544 * fetches vp's compression xattr, converting it into a decmpfs_header; returns 0 or errno
545 * if returnInvalid == 1, returns the header even if the type was invalid (out of range),
546 * and return ERANGE in that case
549 size_t read_size
= 0;
550 size_t attr_size
= 0;
551 uio_t attr_uio
= NULL
;
554 const bool no_additional_data
= ((cp
!= NULL
)
555 && (cp
->cmp_type
!= 0)
556 && (cp
->cmp_minimal_xattr
!= 0));
557 char uio_buf
[UIO_SIZEOF(1)];
558 decmpfs_header
*hdr
= NULL
;
561 * Trace the following parameters on entry with event-id 0x03120004
563 * @vp->v_id: vnode-id for which to fetch compressed header.
564 * @no_additional_data: If set true then xattr didn't have any extra data.
565 * @returnInvalid: return the header even though the type is out of range.
567 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FETCH_COMPRESSED_HEADER
, vp
->v_id
,
568 no_additional_data
, returnInvalid
);
570 if (no_additional_data
) {
571 /* 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 */
573 MALLOC(data
, char *, sizeof(decmpfs_header
), M_TEMP
, M_WAITOK
);
578 hdr
= (decmpfs_header
*)data
;
579 hdr
->attr_size
= sizeof(decmpfs_disk_header
);
580 hdr
->compression_magic
= DECMPFS_MAGIC
;
581 hdr
->compression_type
= cp
->cmp_type
;
582 if (hdr
->compression_type
== DATALESS_PKG_CMPFS_TYPE
) {
583 if (!vnode_isdir(vp
)) {
587 hdr
->_size
.value
= DECMPFS_PKG_VALUE_FROM_SIZE_COUNT(
588 decmpfs_cnode_get_vnode_cached_size(cp
),
589 decmpfs_cnode_get_vnode_cached_nchildren(cp
));
590 } else if (vnode_isdir(vp
)) {
591 hdr
->_size
.value
= decmpfs_cnode_get_vnode_cached_nchildren(cp
);
593 hdr
->_size
.value
= decmpfs_cnode_get_vnode_cached_size(cp
);
596 /* figure out how big the xattr is on disk */
597 err
= vn_getxattr(vp
, DECMPFS_XATTR_NAME
, NULL
, &attr_size
, XATTR_NOSECURITY
, decmpfs_ctx
);
602 if (attr_size
< sizeof(decmpfs_disk_header
) || attr_size
> MAX_DECMPFS_XATTR_SIZE
) {
607 /* allocation includes space for the extra attr_size field of a compressed_header */
608 MALLOC(data
, char *, attr_size
+ sizeof(hdr
->attr_size
), M_TEMP
, M_WAITOK
);
614 /* read the xattr into our buffer, skipping over the attr_size field at the beginning */
615 attr_uio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
, &uio_buf
[0], sizeof(uio_buf
));
616 uio_addiov(attr_uio
, CAST_USER_ADDR_T(data
+ sizeof(hdr
->attr_size
)), attr_size
);
618 err
= vn_getxattr(vp
, DECMPFS_XATTR_NAME
, attr_uio
, &read_size
, XATTR_NOSECURITY
, decmpfs_ctx
);
622 if (read_size
!= attr_size
) {
626 hdr
= (decmpfs_header
*)data
;
627 hdr
->attr_size
= attr_size
;
628 /* swap the fields to native endian */
629 hdr
->compression_magic
= OSSwapLittleToHostInt32(hdr
->compression_magic
);
630 hdr
->compression_type
= OSSwapLittleToHostInt32(hdr
->compression_type
);
631 hdr
->uncompressed_size
= OSSwapLittleToHostInt64(hdr
->uncompressed_size
);
634 if (hdr
->compression_magic
!= DECMPFS_MAGIC
) {
635 ErrorLogWithPath("invalid compression_magic 0x%08x, should be 0x%08x\n", hdr
->compression_magic
, DECMPFS_MAGIC
);
641 * Special-case the DATALESS compressor here; that is a valid type,
642 * even through there will never be an entry in the decompressor
643 * handler table for it. If we don't do this, then the cmp_state
644 * for this cnode will end up being marked NOT_COMPRESSED, and
645 * we'll be stuck in limbo.
647 if (hdr
->compression_type
>= CMP_MAX
&& !decmpfs_type_is_dataless(hdr
->compression_type
)) {
649 /* return the header even though the type is out of range */
652 ErrorLogWithPath("compression_type %d out of range\n", hdr
->compression_type
);
659 if (err
&& (err
!= ERANGE
)) {
660 DebugLogWithPath("err %d\n", err
);
669 * Trace the following parameters on return with event-id 0x03120004.
671 * @vp->v_id: vnode-id for which to fetch compressed header.
672 * @err: value returned from this function.
674 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FETCH_COMPRESSED_HEADER
, vp
->v_id
, err
);
679 decmpfs_fast_get_state(decmpfs_cnode
*cp
)
682 * return the cached state
683 * this should *only* be called when we know that decmpfs_file_is_compressed has already been called,
684 * because this implies that the cached state is valid
686 int cmp_state
= decmpfs_cnode_get_vnode_state(cp
);
689 case FILE_IS_NOT_COMPRESSED
:
690 case FILE_IS_COMPRESSED
:
691 case FILE_IS_CONVERTING
:
693 case FILE_TYPE_UNKNOWN
:
695 * we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
696 * which should not be possible
698 ErrorLog("decmpfs_fast_get_state called on unknown file\n");
699 return FILE_IS_NOT_COMPRESSED
;
702 ErrorLog("unknown cmp_state %d\n", cmp_state
);
703 return FILE_IS_NOT_COMPRESSED
;
708 decmpfs_fast_file_is_compressed(decmpfs_cnode
*cp
)
710 int cmp_state
= decmpfs_cnode_get_vnode_state(cp
);
713 case FILE_IS_NOT_COMPRESSED
:
715 case FILE_IS_COMPRESSED
:
716 case FILE_IS_CONVERTING
:
718 case FILE_TYPE_UNKNOWN
:
720 * we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
721 * which should not be possible
723 ErrorLog("decmpfs_fast_get_state called on unknown file\n");
727 ErrorLog("unknown cmp_state %d\n", cmp_state
);
733 decmpfs_validate_compressed_file(vnode_t vp
, decmpfs_cnode
*cp
)
735 /* give a compressor a chance to indicate that a compressed file is invalid */
737 decmpfs_header
*hdr
= NULL
;
738 errno_t err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0);
740 /* we couldn't get the header */
741 if (decmpfs_fast_get_state(cp
) == FILE_IS_NOT_COMPRESSED
) {
742 /* the file is no longer compressed, so return success */
748 if (!decmpfs_type_is_dataless(hdr
->compression_type
)) {
749 lck_rw_lock_shared(decompressorsLock
);
750 decmpfs_validate_compressed_file_func validate
= decmp_get_func(vp
, hdr
->compression_type
, validate
);
751 if (validate
) { /* make sure this validation function is valid */
752 /* is the data okay? */
753 err
= validate(vp
, decmpfs_ctx
, hdr
);
754 } else if (decmp_get_func(vp
, hdr
->compression_type
, fetch
) == NULL
) {
755 /* the type isn't registered */
758 /* no validate registered, so nothing to do */
761 lck_rw_unlock_shared(decompressorsLock
);
767 #if COMPRESSION_DEBUG
769 DebugLogWithPath("decmpfs_validate_compressed_file ret %d, vp->v_flag %d\n", err
, vp
->v_flag
);
776 decmpfs_file_is_compressed(vnode_t vp
, decmpfs_cnode
*cp
)
779 * determines whether vp points to a compressed file
781 * to speed up this operation, we cache the result in the cnode, and do as little as possible
782 * in the case where the cnode already has a valid cached state
789 struct vnode_attr va_fetch
;
790 decmpfs_header
*hdr
= NULL
;
792 int cnode_locked
= 0;
793 int saveInvalid
= 0; // save the header data even though the type was out of range
794 uint64_t decompression_flags
= 0;
795 bool is_mounted
, is_local_fs
;
797 if (vnode_isnamedstream(vp
)) {
799 * named streams can't be compressed
800 * since named streams of the same file share the same cnode,
801 * we don't want to get/set the state in the cnode, just return 0
806 /* examine the cached a state in this cnode */
807 cmp_state
= decmpfs_cnode_get_vnode_state(cp
);
809 case FILE_IS_NOT_COMPRESSED
:
811 case FILE_IS_COMPRESSED
:
813 case FILE_IS_CONVERTING
:
814 /* treat the file as compressed, because this gives us a way to block future reads until decompression is done */
816 case FILE_TYPE_UNKNOWN
:
817 /* the first time we encountered this vnode, so we need to check it out */
820 /* unknown state, assume file is not compressed */
821 ErrorLogWithPath("unknown cmp_state %d\n", cmp_state
);
827 mp
= vnode_mount(vp
);
832 is_local_fs
= ((mp
->mnt_flag
& MNT_LOCAL
));
835 * Trace the following parameters on entry with event-id 0x03120014.
837 * @vp->v_id: vnode-id of the file being queried.
838 * @is_mounted: set to true if @vp belongs to a mounted fs.
839 * @is_local_fs: set to true if @vp belongs to local fs.
841 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FILE_IS_COMPRESSED
, vp
->v_id
,
842 is_mounted
, is_local_fs
);
846 * this should only be true before we mount the root filesystem
847 * we short-cut this return to avoid the call to getattr below, which
848 * will fail before root is mounted
850 ret
= FILE_IS_NOT_COMPRESSED
;
855 /* compression only supported on local filesystems */
856 ret
= FILE_IS_NOT_COMPRESSED
;
860 /* lock our cnode data so that another caller doesn't change the state under us */
861 decmpfs_lock_compressed_data(cp
, 1);
864 VATTR_INIT(&va_fetch
);
865 VATTR_WANTED(&va_fetch
, va_flags
);
866 error
= vnode_getattr(vp
, &va_fetch
, decmpfs_ctx
);
868 /* failed to get the bsd flags so the file is not compressed */
869 ret
= FILE_IS_NOT_COMPRESSED
;
872 if (va_fetch
.va_flags
& UF_COMPRESSED
) {
873 /* UF_COMPRESSED is on, make sure the file has the DECMPFS_XATTR_NAME xattr */
874 error
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 1);
875 if ((hdr
!= NULL
) && (error
== ERANGE
)) {
879 /* failed to get the xattr so the file is not compressed */
880 ret
= FILE_IS_NOT_COMPRESSED
;
884 * We got the xattr, so the file is at least tagged compressed.
885 * For DATALESS, regular files and directories can be "compressed".
886 * For all other types, only files are allowed.
888 if (!vnode_isreg(vp
) &&
889 !(decmpfs_type_is_dataless(hdr
->compression_type
) && vnode_isdir(vp
))) {
890 ret
= FILE_IS_NOT_COMPRESSED
;
893 ret
= FILE_IS_COMPRESSED
;
896 /* UF_COMPRESSED isn't on, so the file isn't compressed */
897 ret
= FILE_IS_NOT_COMPRESSED
;
900 if (((ret
== FILE_IS_COMPRESSED
) || saveInvalid
) && hdr
) {
902 * cache the uncompressed size away in the cnode
907 * we should never get here since the only place ret is set to FILE_IS_COMPRESSED
908 * is after the call to decmpfs_lock_compressed_data above
910 decmpfs_lock_compressed_data(cp
, 1);
914 if (vnode_isdir(vp
)) {
915 decmpfs_cnode_set_vnode_cached_size(cp
, 64);
916 decmpfs_cnode_set_vnode_cached_nchildren(cp
, decmpfs_get_directory_entries(hdr
));
917 if (hdr
->compression_type
== DATALESS_PKG_CMPFS_TYPE
) {
918 decmpfs_cnode_set_vnode_cached_total_size(cp
, DECMPFS_PKG_SIZE(hdr
->_size
));
921 decmpfs_cnode_set_vnode_cached_size(cp
, hdr
->uncompressed_size
);
923 decmpfs_cnode_set_vnode_state(cp
, ret
, 1);
924 decmpfs_cnode_set_vnode_cmp_type(cp
, hdr
->compression_type
, 1);
925 /* remember if the xattr's size was equal to the minimal xattr */
926 if (hdr
->attr_size
== sizeof(decmpfs_disk_header
)) {
927 decmpfs_cnode_set_vnode_minimal_xattr(cp
, 1, 1);
929 if (ret
== FILE_IS_COMPRESSED
) {
930 /* update the ubc's size for this file */
931 ubc_setsize(vp
, hdr
->uncompressed_size
);
933 /* update the decompression flags in the decmpfs cnode */
934 lck_rw_lock_shared(decompressorsLock
);
935 decmpfs_get_decompression_flags_func get_flags
= decmp_get_func(vp
, hdr
->compression_type
, get_flags
);
937 decompression_flags
= get_flags(vp
, decmpfs_ctx
, hdr
);
939 lck_rw_unlock_shared(decompressorsLock
);
940 decmpfs_cnode_set_decompression_flags(cp
, decompression_flags
);
943 /* we might have already taken the lock above; if so, skip taking it again by passing cnode_locked as the skiplock parameter */
944 decmpfs_cnode_set_vnode_state(cp
, ret
, cnode_locked
);
948 decmpfs_unlock_compressed_data(cp
, 1);
955 * Trace the following parameters on return with event-id 0x03120014.
957 * @vp->v_id: vnode-id of the file being queried.
958 * @return: set to 1 is file is compressed.
961 case FILE_IS_NOT_COMPRESSED
:
962 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED
, vp
->v_id
, 0);
964 case FILE_IS_COMPRESSED
:
965 case FILE_IS_CONVERTING
:
966 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED
, vp
->v_id
, 1);
969 /* unknown state, assume file is not compressed */
970 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED
, vp
->v_id
, 0);
971 ErrorLogWithPath("unknown ret %d\n", ret
);
977 decmpfs_update_attributes(vnode_t vp
, struct vnode_attr
*vap
)
981 if (VATTR_IS_ACTIVE(vap
, va_flags
)) {
982 /* the BSD flags are being updated */
983 if (vap
->va_flags
& UF_COMPRESSED
) {
984 /* the compressed bit is being set, did it change? */
985 struct vnode_attr va_fetch
;
987 VATTR_INIT(&va_fetch
);
988 VATTR_WANTED(&va_fetch
, va_flags
);
989 error
= vnode_getattr(vp
, &va_fetch
, decmpfs_ctx
);
994 old_flags
= va_fetch
.va_flags
;
996 if (!(old_flags
& UF_COMPRESSED
)) {
998 * Compression bit was turned on, make sure the file has the DECMPFS_XATTR_NAME attribute.
999 * This precludes anyone from using the UF_COMPRESSED bit for anything else, and it enforces
1000 * an order of operation -- you must first do the setxattr and then the chflags.
1003 if (VATTR_IS_ACTIVE(vap
, va_data_size
)) {
1005 * don't allow the caller to set the BSD flag and the size in the same call
1006 * since this doesn't really make sense
1008 vap
->va_flags
&= ~UF_COMPRESSED
;
1012 decmpfs_header
*hdr
= NULL
;
1013 error
= decmpfs_fetch_compressed_header(vp
, NULL
, &hdr
, 1);
1016 * Allow the flag to be set since the decmpfs attribute
1019 * If we're creating a dataless file we do not want to
1020 * truncate it to zero which allows the file resolver to
1021 * have more control over when truncation should happen.
1022 * All other types of compressed files are truncated to
1025 if (!decmpfs_type_is_dataless(hdr
->compression_type
)) {
1026 VATTR_SET_ACTIVE(vap
, va_data_size
);
1027 vap
->va_data_size
= 0;
1029 } else if (error
== ERANGE
) {
1030 /* the file had a decmpfs attribute but the type was out of range, so don't muck with the file's data size */
1032 /* no DECMPFS_XATTR_NAME attribute, so deny the update */
1033 vap
->va_flags
&= ~UF_COMPRESSED
;
1046 wait_for_decompress(decmpfs_cnode
*cp
)
1049 lck_mtx_lock(decompress_channel_mtx
);
1051 state
= decmpfs_fast_get_state(cp
);
1052 if (state
!= FILE_IS_CONVERTING
) {
1053 /* file is not decompressing */
1054 lck_mtx_unlock(decompress_channel_mtx
);
1057 msleep((caddr_t
)&decompress_channel
, decompress_channel_mtx
, PINOD
, "wait_for_decompress", NULL
);
1061 #pragma mark --- decmpfs hide query routines ---
1064 decmpfs_hides_rsrc(vfs_context_t ctx
, decmpfs_cnode
*cp
)
1068 * callers may (and do) pass NULL for ctx, so we should only use it
1069 * for this equality comparison
1071 * This routine should only be called after a file has already been through decmpfs_file_is_compressed
1074 if (ctx
== decmpfs_ctx
) {
1078 if (!decmpfs_fast_file_is_compressed(cp
)) {
1082 /* all compressed files hide their resource fork */
1087 decmpfs_hides_xattr(vfs_context_t ctx
, decmpfs_cnode
*cp
, const char *xattr
)
1091 * callers may (and do) pass NULL for ctx, so we should only use it
1092 * for this equality comparison
1094 * This routine should only be called after a file has already been through decmpfs_file_is_compressed
1097 if (ctx
== decmpfs_ctx
) {
1100 if (strncmp(xattr
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
) - 1) == 0) {
1101 return decmpfs_hides_rsrc(ctx
, cp
);
1103 if (!decmpfs_fast_file_is_compressed(cp
)) {
1104 /* file is not compressed, so don't hide this xattr */
1107 if (strncmp(xattr
, DECMPFS_XATTR_NAME
, sizeof(DECMPFS_XATTR_NAME
) - 1) == 0) {
1108 /* it's our xattr, so hide it */
1111 /* don't hide this xattr */
1115 #pragma mark --- registration/validation routines ---
1118 registration_valid(const decmpfs_registration
*registration
)
1120 return registration
&& ((registration
->decmpfs_registration
== DECMPFS_REGISTRATION_VERSION_V1
) || (registration
->decmpfs_registration
== DECMPFS_REGISTRATION_VERSION_V3
));
1124 register_decmpfs_decompressor(uint32_t compression_type
, const decmpfs_registration
*registration
)
1126 /* called by kexts to register decompressors */
1130 char resourceName
[80];
1132 if ((compression_type
>= CMP_MAX
) || !registration_valid(registration
)) {
1137 lck_rw_lock_exclusive(decompressorsLock
); locked
= 1;
1139 /* make sure the registration for this type is zero */
1140 if (decompressors
[compression_type
] != NULL
) {
1144 decompressors
[compression_type
] = registration
;
1145 snprintf(resourceName
, sizeof(resourceName
), "com.apple.AppleFSCompression.Type%u", compression_type
);
1146 IOServicePublishResource(resourceName
, TRUE
);
1150 lck_rw_unlock_exclusive(decompressorsLock
);
1156 unregister_decmpfs_decompressor(uint32_t compression_type
, decmpfs_registration
*registration
)
1158 /* called by kexts to unregister decompressors */
1162 char resourceName
[80];
1164 if ((compression_type
>= CMP_MAX
) || !registration_valid(registration
)) {
1169 lck_rw_lock_exclusive(decompressorsLock
); locked
= 1;
1170 if (decompressors
[compression_type
] != registration
) {
1174 decompressors
[compression_type
] = NULL
;
1175 snprintf(resourceName
, sizeof(resourceName
), "com.apple.AppleFSCompression.Type%u", compression_type
);
1176 IOServicePublishResource(resourceName
, FALSE
);
1180 lck_rw_unlock_exclusive(decompressorsLock
);
1186 compression_type_valid(vnode_t vp
, decmpfs_header
*hdr
)
1188 /* fast pre-check to determine if the given compressor has checked in */
1191 /* every compressor must have at least a fetch function */
1192 lck_rw_lock_shared(decompressorsLock
);
1193 if (decmp_get_func(vp
, hdr
->compression_type
, fetch
) != NULL
) {
1196 lck_rw_unlock_shared(decompressorsLock
);
1201 #pragma mark --- compression/decompression routines ---
1204 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
)
1206 /* get the uncompressed bytes for the specified region of vp by calling out to the registered compressor */
1212 if ((uint64_t)offset
>= hdr
->uncompressed_size
) {
1213 /* reading past end of file; nothing to do */
1218 /* tried to read from before start of file */
1222 if ((uint64_t)(offset
+ size
) > hdr
->uncompressed_size
) {
1223 /* adjust size so we don't read past the end of the file */
1224 size
= hdr
->uncompressed_size
- offset
;
1227 /* nothing to read */
1233 * Trace the following parameters on entry with event-id 0x03120008.
1235 * @vp->v_id: vnode-id of the file being decompressed.
1236 * @hdr->compression_type: compression type.
1237 * @offset: offset from where to fetch uncompressed data.
1238 * @size: amount of uncompressed data to fetch.
1240 * Please NOTE: @offset and @size can overflow in theory but
1243 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FETCH_UNCOMPRESSED_DATA
, vp
->v_id
,
1244 hdr
->compression_type
, (int)offset
, (int)size
);
1245 lck_rw_lock_shared(decompressorsLock
);
1246 decmpfs_fetch_uncompressed_data_func fetch
= decmp_get_func(vp
, hdr
->compression_type
, fetch
);
1248 err
= fetch(vp
, decmpfs_ctx
, hdr
, offset
, size
, nvec
, vec
, bytes_read
);
1249 lck_rw_unlock_shared(decompressorsLock
);
1251 uint64_t decompression_flags
= decmpfs_cnode_get_decompression_flags(cp
);
1252 if (decompression_flags
& DECMPFS_FLAGS_FORCE_FLUSH_ON_DECOMPRESS
) {
1253 #if !defined(__i386__) && !defined(__x86_64__)
1255 for (i
= 0; i
< nvec
; i
++) {
1256 flush_dcache64((addr64_t
)(uintptr_t)vec
[i
].buf
, vec
[i
].size
, FALSE
);
1263 lck_rw_unlock_shared(decompressorsLock
);
1266 * Trace the following parameters on return with event-id 0x03120008.
1268 * @vp->v_id: vnode-id of the file being decompressed.
1269 * @bytes_read: amount of uncompressed bytes fetched in bytes.
1270 * @err: value returned from this function.
1272 * Please NOTE: @bytes_read can overflow in theory but here it is safe.
1274 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FETCH_UNCOMPRESSED_DATA
, vp
->v_id
,
1275 (int)*bytes_read
, err
);
1280 static kern_return_t
1281 commit_upl(upl_t upl
, upl_offset_t pl_offset
, size_t uplSize
, int flags
, int abort
)
1283 kern_return_t kr
= 0;
1286 upl_unmark_decmp(upl
);
1287 #endif /* CONFIG_IOSCHED */
1289 /* commit the upl pages */
1291 VerboseLog("aborting upl, flags 0x%08x\n", flags
);
1292 kr
= ubc_upl_abort_range(upl
, pl_offset
, uplSize
, flags
);
1293 if (kr
!= KERN_SUCCESS
) {
1294 ErrorLog("ubc_upl_abort_range error %d\n", (int)kr
);
1297 VerboseLog("committing upl, flags 0x%08x\n", flags
| UPL_COMMIT_CLEAR_DIRTY
);
1298 kr
= ubc_upl_commit_range(upl
, pl_offset
, uplSize
, flags
| UPL_COMMIT_CLEAR_DIRTY
| UPL_COMMIT_WRITTEN_BY_KERNEL
);
1299 if (kr
!= KERN_SUCCESS
) {
1300 ErrorLog("ubc_upl_commit_range error %d\n", (int)kr
);
1308 decmpfs_pagein_compressed(struct vnop_pagein_args
*ap
, int *is_compressed
, decmpfs_cnode
*cp
)
1310 /* handles a page-in request from vfs for a compressed file */
1313 vnode_t vp
= ap
->a_vp
;
1314 upl_t pl
= ap
->a_pl
;
1315 upl_offset_t pl_offset
= ap
->a_pl_offset
;
1316 off_t f_offset
= ap
->a_f_offset
;
1317 size_t size
= ap
->a_size
;
1318 int flags
= ap
->a_flags
;
1320 user_ssize_t uplSize
= 0;
1322 decmpfs_header
*hdr
= NULL
;
1323 uint64_t cachedSize
= 0;
1324 int cmpdata_locked
= 0;
1326 if (!decmpfs_trylock_compressed_data(cp
, 0)) {
1332 if (flags
& ~(UPL_IOSYNC
| UPL_NOCOMMIT
| UPL_NORDAHEAD
)) {
1333 DebugLogWithPath("pagein: unknown flags 0x%08x\n", (flags
& ~(UPL_IOSYNC
| UPL_NOCOMMIT
| UPL_NORDAHEAD
)));
1336 err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0);
1341 cachedSize
= hdr
->uncompressed_size
;
1343 if (!compression_type_valid(vp
, hdr
)) {
1344 /* compressor not registered */
1350 /* Mark the UPL as the requesting UPL for decompression */
1352 #endif /* CONFIG_IOSCHED */
1354 /* map the upl so we can fetch into it */
1355 kern_return_t kr
= ubc_upl_map(pl
, (vm_offset_t
*)&data
);
1356 if ((kr
!= KERN_SUCCESS
) || (data
== NULL
)) {
1360 upl_unmark_decmp(pl
);
1361 #endif /* CONFIG_IOSCHED */
1368 /* clip the size to the size of the file */
1369 if ((uint64_t)uplPos
+ uplSize
> cachedSize
) {
1370 /* truncate the read to the size of the file */
1371 uplSize
= cachedSize
- uplPos
;
1378 /* 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 */
1379 vec
.buf
= (char*)data
+ pl_offset
;
1382 uint64_t did_read
= 0;
1383 if (decmpfs_fast_get_state(cp
) == FILE_IS_CONVERTING
) {
1384 ErrorLogWithPath("unexpected pagein during decompress\n");
1386 * if the file is converting, this must be a recursive call to pagein from underneath a call to decmpfs_decompress_file;
1387 * pretend that it succeeded but don't do anything since we're just going to write over the pages anyway
1392 err
= decmpfs_fetch_uncompressed_data(vp
, cp
, hdr
, uplPos
, uplSize
, 1, &vec
, &did_read
);
1395 DebugLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err
);
1396 int cmp_state
= decmpfs_fast_get_state(cp
);
1397 if (cmp_state
== FILE_IS_CONVERTING
) {
1398 DebugLogWithPath("cmp_state == FILE_IS_CONVERTING\n");
1399 cmp_state
= wait_for_decompress(cp
);
1400 if (cmp_state
== FILE_IS_COMPRESSED
) {
1401 DebugLogWithPath("cmp_state == FILE_IS_COMPRESSED\n");
1402 /* a decompress was attempted but it failed, let's try calling fetch again */
1406 if (cmp_state
== FILE_IS_NOT_COMPRESSED
) {
1407 DebugLogWithPath("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1408 /* the file was decompressed after we started reading it */
1409 *is_compressed
= 0; /* instruct caller to fall back to its normal path */
1413 /* zero out whatever we didn't read, and zero out the end of the last page(s) */
1414 uint64_t total_size
= (size
+ (PAGE_SIZE
- 1)) & ~(PAGE_SIZE
- 1);
1415 if (did_read
< total_size
) {
1416 memset((char*)vec
.buf
+ did_read
, 0, total_size
- did_read
);
1420 upl_unmark_decmp(pl
);
1421 #endif /* CONFIG_IOSCHED */
1423 kr
= ubc_upl_unmap(pl
); data
= NULL
; /* make sure to set data to NULL so we don't try to unmap again below */
1424 if (kr
!= KERN_SUCCESS
) {
1425 ErrorLogWithPath("ubc_upl_unmap error %d\n", (int)kr
);
1428 /* commit our pages */
1429 kr
= commit_upl(pl
, pl_offset
, total_size
, UPL_COMMIT_FREE_ON_EMPTY
, 0);
1440 if (cmpdata_locked
) {
1441 decmpfs_unlock_compressed_data(cp
, 0);
1445 if (err
!= ENXIO
&& err
!= ENOSPC
) {
1447 MALLOC(path
, char *, PATH_MAX
, M_TEMP
, M_WAITOK
);
1448 panic("%s: decmpfs_pagein_compressed: err %d", vnpath(vp
, path
, PATH_MAX
), err
);
1452 ErrorLogWithPath("err %d\n", err
);
1458 decmpfs_read_compressed(struct vnop_read_args
*ap
, int *is_compressed
, decmpfs_cnode
*cp
)
1460 /* handles a read request from vfs for a compressed file */
1462 uio_t uio
= ap
->a_uio
;
1463 vnode_t vp
= ap
->a_vp
;
1467 user_ssize_t uplSize
= 0;
1468 user_ssize_t uplRemaining
= 0;
1469 off_t curUplPos
= 0;
1470 user_ssize_t curUplSize
= 0;
1471 kern_return_t kr
= KERN_SUCCESS
;
1474 uint64_t did_read
= 0;
1476 upl_page_info_t
*pli
= NULL
;
1477 decmpfs_header
*hdr
= NULL
;
1478 uint64_t cachedSize
= 0;
1480 user_ssize_t uioRemaining
= 0;
1481 int cmpdata_locked
= 0;
1483 decmpfs_lock_compressed_data(cp
, 0); cmpdata_locked
= 1;
1485 uplPos
= uio_offset(uio
);
1486 uplSize
= uio_resid(uio
);
1487 VerboseLogWithPath("uplPos %lld uplSize %lld\n", uplPos
, uplSize
);
1489 cachedSize
= decmpfs_cnode_get_vnode_cached_size(cp
);
1491 if ((uint64_t)uplPos
+ uplSize
> cachedSize
) {
1492 /* truncate the read to the size of the file */
1493 uplSize
= cachedSize
- uplPos
;
1496 /* give the cluster layer a chance to fill in whatever it already has */
1497 countInt
= (uplSize
> INT_MAX
) ? INT_MAX
: uplSize
;
1498 err
= cluster_copy_ubc_data(vp
, uio
, &countInt
, 0);
1503 /* figure out what's left */
1504 uioPos
= uio_offset(uio
);
1505 uioRemaining
= uio_resid(uio
);
1506 if ((uint64_t)uioPos
+ uioRemaining
> cachedSize
) {
1507 /* truncate the read to the size of the file */
1508 uioRemaining
= cachedSize
- uioPos
;
1511 if (uioRemaining
<= 0) {
1516 err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0);
1520 if (!compression_type_valid(vp
, hdr
)) {
1526 uplSize
= uioRemaining
;
1527 #if COMPRESSION_DEBUG
1528 DebugLogWithPath("uplPos %lld uplSize %lld\n", (uint64_t)uplPos
, (uint64_t)uplSize
);
1531 lck_rw_lock_shared(decompressorsLock
);
1532 decmpfs_adjust_fetch_region_func adjust_fetch
= decmp_get_func(vp
, hdr
->compression_type
, adjust_fetch
);
1534 /* give the compressor a chance to adjust the portion of the file that we read */
1535 adjust_fetch(vp
, decmpfs_ctx
, hdr
, &uplPos
, &uplSize
);
1536 VerboseLogWithPath("adjusted uplPos %lld uplSize %lld\n", (uint64_t)uplPos
, (uint64_t)uplSize
);
1538 lck_rw_unlock_shared(decompressorsLock
);
1540 /* clip the adjusted size to the size of the file */
1541 if ((uint64_t)uplPos
+ uplSize
> cachedSize
) {
1542 /* truncate the read to the size of the file */
1543 uplSize
= cachedSize
- uplPos
;
1552 * since we're going to create a upl for the given region of the file,
1553 * make sure we're on page boundaries
1556 if (uplPos
& (PAGE_SIZE
- 1)) {
1557 /* round position down to page boundary */
1558 uplSize
+= (uplPos
& (PAGE_SIZE
- 1));
1559 uplPos
&= ~(PAGE_SIZE
- 1);
1561 /* round size up to page multiple */
1562 uplSize
= (uplSize
+ (PAGE_SIZE
- 1)) & ~(PAGE_SIZE
- 1);
1564 VerboseLogWithPath("new uplPos %lld uplSize %lld\n", (uint64_t)uplPos
, (uint64_t)uplSize
);
1566 uplRemaining
= uplSize
;
1570 while (uplRemaining
> 0) {
1571 /* start after the last upl */
1572 curUplPos
+= curUplSize
;
1574 /* clip to max upl size */
1575 curUplSize
= uplRemaining
;
1576 if (curUplSize
> MAX_UPL_SIZE_BYTES
) {
1577 curUplSize
= MAX_UPL_SIZE_BYTES
;
1580 /* create the upl */
1581 kr
= ubc_create_upl_kernel(vp
, curUplPos
, curUplSize
, &upl
, &pli
, UPL_SET_LITE
, VM_KERN_MEMORY_FILE
);
1582 if (kr
!= KERN_SUCCESS
) {
1583 ErrorLogWithPath("ubc_create_upl error %d\n", (int)kr
);
1587 VerboseLogWithPath("curUplPos %lld curUplSize %lld\n", (uint64_t)curUplPos
, (uint64_t)curUplSize
);
1590 /* Mark the UPL as the requesting UPL for decompression */
1591 upl_mark_decmp(upl
);
1592 #endif /* CONFIG_IOSCHED */
1595 kr
= ubc_upl_map(upl
, (vm_offset_t
*)&data
);
1596 if (kr
!= KERN_SUCCESS
) {
1597 commit_upl(upl
, 0, curUplSize
, UPL_ABORT_FREE_ON_EMPTY
, 1);
1600 MALLOC(path
, char *, PATH_MAX
, M_TEMP
, M_WAITOK
);
1601 panic("%s: decmpfs_read_compressed: ubc_upl_map error %d", vnpath(vp
, path
, PATH_MAX
), (int)kr
);
1604 ErrorLogWithPath("ubc_upl_map kr=0x%x\n", (int)kr
);
1610 /* make sure the map succeeded */
1612 commit_upl(upl
, 0, curUplSize
, UPL_ABORT_FREE_ON_EMPTY
, 1);
1614 ErrorLogWithPath("ubc_upl_map mapped null\n");
1619 /* fetch uncompressed data into the mapped upl */
1622 vec
= (decmpfs_vector
){ .buf
= data
, .size
= curUplSize
};
1623 err
= decmpfs_fetch_uncompressed_data(vp
, cp
, hdr
, curUplPos
, curUplSize
, 1, &vec
, &did_read
);
1625 ErrorLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err
);
1627 /* maybe the file is converting to decompressed */
1628 int cmp_state
= decmpfs_fast_get_state(cp
);
1629 if (cmp_state
== FILE_IS_CONVERTING
) {
1630 ErrorLogWithPath("cmp_state == FILE_IS_CONVERTING\n");
1631 cmp_state
= wait_for_decompress(cp
);
1632 if (cmp_state
== FILE_IS_COMPRESSED
) {
1633 ErrorLogWithPath("cmp_state == FILE_IS_COMPRESSED\n");
1634 /* a decompress was attempted but it failed, let's try fetching again */
1638 if (cmp_state
== FILE_IS_NOT_COMPRESSED
) {
1639 ErrorLogWithPath("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1640 /* the file was decompressed after we started reading it */
1641 abort_read
= 1; /* we're not going to commit our data */
1642 *is_compressed
= 0; /* instruct caller to fall back to its normal path */
1647 /* zero out the remainder of the last page */
1648 memset((char*)data
+ did_read
, 0, curUplSize
- did_read
);
1649 kr
= ubc_upl_unmap(upl
);
1650 if (kr
== KERN_SUCCESS
) {
1652 kr
= commit_upl(upl
, 0, curUplSize
, UPL_ABORT_FREE_ON_EMPTY
, 1);
1654 VerboseLogWithPath("uioPos %lld uioRemaining %lld\n", (uint64_t)uioPos
, (uint64_t)uioRemaining
);
1656 off_t uplOff
= uioPos
- curUplPos
;
1658 ErrorLogWithPath("uplOff %lld should never be negative\n", (int64_t)uplOff
);
1661 off_t count
= curUplPos
+ curUplSize
- uioPos
;
1663 /* this upl is entirely before the uio */
1665 if (count
> uioRemaining
) {
1666 count
= uioRemaining
;
1668 int io_resid
= count
;
1669 err
= cluster_copy_upl_data(uio
, upl
, uplOff
, &io_resid
);
1670 int copied
= count
- io_resid
;
1671 VerboseLogWithPath("uplOff %lld count %lld copied %lld\n", (uint64_t)uplOff
, (uint64_t)count
, (uint64_t)copied
);
1673 ErrorLogWithPath("cluster_copy_upl_data err %d\n", err
);
1676 uioRemaining
-= copied
;
1680 kr
= commit_upl(upl
, 0, curUplSize
, UPL_COMMIT_FREE_ON_EMPTY
| UPL_COMMIT_INACTIVATE
, 0);
1686 ErrorLogWithPath("ubc_upl_unmap error %d\n", (int)kr
);
1689 uplRemaining
-= curUplSize
;
1697 if (cmpdata_locked
) {
1698 decmpfs_unlock_compressed_data(cp
, 0);
1700 if (err
) {/* something went wrong */
1701 ErrorLogWithPath("err %d\n", err
);
1705 #if COMPRESSION_DEBUG
1706 uplSize
= uio_resid(uio
);
1708 VerboseLogWithPath("still %lld bytes to copy\n", uplSize
);
1715 decmpfs_free_compressed_data(vnode_t vp
, decmpfs_cnode
*cp
)
1718 * call out to the decompressor to free remove any data associated with this compressed file
1719 * then delete the file's compression xattr
1721 decmpfs_header
*hdr
= NULL
;
1724 * Trace the following parameters on entry with event-id 0x03120010.
1726 * @vp->v_id: vnode-id of the file for which to free compressed data.
1728 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FREE_COMPRESSED_DATA
, vp
->v_id
);
1730 int err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0);
1732 ErrorLogWithPath("decmpfs_fetch_compressed_header err %d\n", err
);
1734 lck_rw_lock_shared(decompressorsLock
);
1735 decmpfs_free_compressed_data_func free_data
= decmp_get_func(vp
, hdr
->compression_type
, free_data
);
1737 err
= free_data(vp
, decmpfs_ctx
, hdr
);
1739 /* nothing to do, so no error */
1742 lck_rw_unlock_shared(decompressorsLock
);
1745 ErrorLogWithPath("decompressor err %d\n", err
);
1749 * Trace the following parameters on return with event-id 0x03120010.
1751 * @vp->v_id: vnode-id of the file for which to free compressed data.
1752 * @err: value returned from this function.
1754 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FREE_COMPRESSED_DATA
, vp
->v_id
, err
);
1756 /* delete the xattr */
1757 err
= vn_removexattr(vp
, DECMPFS_XATTR_NAME
, 0, decmpfs_ctx
);
1769 #pragma mark --- file conversion routines ---
1772 unset_compressed_flag(vnode_t vp
)
1775 struct vnode_attr va
;
1776 int new_bsdflags
= 0;
1779 VATTR_WANTED(&va
, va_flags
);
1780 err
= vnode_getattr(vp
, &va
, decmpfs_ctx
);
1783 ErrorLogWithPath("vnode_getattr err %d\n", err
);
1785 new_bsdflags
= va
.va_flags
& ~UF_COMPRESSED
;
1788 VATTR_SET(&va
, va_flags
, new_bsdflags
);
1789 err
= vnode_setattr(vp
, &va
, decmpfs_ctx
);
1791 ErrorLogWithPath("vnode_setattr err %d\n", err
);
1798 decmpfs_decompress_file(vnode_t vp
, decmpfs_cnode
*cp
, off_t toSize
, int truncate_okay
, int skiplock
)
1800 /* convert a compressed file to an uncompressed file */
1806 uint32_t old_state
= 0;
1807 uint32_t new_state
= 0;
1808 int update_file_state
= 0;
1810 decmpfs_header
*hdr
= NULL
;
1811 int cmpdata_locked
= 0;
1812 off_t remaining
= 0;
1813 uint64_t uncompressed_size
= 0;
1816 * Trace the following parameters on entry with event-id 0x03120000.
1818 * @vp->v_id: vnode-id of the file being decompressed.
1819 * @toSize: uncompress given bytes of the file.
1820 * @truncate_okay: on error it is OK to truncate.
1821 * @skiplock: compressed data is locked, skip locking again.
1823 * Please NOTE: @toSize can overflow in theory but here it is safe.
1825 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_DECOMPRESS_FILE
, vp
->v_id
,
1826 (int)toSize
, truncate_okay
, skiplock
);
1829 decmpfs_lock_compressed_data(cp
, 1); cmpdata_locked
= 1;
1833 old_state
= decmpfs_fast_get_state(cp
);
1835 switch (old_state
) {
1836 case FILE_IS_NOT_COMPRESSED
:
1838 /* someone else decompressed the file */
1843 case FILE_TYPE_UNKNOWN
:
1845 /* the file is in an unknown state, so update the state and retry */
1846 (void)decmpfs_file_is_compressed(vp
, cp
);
1852 case FILE_IS_COMPRESSED
:
1854 /* the file is compressed, so decompress it */
1861 * this shouldn't happen since multiple calls to decmpfs_decompress_file lock each other out,
1862 * and when decmpfs_decompress_file returns, the state should be always be set back to
1863 * FILE_IS_NOT_COMPRESSED or FILE_IS_UNKNOWN
1870 err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0);
1875 uncompressed_size
= hdr
->uncompressed_size
;
1877 toSize
= hdr
->uncompressed_size
;
1881 /* special case truncating the file to zero bytes */
1883 } else if ((uint64_t)toSize
> hdr
->uncompressed_size
) {
1884 /* the caller is trying to grow the file, so we should decompress all the data */
1885 toSize
= hdr
->uncompressed_size
;
1888 allocSize
= MIN(64 * 1024, toSize
);
1889 MALLOC(data
, char *, allocSize
, M_TEMP
, M_WAITOK
);
1895 uio_w
= uio_create(1, 0LL, UIO_SYSSPACE
, UIO_WRITE
);
1900 uio_w
->uio_flags
|= UIO_FLAGS_IS_COMPRESSED_FILE
;
1904 /* tell the buffer cache that this is an empty file */
1907 /* if we got here, we need to decompress the file */
1908 decmpfs_cnode_set_vnode_state(cp
, FILE_IS_CONVERTING
, 1);
1910 while (remaining
> 0) {
1911 /* loop decompressing data from the file and writing it into the data fork */
1913 uint64_t bytes_read
= 0;
1914 decmpfs_vector vec
= { .buf
= data
, .size
= MIN(allocSize
, remaining
) };
1915 err
= decmpfs_fetch_uncompressed_data(vp
, cp
, hdr
, offset
, vec
.size
, 1, &vec
, &bytes_read
);
1917 ErrorLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err
);
1921 if (bytes_read
== 0) {
1922 /* we're done reading data */
1926 uio_reset(uio_w
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
1927 err
= uio_addiov(uio_w
, CAST_USER_ADDR_T(data
), bytes_read
);
1929 ErrorLogWithPath("uio_addiov err %d\n", err
);
1934 err
= VNOP_WRITE(vp
, uio_w
, 0, decmpfs_ctx
);
1936 /* if the write failed, truncate the file to zero bytes */
1937 ErrorLogWithPath("VNOP_WRITE err %d\n", err
);
1940 offset
+= bytes_read
;
1941 remaining
-= bytes_read
;
1945 if (offset
!= toSize
) {
1946 ErrorLogWithPath("file decompressed to %lld instead of %lld\n", offset
, toSize
);
1953 /* sync the data and metadata */
1954 err
= VNOP_FSYNC(vp
, MNT_WAIT
, decmpfs_ctx
);
1956 ErrorLogWithPath("VNOP_FSYNC err %d\n", err
);
1962 /* write, setattr, or fsync failed */
1963 ErrorLogWithPath("aborting decompress, err %d\n", err
);
1964 if (truncate_okay
) {
1965 /* truncate anything we might have written */
1966 int error
= vnode_setsize(vp
, 0, 0, decmpfs_ctx
);
1967 ErrorLogWithPath("vnode_setsize err %d\n", error
);
1973 /* if we're truncating the file to zero bytes, we'll skip ahead to here */
1975 /* unset the compressed flag */
1976 unset_compressed_flag(vp
);
1978 /* free the compressed data associated with this file */
1979 err
= decmpfs_free_compressed_data(vp
, cp
);
1981 ErrorLogWithPath("decmpfs_free_compressed_data err %d\n", err
);
1985 * even if free_compressed_data or vnode_getattr/vnode_setattr failed, return success
1986 * since we succeeded in writing all of the file data to the data fork
1990 /* if we got this far, the file was successfully decompressed */
1991 update_file_state
= 1;
1992 new_state
= FILE_IS_NOT_COMPRESSED
;
1994 #if COMPRESSION_DEBUG
1996 uint64_t filesize
= 0;
1997 vnsize(vp
, &filesize
);
1998 DebugLogWithPath("new file size %lld\n", filesize
);
2014 /* if there was a failure, reset compression flags to unknown and clear the buffer cache data */
2015 update_file_state
= 1;
2016 new_state
= FILE_TYPE_UNKNOWN
;
2017 if (uncompressed_size
) {
2019 ubc_setsize(vp
, uncompressed_size
);
2023 if (update_file_state
) {
2024 lck_mtx_lock(decompress_channel_mtx
);
2025 decmpfs_cnode_set_vnode_state(cp
, new_state
, 1);
2026 wakeup((caddr_t
)&decompress_channel
); /* wake up anyone who might have been waiting for decompression */
2027 lck_mtx_unlock(decompress_channel_mtx
);
2030 if (cmpdata_locked
) {
2031 decmpfs_unlock_compressed_data(cp
, 1);
2034 * Trace the following parameters on return with event-id 0x03120000.
2036 * @vp->v_id: vnode-id of the file being decompressed.
2037 * @err: value returned from this function.
2039 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_DECOMPRESS_FILE
, vp
->v_id
, err
);
2043 #pragma mark --- Type1 compressor ---
2046 * The "Type1" compressor stores the data fork directly in the compression xattr
2050 decmpfs_validate_compressed_file_Type1(__unused vnode_t vp
, __unused vfs_context_t ctx
, decmpfs_header
*hdr
)
2054 if (hdr
->uncompressed_size
+ sizeof(decmpfs_disk_header
) != (uint64_t)hdr
->attr_size
) {
2063 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
)
2067 user_ssize_t remaining
;
2069 if (hdr
->uncompressed_size
+ sizeof(decmpfs_disk_header
) != (uint64_t)hdr
->attr_size
) {
2074 #if COMPRESSION_DEBUG
2075 static int dummy
= 0; // prevent syslog from coalescing printfs
2076 DebugLogWithPath("%d memcpy %lld at %lld\n", dummy
++, size
, (uint64_t)offset
);
2080 for (i
= 0; (i
< nvec
) && (remaining
> 0); i
++) {
2081 user_ssize_t curCopy
= vec
[i
].size
;
2082 if (curCopy
> remaining
) {
2083 curCopy
= remaining
;
2085 memcpy(vec
[i
].buf
, hdr
->attr_bytes
+ offset
, curCopy
);
2087 remaining
-= curCopy
;
2090 if ((bytes_read
) && (err
== 0)) {
2091 *bytes_read
= (size
- remaining
);
2098 SECURITY_READ_ONLY_EARLY(static decmpfs_registration
) Type1Reg
=
2100 .decmpfs_registration
= DECMPFS_REGISTRATION_VERSION
,
2101 .validate
= decmpfs_validate_compressed_file_Type1
,
2102 .adjust_fetch
= NULL
,/* no adjust necessary */
2103 .fetch
= decmpfs_fetch_uncompressed_data_Type1
,
2104 .free_data
= NULL
,/* no free necessary */
2105 .get_flags
= NULL
/* no flags */
2108 #pragma mark --- decmpfs initialization ---
2113 static int done
= 0;
2118 decmpfs_ctx
= vfs_context_create(vfs_context_kernel());
2120 lck_grp_attr_t
*attr
= lck_grp_attr_alloc_init();
2121 decmpfs_lockgrp
= lck_grp_alloc_init("VFSCOMP", attr
);
2122 lck_grp_attr_free(attr
);
2123 decompressorsLock
= lck_rw_alloc_init(decmpfs_lockgrp
, NULL
);
2124 decompress_channel_mtx
= lck_mtx_alloc_init(decmpfs_lockgrp
, NULL
);
2126 register_decmpfs_decompressor(CMP_Type1
, &Type1Reg
);
2130 #endif /* FS_COMPRESSION */