2 * Copyright (c) 2008 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 /* we need these symbols even though compression is turned off */
30 char register_decmpfs_decompressor
;
31 char unregister_decmpfs_decompressor
;
32 #else /* HFS_COMPRESSION */
33 #include <sys/kernel.h>
34 #include <sys/vnode_internal.h>
35 #include <sys/file_internal.h>
37 #include <sys/fcntl.h>
38 #include <sys/xattr.h>
39 #include <sys/namei.h>
41 #include <sys/mount_internal.h>
43 #include <sys/decmpfs.h>
44 #include <sys/uio_internal.h>
45 #include <libkern/OSByteOrder.h>
47 #pragma mark --- debugging ---
49 #define COMPRESSION_DEBUG 0
50 #define COMPRESSION_DEBUG_VERBOSE 0
51 #define MALLOC_DEBUG 0
54 baseName(const char *path
)
58 const char *ret
= path
;
60 for (i
= 0; path
[i
] != 0; i
++) {
67 #define ErrorLog(x, args...) printf("%s:%d:%s: " x, baseName(__FILE__), __LINE__, __FUNCTION__, ## args)
70 #define DebugLog ErrorLog
72 #define DebugLog(x...) do { } while(0)
75 #if COMPRESSION_DEBUG_VERBOSE
76 #define VerboseLog ErrorLog
78 #define VerboseLog(x...) do { } while(0)
83 static SInt32 totalAlloc
;
93 _malloc(uint32_t sz
, __unused
int type
, __unused
int flags
, const char *file
, int line
)
95 uint32_t allocSz
= sz
+ 2 * sizeof(allocated
);
97 allocated
*alloc
= NULL
;
98 MALLOC(alloc
, allocated
*, allocSz
, type
, flags
);
100 ErrorLog("malloc failed\n");
104 char *ret
= (char*)&alloc
[1];
105 allocated
*alloc2
= (allocated
*)(ret
+ sz
);
107 alloc
->allocSz
= allocSz
;
108 alloc
->magic
= 0xdadadada;
114 int s
= OSAddAtomic(sz
, &totalAlloc
);
115 ErrorLog("malloc(%d) -> %p, total allocations %d\n", sz
, ret
, s
+ sz
);
121 _free(char *ret
, __unused
int type
, const char *file
, int line
)
124 ErrorLog("freeing null\n");
127 allocated
*alloc
= (allocated
*)ret
;
129 uint32_t sz
= alloc
->allocSz
- 2 * sizeof(allocated
);
130 allocated
*alloc2
= (allocated
*)(ret
+ sz
);
132 if (alloc
->magic
!= 0xdadadada) {
133 panic("freeing bad pointer");
136 if (memcmp(alloc
, alloc2
, sizeof(*alloc
)) != 0) {
137 panic("clobbered data");
140 memset(ret
, 0xce, sz
);
144 int s
= OSAddAtomic(-sz
, &totalAlloc
);
145 ErrorLog("free(%p,%d) -> total allocations %d\n", ret
, sz
, s
- sz
);
150 #define MALLOC(space, cast, size, type, flags) (space) = (cast)_malloc(size, type, flags, __FILE__, __LINE__)
151 #define FREE(addr, type) _free((void *)addr, type, __FILE__, __LINE__)
153 #endif /* MALLOC_DEBUG */
155 #pragma mark --- globals ---
157 static lck_grp_t
*decmpfs_lockgrp
;
159 static decmpfs_registration
* decompressors
[CMP_MAX
]; /* the registered compressors */
160 static lck_rw_t
* decompressorsLock
;
161 static int decompress_channel
; /* channel used by decompress_file to wake up waiters */
162 static lck_mtx_t
*decompress_channel_mtx
;
164 vfs_context_t decmpfs_ctx
;
166 #pragma mark --- decmp_get_func ---
168 #define offsetof_func(func) ((uintptr_t)(&(((decmpfs_registration*)NULL)->func)))
171 _func_from_offset(uint32_t type
, int offset
)
173 /* get the function at the given offset in the registration for the given type */
174 decmpfs_registration
*reg
= decompressors
[type
];
175 char *regChar
= (char*)reg
;
176 char *func
= ®Char
[offset
];
177 void **funcPtr
= (void**)func
;
181 extern void IOServicePublishResource( const char * property
, boolean_t value
);
182 extern boolean_t
IOServiceWaitForMatchingResource( const char * property
, uint64_t timeout
);
183 extern boolean_t
IOCatalogueMatchingDriversPresent( const char * property
);
186 _decmp_get_func(uint32_t type
, int offset
)
189 this function should be called while holding a shared lock to decompressorsLock,
190 and will return with the lock held
196 if (decompressors
[type
] != NULL
) {
197 // the compressor has already registered but the function might be null
198 return _func_from_offset(type
, offset
);
201 // does IOKit know about a kext that is supposed to provide this type?
202 char providesName
[80];
203 snprintf(providesName
, sizeof(providesName
), "com.apple.AppleFSCompression.providesType%u", type
);
204 if (IOCatalogueMatchingDriversPresent(providesName
)) {
205 // there is a kext that says it will register for this type, so let's wait for it
206 char resourceName
[80];
207 uint64_t delay
= 10000000ULL; // 10 milliseconds.
208 snprintf(resourceName
, sizeof(resourceName
), "com.apple.AppleFSCompression.Type%u", type
);
209 printf("waiting for %s\n", resourceName
);
210 while(decompressors
[type
] == NULL
) {
211 lck_rw_done(decompressorsLock
); // we have to unlock to allow the kext to register
212 if (IOServiceWaitForMatchingResource(resourceName
, delay
)) {
215 if (!IOCatalogueMatchingDriversPresent(providesName
)) {
217 printf("the kext with %s is no longer present\n", providesName
);
220 printf("still waiting for %s\n", resourceName
);
222 lck_rw_lock_shared(decompressorsLock
);
224 // IOKit says the kext is loaded, so it should be registered too!
225 if (decompressors
[type
] == NULL
) {
226 ErrorLog("we found %s, but the type still isn't registered\n", providesName
);
229 // it's now registered, so let's return the function
230 return _func_from_offset(type
, offset
);
233 // the compressor hasn't registered, so it never will unless someone manually kextloads it
234 ErrorLog("tried to access a compressed file of unregistered type %d\n", type
);
238 #define decmp_get_func(type, func) ((typeof(((decmpfs_registration*)NULL)->func))_decmp_get_func(type, offsetof_func(func)))
240 #pragma mark --- utilities ---
242 #if COMPRESSION_DEBUG
244 vnpath(vnode_t vp
, char *path
, int len
)
248 vn_getpath(vp
, path
, &len
);
249 path
[origlen
- 1] = 0;
254 vnsize(vnode_t vp
, uint64_t *size
)
256 struct vnode_attr va
;
258 VATTR_WANTED(&va
, va_data_size
);
259 int error
= vnode_getattr(vp
, &va
, decmpfs_ctx
);
261 ErrorLog("vnode_getattr err %d\n", error
);
264 *size
= va
.va_data_size
;
267 #endif /* COMPRESSION_DEBUG */
269 #pragma mark --- cnode routines ---
272 decmpfs_cnode_init(decmpfs_cnode
*cp
)
274 memset(cp
, 0, sizeof(*cp
));
275 lck_rw_init(&cp
->compressed_data_lock
, decmpfs_lockgrp
, NULL
);
276 #if !DECMPFS_SUPPORTS_SWAP64
277 lck_mtx_init(&cp
->uncompressed_size_mtx
, decmpfs_lockgrp
, NULL
);
282 decmpfs_cnode_destroy(decmpfs_cnode
*cp
)
284 lck_rw_destroy(&cp
->compressed_data_lock
, decmpfs_lockgrp
);
285 #if !DECMPFS_SUPPORTS_SWAP64
286 lck_mtx_destroy(&cp
->uncompressed_size_mtx
, decmpfs_lockgrp
);
291 decmpfs_trylock_compressed_data(decmpfs_cnode
*cp
, int exclusive
)
293 void *thread
= current_thread();
294 boolean_t retval
= FALSE
;
296 if (cp
->lockowner
== thread
) {
297 /* this thread is already holding an exclusive lock, so bump the count */
300 } else if (exclusive
) {
301 if ((retval
= lck_rw_try_lock_exclusive(&cp
->compressed_data_lock
))) {
302 cp
->lockowner
= thread
;
306 if ((retval
= lck_rw_try_lock_shared(&cp
->compressed_data_lock
))) {
307 cp
->lockowner
= (void *)-1;
314 decmpfs_lock_compressed_data(decmpfs_cnode
*cp
, int exclusive
)
316 void *thread
= current_thread();
318 if (cp
->lockowner
== thread
) {
319 /* this thread is already holding an exclusive lock, so bump the count */
321 } else if (exclusive
) {
322 lck_rw_lock_exclusive(&cp
->compressed_data_lock
);
323 cp
->lockowner
= thread
;
326 lck_rw_lock_shared(&cp
->compressed_data_lock
);
327 cp
->lockowner
= (void *)-1;
332 decmpfs_unlock_compressed_data(decmpfs_cnode
*cp
, __unused
int exclusive
)
334 void *thread
= current_thread();
336 if (cp
->lockowner
== thread
) {
337 /* this thread is holding an exclusive lock, so decrement the count */
338 if ((--cp
->lockcount
) > 0) {
339 /* the caller still has outstanding locks, so we're done */
342 cp
->lockowner
= NULL
;
345 lck_rw_done(&cp
->compressed_data_lock
);
349 decmpfs_cnode_get_vnode_state(decmpfs_cnode
*cp
)
351 return cp
->cmp_state
;
355 decmpfs_cnode_set_vnode_state(decmpfs_cnode
*cp
, uint32_t state
, int skiplock
)
357 if (!skiplock
) decmpfs_lock_compressed_data(cp
, 1);
358 cp
->cmp_state
= state
;
359 if (state
== FILE_TYPE_UNKNOWN
) {
360 /* clear out the compression type too */
363 if (!skiplock
) decmpfs_unlock_compressed_data(cp
, 1);
367 decmpfs_cnode_set_vnode_cmp_type(decmpfs_cnode
*cp
, uint32_t cmp_type
, int skiplock
)
369 if (!skiplock
) decmpfs_lock_compressed_data(cp
, 1);
370 cp
->cmp_type
= cmp_type
;
371 if (!skiplock
) decmpfs_unlock_compressed_data(cp
, 1);
375 decmpfs_cnode_set_vnode_minimal_xattr(decmpfs_cnode
*cp
, int minimal_xattr
, int skiplock
)
377 if (!skiplock
) decmpfs_lock_compressed_data(cp
, 1);
378 cp
->cmp_minimal_xattr
= minimal_xattr
;
379 if (!skiplock
) decmpfs_unlock_compressed_data(cp
, 1);
383 decmpfs_cnode_get_vnode_cached_size(decmpfs_cnode
*cp
)
385 #if DECMPFS_SUPPORTS_SWAP64
386 return cp
->uncompressed_size
;
389 since this is a 64-bit field, we may not be able to access it atomically
393 lck_mtx_lock(&(cp
->uncompressed_size_mtx
));
394 uint64_t ret
= cp
->uncompressed_size
;
395 lck_mtx_unlock(&(cp
->uncompressed_size_mtx
));
401 decmpfs_cnode_set_vnode_cached_size(decmpfs_cnode
*cp
, uint64_t size
)
403 #if DECMPFS_SUPPORTS_SWAP64
405 uint64_t old
= cp
->uncompressed_size
;
406 if (OSCompareAndSwap64(old
, size
, (UInt64
*)&cp
->uncompressed_size
)) {
409 /* failed to write our value, so loop */
414 since this is a 64-bit field, we may not be able to access it atomically
418 lck_mtx_lock(&(cp
->uncompressed_size_mtx
));
419 cp
->uncompressed_size
= size
;
420 lck_mtx_unlock(&(cp
->uncompressed_size_mtx
));
424 #pragma mark --- decmpfs state routines ---
427 decmpfs_fetch_compressed_header(vnode_t vp
, decmpfs_cnode
*cp
, decmpfs_header
**hdrOut
, int returnInvalid
)
430 fetches vp's compression xattr, converting it into a decmpfs_header; returns 0 or errno
431 if returnInvalid == 1, returns the header even if the type was invalid (out of range),
432 and return ERANGE in that case
435 size_t read_size
= 0;
436 size_t attr_size
= 0;
437 uio_t attr_uio
= NULL
;
440 decmpfs_header
*hdr
= NULL
;
441 char uio_buf
[ UIO_SIZEOF(1) ];
444 (cp
->cmp_type
!= 0) &&
445 (cp
->cmp_minimal_xattr
!= 0)) {
446 /* 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 */
448 MALLOC(data
, char *, sizeof(decmpfs_header
), M_TEMP
, M_WAITOK
);
453 hdr
= (decmpfs_header
*)data
;
454 hdr
->attr_size
= sizeof(decmpfs_disk_header
);
455 hdr
->compression_magic
= DECMPFS_MAGIC
;
456 hdr
->compression_type
= cp
->cmp_type
;
457 hdr
->uncompressed_size
= decmpfs_cnode_get_vnode_cached_size(cp
);
459 /* figure out how big the xattr is on disk */
460 err
= vn_getxattr(vp
, DECMPFS_XATTR_NAME
, NULL
, &attr_size
, XATTR_NOSECURITY
, decmpfs_ctx
);
464 if (attr_size
< sizeof(decmpfs_disk_header
) || attr_size
> MAX_DECMPFS_XATTR_SIZE
) {
469 /* allocation includes space for the extra attr_size field of a compressed_header */
470 MALLOC(data
, char *, attr_size
+ sizeof(hdr
->attr_size
), M_TEMP
, M_WAITOK
);
476 /* read the xattr into our buffer, skipping over the attr_size field at the beginning */
477 attr_uio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
, &uio_buf
[0], sizeof(uio_buf
));
478 uio_addiov(attr_uio
, CAST_USER_ADDR_T(data
+ sizeof(hdr
->attr_size
)), attr_size
);
480 err
= vn_getxattr(vp
, DECMPFS_XATTR_NAME
, attr_uio
, &read_size
, XATTR_NOSECURITY
, decmpfs_ctx
);
483 if (read_size
!= attr_size
) {
487 hdr
= (decmpfs_header
*)data
;
488 hdr
->attr_size
= attr_size
;
489 /* swap the fields to native endian */
490 hdr
->compression_magic
= OSSwapLittleToHostInt32(hdr
->compression_magic
);
491 hdr
->compression_type
= OSSwapLittleToHostInt32(hdr
->compression_type
);
492 hdr
->uncompressed_size
= OSSwapLittleToHostInt64(hdr
->uncompressed_size
);
495 if (hdr
->compression_magic
!= DECMPFS_MAGIC
) {
496 ErrorLog("invalid compression_magic 0x%08x, should be 0x%08x\n", hdr
->compression_magic
, DECMPFS_MAGIC
);
501 if (hdr
->compression_type
>= CMP_MAX
) {
503 /* return the header even though the type is out of range */
506 ErrorLog("compression_type %d out of range\n", hdr
->compression_type
);
513 if (err
&& (err
!= ERANGE
)) {
514 DebugLog("err %d\n", err
);
515 if (data
) FREE(data
, M_TEMP
);
524 decmpfs_fast_get_state(decmpfs_cnode
*cp
)
527 return the cached state
528 this should *only* be called when we know that decmpfs_file_is_compressed has already been called,
529 because this implies that the cached state is valid
531 int cmp_state
= decmpfs_cnode_get_vnode_state(cp
);
534 case FILE_IS_NOT_COMPRESSED
:
535 case FILE_IS_COMPRESSED
:
536 case FILE_IS_CONVERTING
:
538 case FILE_TYPE_UNKNOWN
:
540 we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
541 which should not be possible
543 ErrorLog("decmpfs_fast_get_state called on unknown file\n");
544 return FILE_IS_NOT_COMPRESSED
;
547 ErrorLog("unknown cmp_state %d\n", cmp_state
);
548 return FILE_IS_NOT_COMPRESSED
;
553 decmpfs_fast_file_is_compressed(decmpfs_cnode
*cp
)
555 int cmp_state
= decmpfs_cnode_get_vnode_state(cp
);
558 case FILE_IS_NOT_COMPRESSED
:
560 case FILE_IS_COMPRESSED
:
561 case FILE_IS_CONVERTING
:
563 case FILE_TYPE_UNKNOWN
:
565 we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
566 which should not be possible
568 ErrorLog("decmpfs_fast_get_state called on unknown file\n");
572 ErrorLog("unknown cmp_state %d\n", cmp_state
);
578 decmpfs_validate_compressed_file(vnode_t vp
, decmpfs_cnode
*cp
)
580 /* give a compressor a chance to indicate that a compressed file is invalid */
582 decmpfs_header
*hdr
= NULL
;
583 errno_t err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0);
585 /* we couldn't get the header */
586 if (decmpfs_fast_get_state(cp
) == FILE_IS_NOT_COMPRESSED
) {
587 /* the file is no longer compressed, so return success */
593 lck_rw_lock_shared(decompressorsLock
);
594 decmpfs_validate_compressed_file_func validate
= decmp_get_func(hdr
->compression_type
, validate
);
595 if (validate
) { /* make sure this validation function is valid */
596 /* is the data okay? */
597 err
= validate(vp
, decmpfs_ctx
, hdr
);
598 } else if (decmp_get_func(hdr
->compression_type
, fetch
) == NULL
) {
599 /* the type isn't registered */
602 /* no validate registered, so nothing to do */
605 lck_rw_done(decompressorsLock
);
607 if (hdr
) FREE(hdr
, M_TEMP
);
608 #if COMPRESSION_DEBUG
610 DebugLog("decmpfs_validate_compressed_file ret %d, vp->v_flag %d\n", err
, vp
->v_flag
);
617 decmpfs_file_is_compressed(vnode_t vp
, decmpfs_cnode
*cp
)
620 determines whether vp points to a compressed file
622 to speed up this operation, we cache the result in the cnode, and do as little as possible
623 in the case where the cnode already has a valid cached state
630 struct vnode_attr va_fetch
;
631 decmpfs_header
*hdr
= NULL
;
633 int cnode_locked
= 0;
634 int saveInvalid
= 0; // save the header data even though the type was out of range
636 if (vnode_isnamedstream(vp
)) {
638 named streams can't be compressed
639 since named streams of the same file share the same cnode,
640 we don't want to get/set the state in the cnode, just return 0
645 /* examine the cached a state in this cnode */
646 cmp_state
= decmpfs_cnode_get_vnode_state(cp
);
648 case FILE_IS_NOT_COMPRESSED
:
650 case FILE_IS_COMPRESSED
:
652 case FILE_IS_CONVERTING
:
653 /* treat the file as compressed, because this gives us a way to block future reads until decompression is done */
655 case FILE_TYPE_UNKNOWN
:
656 /* the first time we encountered this vnode, so we need to check it out */
659 /* unknown state, assume file is not compressed */
660 ErrorLog("unknown cmp_state %d\n", cmp_state
);
664 // if (!vnode_isreg(vp)) {
665 // /* only regular files can be compressed */
666 // ret = FILE_IS_NOT_COMPRESSED;
670 mp
= vnode_mount(vp
);
673 this should only be true before we mount the root filesystem
674 we short-cut this return to avoid the call to getattr below, which
675 will fail before root is mounted
677 ret
= FILE_IS_NOT_COMPRESSED
;
680 if ((mp
->mnt_flag
& MNT_LOCAL
) == 0) {
681 /* compression only supported on local filesystems */
682 ret
= FILE_IS_NOT_COMPRESSED
;
686 /* lock our cnode data so that another caller doesn't change the state under us */
687 decmpfs_lock_compressed_data(cp
, 1);
690 VATTR_INIT(&va_fetch
);
691 VATTR_WANTED(&va_fetch
, va_flags
);
692 error
= vnode_getattr(vp
, &va_fetch
, decmpfs_ctx
);
694 /* failed to get the bsd flags so the file is not compressed */
695 ret
= FILE_IS_NOT_COMPRESSED
;
698 if (va_fetch
.va_flags
& UF_COMPRESSED
) {
699 /* UF_COMPRESSED is on, make sure the file has the DECMPFS_XATTR_NAME xattr */
700 error
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 1);
701 if ((hdr
!= NULL
) && (error
== ERANGE
)) {
705 /* failed to get the xattr so the file is not compressed */
706 ret
= FILE_IS_NOT_COMPRESSED
;
709 /* we got the xattr, so the file is compressed */
710 ret
= FILE_IS_COMPRESSED
;
713 /* UF_COMPRESSED isn't on, so the file isn't compressed */
714 ret
= FILE_IS_NOT_COMPRESSED
;
717 if (((ret
== FILE_IS_COMPRESSED
) || saveInvalid
) && hdr
) {
719 cache the uncompressed size away in the cnode
724 we should never get here since the only place ret is set to FILE_IS_COMPRESSED
725 is after the call to decmpfs_lock_compressed_data above
727 decmpfs_lock_compressed_data(cp
, 1);
731 decmpfs_cnode_set_vnode_cached_size(cp
, hdr
->uncompressed_size
);
732 decmpfs_cnode_set_vnode_state(cp
, ret
, 1);
733 decmpfs_cnode_set_vnode_cmp_type(cp
, hdr
->compression_type
, 1);
734 /* remember if the xattr's size was equal to the minimal xattr */
735 if (hdr
->attr_size
== sizeof(decmpfs_disk_header
)) {
736 decmpfs_cnode_set_vnode_minimal_xattr(cp
, 1, 1);
738 if (ret
== FILE_IS_COMPRESSED
) {
739 /* update the ubc's size for this file */
740 ubc_setsize(vp
, hdr
->uncompressed_size
);
743 /* we might have already taken the lock above; if so, skip taking it again by passing cnode_locked as the skiplock parameter */
744 decmpfs_cnode_set_vnode_state(cp
, ret
, cnode_locked
);
747 if (cnode_locked
) decmpfs_unlock_compressed_data(cp
, 1);
749 if (hdr
) FREE(hdr
, M_TEMP
);
752 case FILE_IS_NOT_COMPRESSED
:
754 case FILE_IS_COMPRESSED
:
755 case FILE_IS_CONVERTING
:
758 /* unknown state, assume file is not compressed */
759 ErrorLog("unknown ret %d\n", ret
);
765 decmpfs_update_attributes(vnode_t vp
, struct vnode_attr
*vap
)
769 if (VATTR_IS_ACTIVE(vap
, va_flags
)) {
770 /* the BSD flags are being updated */
771 if (vap
->va_flags
& UF_COMPRESSED
) {
772 /* the compressed bit is being set, did it change? */
773 struct vnode_attr va_fetch
;
775 VATTR_INIT(&va_fetch
);
776 VATTR_WANTED(&va_fetch
, va_flags
);
777 error
= vnode_getattr(vp
, &va_fetch
, decmpfs_ctx
);
781 old_flags
= va_fetch
.va_flags
;
783 if (!(old_flags
& UF_COMPRESSED
)) {
785 * Compression bit was turned on, make sure the file has the DECMPFS_XATTR_NAME attribute.
786 * This precludes anyone from using the UF_COMPRESSED bit for anything else, and it enforces
787 * an order of operation -- you must first do the setxattr and then the chflags.
790 if (VATTR_IS_ACTIVE(vap
, va_data_size
)) {
792 * don't allow the caller to set the BSD flag and the size in the same call
793 * since this doesn't really make sense
795 vap
->va_flags
&= ~UF_COMPRESSED
;
799 decmpfs_header
*hdr
= NULL
;
800 error
= decmpfs_fetch_compressed_header(vp
, NULL
, &hdr
, 1);
803 allow the flag to be set since the decmpfs attribute is present
804 in that case, we also want to truncate the data fork of the file
806 VATTR_SET_ACTIVE(vap
, va_data_size
);
807 vap
->va_data_size
= 0;
808 } else if (error
== ERANGE
) {
809 /* the file had a decmpfs attribute but the type was out of range, so don't muck with the file's data size */
811 /* no DECMPFS_XATTR_NAME attribute, so deny the update */
812 vap
->va_flags
&= ~UF_COMPRESSED
;
814 if (hdr
) FREE(hdr
, M_TEMP
);
823 wait_for_decompress(decmpfs_cnode
*cp
)
826 lck_mtx_lock(decompress_channel_mtx
);
828 state
= decmpfs_fast_get_state(cp
);
829 if (state
!= FILE_IS_CONVERTING
) {
830 /* file is not decompressing */
831 lck_mtx_unlock(decompress_channel_mtx
);
834 msleep((caddr_t
)&decompress_channel
, decompress_channel_mtx
, PINOD
, "wait_for_decompress", NULL
);
838 #pragma mark --- decmpfs hide query routines ---
841 decmpfs_hides_rsrc(vfs_context_t ctx
, decmpfs_cnode
*cp
)
845 callers may (and do) pass NULL for ctx, so we should only use it
846 for this equality comparison
848 This routine should only be called after a file has already been through decmpfs_file_is_compressed
851 if (ctx
== decmpfs_ctx
)
854 if (!decmpfs_fast_file_is_compressed(cp
))
857 /* all compressed files hide their resource fork */
862 decmpfs_hides_xattr(vfs_context_t ctx
, decmpfs_cnode
*cp
, const char *xattr
)
866 callers may (and do) pass NULL for ctx, so we should only use it
867 for this equality comparison
869 This routine should only be called after a file has already been through decmpfs_file_is_compressed
872 if (ctx
== decmpfs_ctx
)
874 if (strncmp(xattr
, XATTR_RESOURCEFORK_NAME
, 22) == 0)
875 return decmpfs_hides_rsrc(ctx
, cp
);
876 if (!decmpfs_fast_file_is_compressed(cp
))
877 /* file is not compressed, so don't hide this xattr */
879 if (strncmp(xattr
, DECMPFS_XATTR_NAME
, 11) == 0)
880 /* it's our xattr, so hide it */
882 /* don't hide this xattr */
886 #pragma mark --- registration/validation routines ---
889 register_decmpfs_decompressor(uint32_t compression_type
, decmpfs_registration
*registration
)
891 /* called by kexts to register decompressors */
895 char resourceName
[80];
897 if ((compression_type
>= CMP_MAX
) ||
899 (registration
->decmpfs_registration
!= DECMPFS_REGISTRATION_VERSION
)) {
904 lck_rw_lock_exclusive(decompressorsLock
); locked
= 1;
906 /* make sure the registration for this type is zero */
907 if (decompressors
[compression_type
] != NULL
) {
911 decompressors
[compression_type
] = registration
;
912 snprintf(resourceName
, sizeof(resourceName
), "com.apple.AppleFSCompression.Type%u", compression_type
);
913 IOServicePublishResource(resourceName
, TRUE
);
914 wakeup((caddr_t
)&decompressors
);
917 if (locked
) lck_rw_done(decompressorsLock
);
922 unregister_decmpfs_decompressor(uint32_t compression_type
, decmpfs_registration
*registration
)
924 /* called by kexts to unregister decompressors */
928 char resourceName
[80];
930 if ((compression_type
>= CMP_MAX
) ||
932 (registration
->decmpfs_registration
!= DECMPFS_REGISTRATION_VERSION
)) {
937 lck_rw_lock_exclusive(decompressorsLock
); locked
= 1;
938 if (decompressors
[compression_type
] != registration
) {
942 decompressors
[compression_type
] = NULL
;
943 snprintf(resourceName
, sizeof(resourceName
), "com.apple.AppleFSCompression.Type%u", compression_type
);
944 IOServicePublishResource(resourceName
, FALSE
);
945 wakeup((caddr_t
)&decompressors
);
948 if (locked
) lck_rw_done(decompressorsLock
);
953 compression_type_valid(decmpfs_header
*hdr
)
955 /* fast pre-check to determine if the given compressor has checked in */
958 /* every compressor must have at least a fetch function */
959 lck_rw_lock_shared(decompressorsLock
);
960 if (decmp_get_func(hdr
->compression_type
, fetch
) != NULL
) {
963 lck_rw_done(decompressorsLock
);
968 #pragma mark --- compression/decompression routines ---
971 decmpfs_fetch_uncompressed_data(vnode_t vp
, decmpfs_header
*hdr
, off_t offset
, user_ssize_t size
, int nvec
, decmpfs_vector
*vec
, uint64_t *bytes_read
)
973 /* get the uncompressed bytes for the specified region of vp by calling out to the registered compressor */
979 if ((uint64_t)offset
>= hdr
->uncompressed_size
) {
980 /* reading past end of file; nothing to do */
985 /* tried to read from before start of file */
989 if ((uint64_t)(offset
+ size
) > hdr
->uncompressed_size
) {
990 /* adjust size so we don't read past the end of the file */
991 size
= hdr
->uncompressed_size
- offset
;
994 /* nothing to read */
999 lck_rw_lock_shared(decompressorsLock
);
1000 decmpfs_fetch_uncompressed_data_func fetch
= decmp_get_func(hdr
->compression_type
, fetch
);
1002 err
= fetch(vp
, decmpfs_ctx
, hdr
, offset
, size
, nvec
, vec
, bytes_read
);
1006 lck_rw_done(decompressorsLock
);
1012 static kern_return_t
1013 commit_upl(upl_t upl
, upl_offset_t pl_offset
, size_t uplSize
, int flags
, int abort
)
1015 kern_return_t kr
= 0;
1017 /* commit the upl pages */
1019 VerboseLog("aborting upl, flags 0x%08x\n", flags
);
1020 kr
= ubc_upl_abort_range(upl
, pl_offset
, uplSize
, flags
);
1021 if (kr
!= KERN_SUCCESS
)
1022 ErrorLog("ubc_upl_commit_range error %d\n", (int)kr
);
1024 VerboseLog("committing upl, flags 0x%08x\n", flags
| UPL_COMMIT_CLEAR_DIRTY
);
1025 kr
= ubc_upl_commit_range(upl
, pl_offset
, uplSize
, flags
| UPL_COMMIT_CLEAR_DIRTY
);
1026 if (kr
!= KERN_SUCCESS
)
1027 ErrorLog("ubc_upl_commit_range error %d\n", (int)kr
);
1033 decmpfs_pagein_compressed(struct vnop_pagein_args
*ap
, int *is_compressed
, decmpfs_cnode
*cp
)
1035 /* handles a page-in request from vfs for a compressed file */
1038 struct vnode
*vp
= ap
->a_vp
;
1039 upl_t pl
= ap
->a_pl
;
1040 upl_offset_t pl_offset
= ap
->a_pl_offset
;
1041 off_t f_offset
= ap
->a_f_offset
;
1042 size_t size
= ap
->a_size
;
1043 int flags
= ap
->a_flags
;
1045 user_ssize_t uplSize
= 0;
1047 decmpfs_header
*hdr
= NULL
;
1048 int abort_pagein
= 0;
1049 uint64_t cachedSize
= 0;
1050 int cmpdata_locked
= 0;
1052 if(!decmpfs_trylock_compressed_data(cp
, 0)) {
1058 if (flags
& ~(UPL_IOSYNC
| UPL_NOCOMMIT
| UPL_NORDAHEAD
)) {
1059 DebugLog("pagein: unknown flags 0x%08x\n", (flags
& ~(UPL_IOSYNC
| UPL_NOCOMMIT
| UPL_NORDAHEAD
)));
1062 err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0);
1067 cachedSize
= hdr
->uncompressed_size
;
1069 if (!compression_type_valid(hdr
)) {
1070 /* compressor not registered */
1075 /* map the upl so we can fetch into it */
1076 kern_return_t kr
= ubc_upl_map(pl
, (vm_offset_t
*)&data
);
1077 if ((kr
!= KERN_SUCCESS
) || (data
== NULL
)) {
1084 /* clip the size to the size of the file */
1085 if ((uint64_t)uplPos
+ uplSize
> cachedSize
) {
1086 /* truncate the read to the size of the file */
1087 uplSize
= cachedSize
- uplPos
;
1094 /* 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 */
1095 vec
.buf
= (char*)data
+ pl_offset
;
1098 uint64_t did_read
= 0;
1099 if (decmpfs_fast_get_state(cp
) == FILE_IS_CONVERTING
) {
1100 ErrorLog("unexpected pagein during decompress\n");
1102 if the file is converting, this must be a recursive call to pagein from underneath a call to decmpfs_decompress_file;
1103 pretend that it succeeded but don't do anything since we're just going to write over the pages anyway
1108 err
= decmpfs_fetch_uncompressed_data(vp
, hdr
, uplPos
, uplSize
, 1, &vec
, &did_read
);
1111 DebugLog("decmpfs_fetch_uncompressed_data err %d\n", err
);
1112 int cmp_state
= decmpfs_fast_get_state(cp
);
1113 if (cmp_state
== FILE_IS_CONVERTING
) {
1114 DebugLog("cmp_state == FILE_IS_CONVERTING\n");
1115 cmp_state
= wait_for_decompress(cp
);
1116 if (cmp_state
== FILE_IS_COMPRESSED
) {
1117 DebugLog("cmp_state == FILE_IS_COMPRESSED\n");
1118 /* a decompress was attempted but it failed, let's try calling fetch again */
1122 if (cmp_state
== FILE_IS_NOT_COMPRESSED
) {
1123 DebugLog("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1124 /* the file was decompressed after we started reading it */
1125 abort_pagein
= 1; /* we're not going to commit our data */
1126 *is_compressed
= 0; /* instruct caller to fall back to its normal path */
1130 /* zero out whatever we didn't read, and zero out the end of the last page(s) */
1131 uint64_t total_size
= (size
+ (PAGE_SIZE
- 1)) & ~(PAGE_SIZE
- 1);
1132 if (did_read
< total_size
) {
1133 memset((char*)vec
.buf
+ did_read
, 0, total_size
- did_read
);
1136 kr
= ubc_upl_unmap(pl
); data
= NULL
; /* make sure to set data to NULL so we don't try to unmap again below */
1137 if (kr
!= KERN_SUCCESS
)
1138 ErrorLog("ubc_upl_unmap error %d\n", (int)kr
);
1140 if (!abort_pagein
) {
1141 /* commit our pages */
1142 kr
= commit_upl(pl
, pl_offset
, total_size
, UPL_COMMIT_FREE_ON_EMPTY
, 0);
1147 if (data
) ubc_upl_unmap(pl
);
1148 if (hdr
) FREE(hdr
, M_TEMP
);
1149 if (cmpdata_locked
) decmpfs_unlock_compressed_data(cp
, 0);
1151 ErrorLog("err %d\n", err
);
1157 decmpfs_read_compressed(struct vnop_read_args
*ap
, int *is_compressed
, decmpfs_cnode
*cp
)
1159 /* handles a read request from vfs for a compressed file */
1161 uio_t uio
= ap
->a_uio
;
1162 vnode_t vp
= ap
->a_vp
;
1166 user_ssize_t uplSize
= 0;
1167 user_ssize_t uplRemaining
= 0;
1168 off_t curUplPos
= 0;
1169 user_ssize_t curUplSize
= 0;
1170 kern_return_t kr
= KERN_SUCCESS
;
1173 uint64_t did_read
= 0;
1175 upl_page_info_t
*pli
= NULL
;
1176 decmpfs_header
*hdr
= NULL
;
1177 uint64_t cachedSize
= 0;
1179 user_ssize_t uioRemaining
= 0;
1180 int cmpdata_locked
= 0;
1182 decmpfs_lock_compressed_data(cp
, 0); cmpdata_locked
= 1;
1184 uplPos
= uio_offset(uio
);
1185 uplSize
= uio_resid(uio
);
1186 VerboseLog("uplPos %lld uplSize %lld\n", uplPos
, uplSize
);
1188 cachedSize
= decmpfs_cnode_get_vnode_cached_size(cp
);
1190 if ((uint64_t)uplPos
+ uplSize
> cachedSize
) {
1191 /* truncate the read to the size of the file */
1192 uplSize
= cachedSize
- uplPos
;
1195 /* give the cluster layer a chance to fill in whatever it already has */
1196 countInt
= (uplSize
> INT_MAX
) ? INT_MAX
: uplSize
;
1197 err
= cluster_copy_ubc_data(vp
, uio
, &countInt
, 0);
1201 /* figure out what's left */
1202 uioPos
= uio_offset(uio
);
1203 uioRemaining
= uio_resid(uio
);
1204 if ((uint64_t)uioPos
+ uioRemaining
> cachedSize
) {
1205 /* truncate the read to the size of the file */
1206 uioRemaining
= cachedSize
- uioPos
;
1209 if (uioRemaining
<= 0) {
1214 err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0);
1218 if (!compression_type_valid(hdr
)) {
1224 uplSize
= uioRemaining
;
1225 #if COMPRESSION_DEBUG
1226 char path
[PATH_MAX
];
1227 DebugLog("%s: uplPos %lld uplSize %lld\n", vnpath(vp
, path
, sizeof(path
)), (uint64_t)uplPos
, (uint64_t)uplSize
);
1230 lck_rw_lock_shared(decompressorsLock
);
1231 decmpfs_adjust_fetch_region_func adjust_fetch
= decmp_get_func(hdr
->compression_type
, adjust_fetch
);
1233 /* give the compressor a chance to adjust the portion of the file that we read */
1234 adjust_fetch(vp
, decmpfs_ctx
, hdr
, &uplPos
, &uplSize
);
1235 VerboseLog("adjusted uplPos %lld uplSize %lld\n", (uint64_t)uplPos
, (uint64_t)uplSize
);
1237 lck_rw_done(decompressorsLock
);
1239 /* clip the adjusted size to the size of the file */
1240 if ((uint64_t)uplPos
+ uplSize
> cachedSize
) {
1241 /* truncate the read to the size of the file */
1242 uplSize
= cachedSize
- uplPos
;
1251 since we're going to create a upl for the given region of the file,
1252 make sure we're on page boundaries
1255 if (uplPos
& (PAGE_SIZE
- 1)) {
1256 /* round position down to page boundary */
1257 uplSize
+= (uplPos
& (PAGE_SIZE
- 1));
1258 uplPos
&= ~(PAGE_SIZE
- 1);
1260 /* round size up to page multiple */
1261 uplSize
= (uplSize
+ (PAGE_SIZE
- 1)) & ~(PAGE_SIZE
- 1);
1263 VerboseLog("new uplPos %lld uplSize %lld\n", (uint64_t)uplPos
, (uint64_t)uplSize
);
1265 uplRemaining
= uplSize
;
1269 while(uplRemaining
> 0) {
1270 /* start after the last upl */
1271 curUplPos
+= curUplSize
;
1273 /* clip to max upl size */
1274 curUplSize
= uplRemaining
;
1275 if (curUplSize
> MAX_UPL_SIZE
* PAGE_SIZE
) {
1276 curUplSize
= MAX_UPL_SIZE
* PAGE_SIZE
;
1279 /* create the upl */
1280 kr
= ubc_create_upl(vp
, curUplPos
, curUplSize
, &upl
, &pli
, UPL_SET_LITE
);
1281 if (kr
!= KERN_SUCCESS
) {
1282 ErrorLog("ubc_create_upl error %d\n", (int)kr
);
1286 VerboseLog("curUplPos %lld curUplSize %lld\n", (uint64_t)curUplPos
, (uint64_t)curUplSize
);
1289 kr
= ubc_upl_map(upl
, (vm_offset_t
*)&data
);
1290 if (kr
!= KERN_SUCCESS
) {
1291 ErrorLog("ubc_upl_map error %d\n", (int)kr
);
1296 /* make sure the map succeeded */
1298 ErrorLog("ubc_upl_map mapped null\n");
1303 /* fetch uncompressed data into the mapped upl */
1306 vec
= (decmpfs_vector
){ .buf
= data
, .size
= curUplSize
};
1307 err
= decmpfs_fetch_uncompressed_data(vp
, hdr
, curUplPos
, curUplSize
, 1, &vec
, &did_read
);
1309 ErrorLog("decmpfs_fetch_uncompressed_data err %d\n", err
);
1311 /* maybe the file is converting to decompressed */
1312 int cmp_state
= decmpfs_fast_get_state(cp
);
1313 if (cmp_state
== FILE_IS_CONVERTING
) {
1314 ErrorLog("cmp_state == FILE_IS_CONVERTING\n");
1315 cmp_state
= wait_for_decompress(cp
);
1316 if (cmp_state
== FILE_IS_COMPRESSED
) {
1317 ErrorLog("cmp_state == FILE_IS_COMPRESSED\n");
1318 /* a decompress was attempted but it failed, let's try fetching again */
1322 if (cmp_state
== FILE_IS_NOT_COMPRESSED
) {
1323 ErrorLog("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1324 /* the file was decompressed after we started reading it */
1325 abort_read
= 1; /* we're not going to commit our data */
1326 *is_compressed
= 0; /* instruct caller to fall back to its normal path */
1331 /* zero out the remainder of the last page */
1332 memset((char*)data
+ did_read
, 0, curUplSize
- did_read
);
1333 kr
= ubc_upl_unmap(upl
);
1334 if (kr
== KERN_SUCCESS
) {
1336 kr
= commit_upl(upl
, 0, curUplSize
, UPL_ABORT_FREE_ON_EMPTY
, 1);
1338 VerboseLog("uioPos %lld uioRemaining %lld\n", (uint64_t)uioPos
, (uint64_t)uioRemaining
);
1340 off_t uplOff
= uioPos
- curUplPos
;
1342 ErrorLog("uplOff %lld should never be negative\n", (int64_t)uplOff
);
1345 off_t count
= curUplPos
+ curUplSize
- uioPos
;
1347 /* this upl is entirely before the uio */
1349 if (count
> uioRemaining
)
1350 count
= uioRemaining
;
1351 int io_resid
= count
;
1352 err
= cluster_copy_upl_data(uio
, upl
, uplOff
, &io_resid
);
1353 int copied
= count
- io_resid
;
1354 VerboseLog("uplOff %lld count %lld copied %lld\n", (uint64_t)uplOff
, (uint64_t)count
, (uint64_t)copied
);
1356 ErrorLog("cluster_copy_upl_data err %d\n", err
);
1359 uioRemaining
-= copied
;
1363 kr
= commit_upl(upl
, 0, curUplSize
, UPL_COMMIT_FREE_ON_EMPTY
| UPL_COMMIT_INACTIVATE
, 0);
1369 ErrorLog("ubc_upl_unmap error %d\n", (int)kr
);
1372 uplRemaining
-= curUplSize
;
1376 if (hdr
) FREE(hdr
, M_TEMP
);
1377 if (cmpdata_locked
) decmpfs_unlock_compressed_data(cp
, 0);
1378 if (err
) {/* something went wrong */
1379 ErrorLog("err %d\n", err
);
1383 #if COMPRESSION_DEBUG
1384 uplSize
= uio_resid(uio
);
1386 VerboseLog("still %lld bytes to copy\n", uplSize
);
1392 decmpfs_free_compressed_data(vnode_t vp
, decmpfs_cnode
*cp
)
1395 call out to the decompressor to free remove any data associated with this compressed file
1396 then delete the file's compression xattr
1399 decmpfs_header
*hdr
= NULL
;
1400 int err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0);
1402 ErrorLog("decmpfs_fetch_compressed_header err %d\n", err
);
1404 lck_rw_lock_shared(decompressorsLock
);
1405 decmpfs_free_compressed_data_func free_data
= decmp_get_func(hdr
->compression_type
, free_data
);
1407 err
= free_data(vp
, decmpfs_ctx
, hdr
);
1409 /* nothing to do, so no error */
1412 lck_rw_done(decompressorsLock
);
1415 ErrorLog("decompressor err %d\n", err
);
1419 /* delete the xattr */
1420 err
= vn_removexattr(vp
, DECMPFS_XATTR_NAME
, 0, decmpfs_ctx
);
1426 if (hdr
) FREE(hdr
, M_TEMP
);
1430 #pragma mark --- file conversion routines ---
1433 unset_compressed_flag(vnode_t vp
)
1436 struct vnode_attr va
;
1437 int new_bsdflags
= 0;
1440 VATTR_WANTED(&va
, va_flags
);
1441 err
= vnode_getattr(vp
, &va
, decmpfs_ctx
);
1444 ErrorLog("vnode_getattr err %d\n", err
);
1446 new_bsdflags
= va
.va_flags
& ~UF_COMPRESSED
;
1449 VATTR_SET(&va
, va_flags
, new_bsdflags
);
1450 err
= vnode_setattr(vp
, &va
, decmpfs_ctx
);
1452 ErrorLog("vnode_setattr err %d\n", err
);
1459 decmpfs_decompress_file(vnode_t vp
, decmpfs_cnode
*cp
, off_t toSize
, int truncate_okay
, int skiplock
)
1461 /* convert a compressed file to an uncompressed file */
1467 uint32_t old_state
= 0;
1468 uint32_t new_state
= 0;
1469 int update_file_state
= 0;
1471 decmpfs_header
*hdr
= NULL
;
1472 int cmpdata_locked
= 0;
1473 off_t remaining
= 0;
1474 uint64_t uncompressed_size
= 0;
1477 decmpfs_lock_compressed_data(cp
, 1); cmpdata_locked
= 1;
1481 old_state
= decmpfs_fast_get_state(cp
);
1484 case FILE_IS_NOT_COMPRESSED
:
1486 /* someone else decompressed the file */
1491 case FILE_TYPE_UNKNOWN
:
1493 /* the file is in an unknown state, so update the state and retry */
1494 (void)decmpfs_file_is_compressed(vp
, cp
);
1500 case FILE_IS_COMPRESSED
:
1502 /* the file is compressed, so decompress it */
1509 this shouldn't happen since multiple calls to decmpfs_decompress_file lock each other out,
1510 and when decmpfs_decompress_file returns, the state should be always be set back to
1511 FILE_IS_NOT_COMPRESSED or FILE_IS_UNKNOWN
1518 err
= decmpfs_fetch_compressed_header(vp
, cp
, &hdr
, 0);
1523 uncompressed_size
= hdr
->uncompressed_size
;
1525 toSize
= hdr
->uncompressed_size
;
1528 /* special case truncating the file to zero bytes */
1530 } else if ((uint64_t)toSize
> hdr
->uncompressed_size
) {
1531 /* the caller is trying to grow the file, so we should decompress all the data */
1532 toSize
= hdr
->uncompressed_size
;
1535 allocSize
= MIN(64*1024, toSize
);
1536 MALLOC(data
, char *, allocSize
, M_TEMP
, M_WAITOK
);
1542 uio_w
= uio_create(1, 0LL, UIO_SYSSPACE
, UIO_WRITE
);
1547 uio_w
->uio_flags
|= UIO_FLAGS_IS_COMPRESSED_FILE
;
1551 /* tell the buffer cache that this is an empty file */
1554 /* if we got here, we need to decompress the file */
1555 decmpfs_cnode_set_vnode_state(cp
, FILE_IS_CONVERTING
, 1);
1557 while(remaining
> 0) {
1558 /* loop decompressing data from the file and writing it into the data fork */
1560 uint64_t bytes_read
= 0;
1561 decmpfs_vector vec
= { .buf
= data
, .size
= MIN(allocSize
, remaining
) };
1562 err
= decmpfs_fetch_uncompressed_data(vp
, hdr
, offset
, vec
.size
, 1, &vec
, &bytes_read
);
1564 ErrorLog("decmpfs_fetch_uncompressed_data err %d\n", err
);
1568 if (bytes_read
== 0) {
1569 /* we're done reading data */
1573 uio_reset(uio_w
, offset
, UIO_SYSSPACE
, UIO_WRITE
);
1574 err
= uio_addiov(uio_w
, CAST_USER_ADDR_T(data
), bytes_read
);
1576 ErrorLog("uio_addiov err %d\n", err
);
1581 err
= VNOP_WRITE(vp
, uio_w
, 0, decmpfs_ctx
);
1583 /* if the write failed, truncate the file to zero bytes */
1584 ErrorLog("VNOP_WRITE err %d\n", err
);
1587 offset
+= bytes_read
;
1588 remaining
-= bytes_read
;
1592 if (offset
!= toSize
) {
1593 ErrorLog("file decompressed to %lld instead of %lld\n", offset
, toSize
);
1600 /* sync the data and metadata */
1601 err
= VNOP_FSYNC(vp
, MNT_WAIT
, decmpfs_ctx
);
1603 ErrorLog("VNOP_FSYNC err %d\n", err
);
1609 /* write, setattr, or fsync failed */
1610 ErrorLog("aborting decompress, err %d\n", err
);
1611 if (truncate_okay
) {
1612 /* truncate anything we might have written */
1613 int error
= vnode_setsize(vp
, 0, 0, decmpfs_ctx
);
1614 ErrorLog("vnode_setsize err %d\n", error
);
1620 /* if we're truncating the file to zero bytes, we'll skip ahead to here */
1622 /* unset the compressed flag */
1623 unset_compressed_flag(vp
);
1625 /* free the compressed data associated with this file */
1626 err
= decmpfs_free_compressed_data(vp
, cp
);
1628 ErrorLog("decmpfs_free_compressed_data err %d\n", err
);
1632 even if free_compressed_data or vnode_getattr/vnode_setattr failed, return success
1633 since we succeeded in writing all of the file data to the data fork
1637 /* if we got this far, the file was successfully decompressed */
1638 update_file_state
= 1;
1639 new_state
= FILE_IS_NOT_COMPRESSED
;
1641 #if COMPRESSION_DEBUG
1643 uint64_t filesize
= 0;
1644 vnsize(vp
, &filesize
);
1645 DebugLog("new file size %lld\n", filesize
);
1650 if (hdr
) FREE(hdr
, M_TEMP
);
1651 if (data
) FREE(data
, M_TEMP
);
1652 if (uio_w
) uio_free(uio_w
);
1655 /* if there was a failure, reset compression flags to unknown and clear the buffer cache data */
1656 update_file_state
= 1;
1657 new_state
= FILE_TYPE_UNKNOWN
;
1658 if (uncompressed_size
) {
1660 ubc_setsize(vp
, uncompressed_size
);
1664 if (update_file_state
) {
1665 lck_mtx_lock(decompress_channel_mtx
);
1666 decmpfs_cnode_set_vnode_state(cp
, new_state
, 1);
1667 wakeup((caddr_t
)&decompress_channel
); /* wake up anyone who might have been waiting for decompression */
1668 lck_mtx_unlock(decompress_channel_mtx
);
1671 if (cmpdata_locked
) decmpfs_unlock_compressed_data(cp
, 1);
1676 #pragma mark --- Type1 compressor ---
1679 The "Type1" compressor stores the data fork directly in the compression xattr
1683 decmpfs_validate_compressed_file_Type1(__unused vnode_t vp
, __unused vfs_context_t ctx
, decmpfs_header
*hdr
)
1687 if (hdr
->uncompressed_size
+ sizeof(decmpfs_disk_header
) != (uint64_t)hdr
->attr_size
) {
1696 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
)
1700 user_ssize_t remaining
;
1702 if (hdr
->uncompressed_size
+ sizeof(decmpfs_disk_header
) != (uint64_t)hdr
->attr_size
) {
1707 #if COMPRESSION_DEBUG
1708 static int dummy
= 0; // prevent syslog from coalescing printfs
1709 char path
[PATH_MAX
];
1710 DebugLog("%s: %d memcpy %lld at %lld\n", vnpath(vp
, path
, sizeof(path
)), dummy
++, size
, (uint64_t)offset
);
1714 for (i
= 0; (i
< nvec
) && (remaining
> 0); i
++) {
1715 user_ssize_t curCopy
= vec
[i
].size
;
1716 if (curCopy
> remaining
)
1717 curCopy
= remaining
;
1718 memcpy(vec
[i
].buf
, hdr
->attr_bytes
+ offset
, curCopy
);
1720 remaining
-= curCopy
;
1723 if ((bytes_read
) && (err
== 0))
1724 *bytes_read
= (size
- remaining
);
1730 static decmpfs_registration Type1Reg
=
1732 .decmpfs_registration
= DECMPFS_REGISTRATION_VERSION
,
1733 .validate
= decmpfs_validate_compressed_file_Type1
,
1734 .adjust_fetch
= NULL
, /* no adjust necessary */
1735 .fetch
= decmpfs_fetch_uncompressed_data_Type1
,
1736 .free_data
= NULL
/* no free necessary */
1739 #pragma mark --- decmpfs initialization ---
1743 static int done
= 0;
1746 decmpfs_ctx
= vfs_context_create(vfs_context_kernel());
1748 lck_grp_attr_t
*attr
= lck_grp_attr_alloc_init();
1749 decmpfs_lockgrp
= lck_grp_alloc_init("VFSCOMP", attr
);
1750 decompressorsLock
= lck_rw_alloc_init(decmpfs_lockgrp
, NULL
);
1751 decompress_channel_mtx
= lck_mtx_alloc_init(decmpfs_lockgrp
, NULL
);
1753 register_decmpfs_decompressor(CMP_Type1
, &Type1Reg
);
1757 #endif /* HFS_COMPRESSION */