xref: /xnu-8796.101.5/bsd/kern/decmpfs.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1 /*
2  * Copyright (c) 2008-2018 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 #if !FS_COMPRESSION
29 
30 /* We need these symbols even though compression is turned off */
31 
32 #define UNUSED_SYMBOL(x)        asm(".global _" #x "\n.set _" #x ", 0\n");
33 
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)
58 
59 #else /* FS_COMPRESSION */
60 #include <sys/kernel.h>
61 #include <sys/vnode_internal.h>
62 #include <sys/file_internal.h>
63 #include <sys/stat.h>
64 #include <sys/fcntl.h>
65 #include <sys/xattr.h>
66 #include <sys/namei.h>
67 #include <sys/user.h>
68 #include <sys/mount_internal.h>
69 #include <sys/ubc.h>
70 #include <sys/decmpfs.h>
71 #include <sys/uio_internal.h>
72 #include <libkern/OSByteOrder.h>
73 #include <libkern/section_keywords.h>
74 #include <sys/fsctl.h>
75 
76 #include <sys/kdebug_triage.h>
77 
78 #include <ptrauth.h>
79 
80 #pragma mark --- debugging ---
81 
82 #define COMPRESSION_DEBUG 0
83 #define COMPRESSION_DEBUG_VERBOSE 0
84 #define MALLOC_DEBUG 0
85 
86 #if COMPRESSION_DEBUG
87 static char*
88 vnpath(vnode_t vp, char *path, int len)
89 {
90 	int origlen = len;
91 	path[0] = 0;
92 	vn_getpath(vp, path, &len);
93 	path[origlen - 1] = 0;
94 	return path;
95 }
96 #endif
97 
98 #define ErrorLog(x, args...) \
99 	printf("%s:%d:%s: " x, __FILE_NAME__, __LINE__, __FUNCTION__, ## args)
100 #if COMPRESSION_DEBUG
101 #define ErrorLogWithPath(x, args...) do { \
102 	char *path = zalloc(ZV_NAMEI); \
103 	printf("%s:%d:%s: %s: " x, __FILE_NAME__, __LINE__, __FUNCTION__, \
104 	    vnpath(vp, path, PATH_MAX), ## args); \
105 	zfree(ZV_NAMEI, path); \
106 } while(0)
107 #else
108 #define ErrorLogWithPath(x, args...) do { \
109 	(void*)vp; \
110 	printf("%s:%d:%s: %s: " x, __FILE_NAME__, __LINE__, __FUNCTION__, \
111 	    "<private>", ## args); \
112 } while(0)
113 #endif
114 
115 #if COMPRESSION_DEBUG
116 #define DebugLog ErrorLog
117 #define DebugLogWithPath ErrorLogWithPath
118 #else
119 #define DebugLog(x...) do { } while(0)
120 #define DebugLogWithPath(x...) do { } while(0)
121 #endif
122 
123 #if COMPRESSION_DEBUG_VERBOSE
124 #define VerboseLog ErrorLog
125 #define VerboseLogWithPath ErrorLogWithPath
126 #else
127 #define VerboseLog(x...) do { } while(0)
128 #define VerboseLogWithPath(x...) do { } while(0)
129 #endif
130 
131 #define decmpfs_ktriage_record(code, arg) ktriage_record(thread_tid(current_thread()), KDBG_TRIAGE_EVENTID(KDBG_TRIAGE_SUBSYS_DECMPFS, KDBG_TRIAGE_RESERVED, code), arg);
132 
133 enum ktriage_decmpfs_error_codes {
134 	KTRIAGE_DECMPFS_PREFIX = 0,
135 	KTRIAGE_DECMPFS_IVALID_OFFSET,
136 	KTRIAGE_DECMPFS_COMPRESSOR_NOT_REGISTERED,
137 	KTRIAGE_DECMPFS_FETCH_CALLBACK_FAILED,
138 	KTRIAGE_DECMPFS_FETCH_HEADER_FAILED,
139 	KTRIAGE_DECMPFS_UBC_UPL_MAP_FAILED,
140 	KTRIAGE_DECMPFS_FETCH_UNCOMPRESSED_DATA_FAILED,
141 
142 	KTRIAGE_DECMPFS_MAX
143 };
144 
145 const char *ktriage_decmpfs_strings[] = {
146 	[KTRIAGE_DECMPFS_PREFIX] = "decmpfs - ",
147 	[KTRIAGE_DECMPFS_IVALID_OFFSET] = "pagein offset is invalid\n",
148 	[KTRIAGE_DECMPFS_COMPRESSOR_NOT_REGISTERED] = "compressor is not registered\n",
149 	[KTRIAGE_DECMPFS_FETCH_CALLBACK_FAILED] = "fetch callback failed\n",
150 	[KTRIAGE_DECMPFS_FETCH_HEADER_FAILED] = "fetch decmpfs xattr failed\n",
151 	[KTRIAGE_DECMPFS_UBC_UPL_MAP_FAILED] = "failed to map a UBC UPL\n",
152 	[KTRIAGE_DECMPFS_FETCH_UNCOMPRESSED_DATA_FAILED] = "failed to fetch uncompressed data\n",
153 };
154 
155 ktriage_strings_t ktriage_decmpfs_subsystem_strings = {KTRIAGE_DECMPFS_MAX, ktriage_decmpfs_strings};
156 
157 #pragma mark --- globals ---
158 
159 static LCK_GRP_DECLARE(decmpfs_lockgrp, "VFSCOMP");
160 static LCK_RW_DECLARE(decompressorsLock, &decmpfs_lockgrp);
161 static LCK_MTX_DECLARE(decompress_channel_mtx, &decmpfs_lockgrp);
162 
163 static const decmpfs_registration *decompressors[CMP_MAX]; /* the registered compressors */
164 static int decompress_channel; /* channel used by decompress_file to wake up waiters */
165 
166 vfs_context_t decmpfs_ctx;
167 
168 #pragma mark --- decmp_get_func ---
169 
170 #define offsetof_func(func) ((uintptr_t)offsetof(decmpfs_registration, func))
171 
172 static void *
173 _func_from_offset(uint32_t type, uintptr_t offset, uint32_t discriminator)
174 {
175 	/* get the function at the given offset in the registration for the given type */
176 	const decmpfs_registration *reg = decompressors[type];
177 
178 	switch (reg->decmpfs_registration) {
179 	case DECMPFS_REGISTRATION_VERSION_V1:
180 		if (offset > offsetof_func(free_data)) {
181 			return NULL;
182 		}
183 		break;
184 	case DECMPFS_REGISTRATION_VERSION_V3:
185 		if (offset > offsetof_func(get_flags)) {
186 			return NULL;
187 		}
188 		break;
189 	default:
190 		return NULL;
191 	}
192 
193 	void *ptr = *(void * const *)((uintptr_t)reg + offset);
194 	if (ptr != NULL) {
195 		/* Resign as a function-in-void* */
196 		ptr = ptrauth_auth_and_resign(ptr, ptrauth_key_asia, discriminator, ptrauth_key_asia, 0);
197 	}
198 	return ptr;
199 }
200 
201 extern void IOServicePublishResource( const char * property, boolean_t value );
202 extern boolean_t IOServiceWaitForMatchingResource( const char * property, uint64_t timeout );
203 extern boolean_t IOCatalogueMatchingDriversPresent( const char * property );
204 
205 static void *
206 _decmp_get_func(vnode_t vp, uint32_t type, uintptr_t offset, uint32_t discriminator)
207 {
208 	/*
209 	 *  this function should be called while holding a shared lock to decompressorsLock,
210 	 *  and will return with the lock held
211 	 */
212 
213 	if (type >= CMP_MAX) {
214 		return NULL;
215 	}
216 
217 	if (decompressors[type] != NULL) {
218 		// the compressor has already registered but the function might be null
219 		return _func_from_offset(type, offset, discriminator);
220 	}
221 
222 	// does IOKit know about a kext that is supposed to provide this type?
223 	char providesName[80];
224 	snprintf(providesName, sizeof(providesName), "com.apple.AppleFSCompression.providesType%u", type);
225 	if (IOCatalogueMatchingDriversPresent(providesName)) {
226 		// there is a kext that says it will register for this type, so let's wait for it
227 		char resourceName[80];
228 		uint64_t delay = 10000000ULL; // 10 milliseconds.
229 		snprintf(resourceName, sizeof(resourceName), "com.apple.AppleFSCompression.Type%u", type);
230 		ErrorLogWithPath("waiting for %s\n", resourceName);
231 		while (decompressors[type] == NULL) {
232 			lck_rw_unlock_shared(&decompressorsLock); // we have to unlock to allow the kext to register
233 			if (IOServiceWaitForMatchingResource(resourceName, delay)) {
234 				lck_rw_lock_shared(&decompressorsLock);
235 				break;
236 			}
237 			if (!IOCatalogueMatchingDriversPresent(providesName)) {
238 				//
239 				ErrorLogWithPath("the kext with %s is no longer present\n", providesName);
240 				lck_rw_lock_shared(&decompressorsLock);
241 				break;
242 			}
243 			ErrorLogWithPath("still waiting for %s\n", resourceName);
244 			delay *= 2;
245 			lck_rw_lock_shared(&decompressorsLock);
246 		}
247 		// IOKit says the kext is loaded, so it should be registered too!
248 		if (decompressors[type] == NULL) {
249 			ErrorLogWithPath("we found %s, but the type still isn't registered\n", providesName);
250 			return NULL;
251 		}
252 		// it's now registered, so let's return the function
253 		return _func_from_offset(type, offset, discriminator);
254 	}
255 
256 	// the compressor hasn't registered, so it never will unless someone manually kextloads it
257 	ErrorLogWithPath("tried to access a compressed file of unregistered type %d\n", type);
258 	return NULL;
259 }
260 
261 #define decmp_get_func(vp, type, func) (typeof(decompressors[0]->func))_decmp_get_func(vp, type, offsetof_func(func), ptrauth_function_pointer_type_discriminator(typeof(decompressors[0]->func)))
262 
263 #pragma mark --- utilities ---
264 
265 #if COMPRESSION_DEBUG
266 static int
267 vnsize(vnode_t vp, uint64_t *size)
268 {
269 	struct vnode_attr va;
270 	VATTR_INIT(&va);
271 	VATTR_WANTED(&va, va_data_size);
272 	int error = vnode_getattr(vp, &va, decmpfs_ctx);
273 	if (error != 0) {
274 		ErrorLogWithPath("vnode_getattr err %d\n", error);
275 		return error;
276 	}
277 	*size = va.va_data_size;
278 	return 0;
279 }
280 #endif /* COMPRESSION_DEBUG */
281 
282 #pragma mark --- cnode routines ---
283 
284 ZONE_DEFINE(decmpfs_cnode_zone, "decmpfs_cnode",
285     sizeof(struct decmpfs_cnode), ZC_NONE);
286 
287 decmpfs_cnode *
288 decmpfs_cnode_alloc(void)
289 {
290 	return zalloc(decmpfs_cnode_zone);
291 }
292 
293 void
294 decmpfs_cnode_free(decmpfs_cnode *dp)
295 {
296 	zfree(decmpfs_cnode_zone, dp);
297 }
298 
299 void
300 decmpfs_cnode_init(decmpfs_cnode *cp)
301 {
302 	memset(cp, 0, sizeof(*cp));
303 	lck_rw_init(&cp->compressed_data_lock, &decmpfs_lockgrp, NULL);
304 }
305 
306 void
307 decmpfs_cnode_destroy(decmpfs_cnode *cp)
308 {
309 	lck_rw_destroy(&cp->compressed_data_lock, &decmpfs_lockgrp);
310 }
311 
312 bool
313 decmpfs_trylock_compressed_data(decmpfs_cnode *cp, int exclusive)
314 {
315 	void *thread = current_thread();
316 	bool retval = false;
317 
318 	if (cp->lockowner == thread) {
319 		/* this thread is already holding an exclusive lock, so bump the count */
320 		cp->lockcount++;
321 		retval = true;
322 	} else if (exclusive) {
323 		if ((retval = lck_rw_try_lock_exclusive(&cp->compressed_data_lock))) {
324 			cp->lockowner = thread;
325 			cp->lockcount = 1;
326 		}
327 	} else {
328 		if ((retval = lck_rw_try_lock_shared(&cp->compressed_data_lock))) {
329 			cp->lockowner = (void *)-1;
330 		}
331 	}
332 	return retval;
333 }
334 
335 void
336 decmpfs_lock_compressed_data(decmpfs_cnode *cp, int exclusive)
337 {
338 	void *thread = current_thread();
339 
340 	if (cp->lockowner == thread) {
341 		/* this thread is already holding an exclusive lock, so bump the count */
342 		cp->lockcount++;
343 	} else if (exclusive) {
344 		lck_rw_lock_exclusive(&cp->compressed_data_lock);
345 		cp->lockowner = thread;
346 		cp->lockcount = 1;
347 	} else {
348 		lck_rw_lock_shared(&cp->compressed_data_lock);
349 		cp->lockowner = (void *)-1;
350 	}
351 }
352 
353 void
354 decmpfs_unlock_compressed_data(decmpfs_cnode *cp, __unused int exclusive)
355 {
356 	void *thread = current_thread();
357 
358 	if (cp->lockowner == thread) {
359 		/* this thread is holding an exclusive lock, so decrement the count */
360 		if ((--cp->lockcount) > 0) {
361 			/* the caller still has outstanding locks, so we're done */
362 			return;
363 		}
364 		cp->lockowner = NULL;
365 	}
366 
367 	lck_rw_done(&cp->compressed_data_lock);
368 }
369 
370 uint32_t
371 decmpfs_cnode_get_vnode_state(decmpfs_cnode *cp)
372 {
373 	return cp->cmp_state;
374 }
375 
376 void
377 decmpfs_cnode_set_vnode_state(decmpfs_cnode *cp, uint32_t state, int skiplock)
378 {
379 	if (!skiplock) {
380 		decmpfs_lock_compressed_data(cp, 1);
381 	}
382 	cp->cmp_state = (uint8_t)state;
383 	if (state == FILE_TYPE_UNKNOWN) {
384 		/* clear out the compression type too */
385 		cp->cmp_type = 0;
386 	}
387 	if (!skiplock) {
388 		decmpfs_unlock_compressed_data(cp, 1);
389 	}
390 }
391 
392 static void
393 decmpfs_cnode_set_vnode_cmp_type(decmpfs_cnode *cp, uint32_t cmp_type, int skiplock)
394 {
395 	if (!skiplock) {
396 		decmpfs_lock_compressed_data(cp, 1);
397 	}
398 	cp->cmp_type = cmp_type;
399 	if (!skiplock) {
400 		decmpfs_unlock_compressed_data(cp, 1);
401 	}
402 }
403 
404 static void
405 decmpfs_cnode_set_vnode_minimal_xattr(decmpfs_cnode *cp, int minimal_xattr, int skiplock)
406 {
407 	if (!skiplock) {
408 		decmpfs_lock_compressed_data(cp, 1);
409 	}
410 	cp->cmp_minimal_xattr = !!minimal_xattr;
411 	if (!skiplock) {
412 		decmpfs_unlock_compressed_data(cp, 1);
413 	}
414 }
415 
416 uint64_t
417 decmpfs_cnode_get_vnode_cached_size(decmpfs_cnode *cp)
418 {
419 	return cp->uncompressed_size;
420 }
421 
422 uint64_t
423 decmpfs_cnode_get_vnode_cached_nchildren(decmpfs_cnode *cp)
424 {
425 	return cp->nchildren;
426 }
427 
428 uint64_t
429 decmpfs_cnode_get_vnode_cached_total_size(decmpfs_cnode *cp)
430 {
431 	return cp->total_size;
432 }
433 
434 void
435 decmpfs_cnode_set_vnode_cached_size(decmpfs_cnode *cp, uint64_t size)
436 {
437 	while (1) {
438 		uint64_t old = cp->uncompressed_size;
439 		if (OSCompareAndSwap64(old, size, (UInt64*)&cp->uncompressed_size)) {
440 			return;
441 		} else {
442 			/* failed to write our value, so loop */
443 		}
444 	}
445 }
446 
447 void
448 decmpfs_cnode_set_vnode_cached_nchildren(decmpfs_cnode *cp, uint64_t nchildren)
449 {
450 	while (1) {
451 		uint64_t old = cp->nchildren;
452 		if (OSCompareAndSwap64(old, nchildren, (UInt64*)&cp->nchildren)) {
453 			return;
454 		} else {
455 			/* failed to write our value, so loop */
456 		}
457 	}
458 }
459 
460 void
461 decmpfs_cnode_set_vnode_cached_total_size(decmpfs_cnode *cp, uint64_t total_sz)
462 {
463 	while (1) {
464 		uint64_t old = cp->total_size;
465 		if (OSCompareAndSwap64(old, total_sz, (UInt64*)&cp->total_size)) {
466 			return;
467 		} else {
468 			/* failed to write our value, so loop */
469 		}
470 	}
471 }
472 
473 static uint64_t
474 decmpfs_cnode_get_decompression_flags(decmpfs_cnode *cp)
475 {
476 	return cp->decompression_flags;
477 }
478 
479 static void
480 decmpfs_cnode_set_decompression_flags(decmpfs_cnode *cp, uint64_t flags)
481 {
482 	while (1) {
483 		uint64_t old = cp->decompression_flags;
484 		if (OSCompareAndSwap64(old, flags, (UInt64*)&cp->decompression_flags)) {
485 			return;
486 		} else {
487 			/* failed to write our value, so loop */
488 		}
489 	}
490 }
491 
492 uint32_t
493 decmpfs_cnode_cmp_type(decmpfs_cnode *cp)
494 {
495 	return cp->cmp_type;
496 }
497 
498 #pragma mark --- decmpfs state routines ---
499 
500 static int
501 decmpfs_fetch_compressed_header(vnode_t vp, decmpfs_cnode *cp, decmpfs_header **hdrOut, int returnInvalid, size_t *hdr_size)
502 {
503 	/*
504 	 *  fetches vp's compression xattr, converting it into a decmpfs_header; returns 0 or errno
505 	 *  if returnInvalid == 1, returns the header even if the type was invalid (out of range),
506 	 *  and return ERANGE in that case
507 	 */
508 
509 	size_t read_size             = 0;
510 	size_t attr_size             = 0;
511 	size_t alloc_size            = 0;
512 	uio_t attr_uio               = NULL;
513 	int err                      = 0;
514 	char *data                   = NULL;
515 	const bool no_additional_data = ((cp != NULL)
516 	    && (cp->cmp_type != 0)
517 	    && (cp->cmp_minimal_xattr != 0));
518 	uio_stackbuf_t uio_buf[UIO_SIZEOF(1)];
519 	decmpfs_header *hdr = NULL;
520 
521 	/*
522 	 * Trace the following parameters on entry with event-id 0x03120004
523 	 *
524 	 * @vp->v_id:       vnode-id for which to fetch compressed header.
525 	 * @no_additional_data: If set true then xattr didn't have any extra data.
526 	 * @returnInvalid:  return the header even though the type is out of range.
527 	 */
528 	DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FETCH_COMPRESSED_HEADER, vp->v_id,
529 	    no_additional_data, returnInvalid);
530 
531 	if (no_additional_data) {
532 		/* 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 */
533 
534 		alloc_size = sizeof(decmpfs_header);
535 		data = kalloc_data(sizeof(decmpfs_header), Z_WAITOK);
536 		if (!data) {
537 			err = ENOMEM;
538 			goto out;
539 		}
540 		hdr = (decmpfs_header*)data;
541 		hdr->attr_size = sizeof(decmpfs_disk_header);
542 		hdr->compression_magic = DECMPFS_MAGIC;
543 		hdr->compression_type  = cp->cmp_type;
544 		if (hdr->compression_type == DATALESS_PKG_CMPFS_TYPE) {
545 			if (!vnode_isdir(vp)) {
546 				err = EINVAL;
547 				goto out;
548 			}
549 			hdr->_size.value = DECMPFS_PKG_VALUE_FROM_SIZE_COUNT(
550 				decmpfs_cnode_get_vnode_cached_size(cp),
551 				decmpfs_cnode_get_vnode_cached_nchildren(cp));
552 		} else if (vnode_isdir(vp)) {
553 			hdr->_size.value = decmpfs_cnode_get_vnode_cached_nchildren(cp);
554 		} else {
555 			hdr->_size.value = decmpfs_cnode_get_vnode_cached_size(cp);
556 		}
557 	} else {
558 		/* figure out how big the xattr is on disk */
559 		err = vn_getxattr(vp, DECMPFS_XATTR_NAME, NULL, &attr_size, XATTR_NOSECURITY, decmpfs_ctx);
560 		if (err != 0) {
561 			goto out;
562 		}
563 		alloc_size = attr_size + sizeof(hdr->attr_size);
564 
565 		if (attr_size < sizeof(decmpfs_disk_header) || attr_size > MAX_DECMPFS_XATTR_SIZE) {
566 			err = EINVAL;
567 			goto out;
568 		}
569 
570 		/* allocation includes space for the extra attr_size field of a compressed_header */
571 		data = (char *)kalloc_data(alloc_size, Z_WAITOK);
572 		if (!data) {
573 			err = ENOMEM;
574 			goto out;
575 		}
576 
577 		/* read the xattr into our buffer, skipping over the attr_size field at the beginning */
578 		attr_uio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
579 		uio_addiov(attr_uio, CAST_USER_ADDR_T(data + sizeof(hdr->attr_size)), attr_size);
580 
581 		err = vn_getxattr(vp, DECMPFS_XATTR_NAME, attr_uio, &read_size, XATTR_NOSECURITY, decmpfs_ctx);
582 		if (err != 0) {
583 			goto out;
584 		}
585 		if (read_size != attr_size) {
586 			err = EINVAL;
587 			goto out;
588 		}
589 		hdr = (decmpfs_header*)data;
590 		hdr->attr_size = (uint32_t)attr_size;
591 		/* swap the fields to native endian */
592 		hdr->compression_magic = OSSwapLittleToHostInt32(hdr->compression_magic);
593 		hdr->compression_type  = OSSwapLittleToHostInt32(hdr->compression_type);
594 		hdr->uncompressed_size = OSSwapLittleToHostInt64(hdr->uncompressed_size);
595 	}
596 
597 	if (hdr->compression_magic != DECMPFS_MAGIC) {
598 		ErrorLogWithPath("invalid compression_magic 0x%08x, should be 0x%08x\n", hdr->compression_magic, DECMPFS_MAGIC);
599 		err = EINVAL;
600 		goto out;
601 	}
602 
603 	/*
604 	 * Special-case the DATALESS compressor here; that is a valid type,
605 	 * even through there will never be an entry in the decompressor
606 	 * handler table for it.  If we don't do this, then the cmp_state
607 	 * for this cnode will end up being marked NOT_COMPRESSED, and
608 	 * we'll be stuck in limbo.
609 	 */
610 	if (hdr->compression_type >= CMP_MAX && !decmpfs_type_is_dataless(hdr->compression_type)) {
611 		if (returnInvalid) {
612 			/* return the header even though the type is out of range */
613 			err = ERANGE;
614 		} else {
615 			ErrorLogWithPath("compression_type %d out of range\n", hdr->compression_type);
616 			err = EINVAL;
617 		}
618 		goto out;
619 	}
620 
621 out:
622 	if (err && (err != ERANGE)) {
623 		DebugLogWithPath("err %d\n", err);
624 		kfree_data(data, alloc_size);
625 		*hdrOut = NULL;
626 	} else {
627 		*hdrOut = hdr;
628 		*hdr_size = alloc_size;
629 	}
630 	/*
631 	 * Trace the following parameters on return with event-id 0x03120004.
632 	 *
633 	 * @vp->v_id:       vnode-id for which to fetch compressed header.
634 	 * @err:            value returned from this function.
635 	 */
636 	DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FETCH_COMPRESSED_HEADER, vp->v_id, err);
637 	return err;
638 }
639 
640 static int
641 decmpfs_fast_get_state(decmpfs_cnode *cp)
642 {
643 	/*
644 	 *  return the cached state
645 	 *  this should *only* be called when we know that decmpfs_file_is_compressed has already been called,
646 	 *  because this implies that the cached state is valid
647 	 */
648 	int cmp_state = decmpfs_cnode_get_vnode_state(cp);
649 
650 	switch (cmp_state) {
651 	case FILE_IS_NOT_COMPRESSED:
652 	case FILE_IS_COMPRESSED:
653 	case FILE_IS_CONVERTING:
654 		return cmp_state;
655 	case FILE_TYPE_UNKNOWN:
656 		/*
657 		 *  we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
658 		 *  which should not be possible
659 		 */
660 		ErrorLog("decmpfs_fast_get_state called on unknown file\n");
661 		return FILE_IS_NOT_COMPRESSED;
662 	default:
663 		/* */
664 		ErrorLog("unknown cmp_state %d\n", cmp_state);
665 		return FILE_IS_NOT_COMPRESSED;
666 	}
667 }
668 
669 static int
670 decmpfs_fast_file_is_compressed(decmpfs_cnode *cp)
671 {
672 	int cmp_state = decmpfs_cnode_get_vnode_state(cp);
673 
674 	switch (cmp_state) {
675 	case FILE_IS_NOT_COMPRESSED:
676 		return 0;
677 	case FILE_IS_COMPRESSED:
678 	case FILE_IS_CONVERTING:
679 		return 1;
680 	case FILE_TYPE_UNKNOWN:
681 		/*
682 		 *  we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
683 		 *  which should not be possible
684 		 */
685 		ErrorLog("decmpfs_fast_get_state called on unknown file\n");
686 		return 0;
687 	default:
688 		/* */
689 		ErrorLog("unknown cmp_state %d\n", cmp_state);
690 		return 0;
691 	}
692 }
693 
694 errno_t
695 decmpfs_validate_compressed_file(vnode_t vp, decmpfs_cnode *cp)
696 {
697 	/* give a compressor a chance to indicate that a compressed file is invalid */
698 	decmpfs_header *hdr = NULL;
699 	size_t alloc_size = 0;
700 	errno_t err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0, &alloc_size);
701 
702 	if (err) {
703 		/* we couldn't get the header */
704 		if (decmpfs_fast_get_state(cp) == FILE_IS_NOT_COMPRESSED) {
705 			/* the file is no longer compressed, so return success */
706 			err = 0;
707 		}
708 		goto out;
709 	}
710 
711 	if (!decmpfs_type_is_dataless(hdr->compression_type)) {
712 		lck_rw_lock_shared(&decompressorsLock);
713 		decmpfs_validate_compressed_file_func validate = decmp_get_func(vp, hdr->compression_type, validate);
714 		if (validate) { /* make sure this validation function is valid */
715 			/* is the data okay? */
716 			err = validate(vp, decmpfs_ctx, hdr);
717 		} else if (decmp_get_func(vp, hdr->compression_type, fetch) == NULL) {
718 			/* the type isn't registered */
719 			err = EIO;
720 		} else {
721 			/* no validate registered, so nothing to do */
722 			err = 0;
723 		}
724 		lck_rw_unlock_shared(&decompressorsLock);
725 	}
726 out:
727 	if (hdr != NULL) {
728 		kfree_data(hdr, alloc_size);
729 	}
730 #if COMPRESSION_DEBUG
731 	if (err) {
732 		DebugLogWithPath("decmpfs_validate_compressed_file ret %d, vp->v_flag %d\n", err, vp->v_flag);
733 	}
734 #endif
735 	return err;
736 }
737 
738 int
739 decmpfs_file_is_compressed(vnode_t vp, decmpfs_cnode *cp)
740 {
741 	/*
742 	 *  determines whether vp points to a compressed file
743 	 *
744 	 *  to speed up this operation, we cache the result in the cnode, and do as little as possible
745 	 *  in the case where the cnode already has a valid cached state
746 	 *
747 	 */
748 
749 	int ret = 0;
750 	int error = 0;
751 	uint32_t cmp_state;
752 	struct vnode_attr va_fetch;
753 	decmpfs_header *hdr = NULL;
754 	size_t alloc_size = 0;
755 	mount_t mp = NULL;
756 	int cnode_locked = 0;
757 	int saveInvalid = 0; // save the header data even though the type was out of range
758 	uint64_t decompression_flags = 0;
759 	bool is_mounted, is_local_fs;
760 
761 	if (vnode_isnamedstream(vp)) {
762 		/*
763 		 *  named streams can't be compressed
764 		 *  since named streams of the same file share the same cnode,
765 		 *  we don't want to get/set the state in the cnode, just return 0
766 		 */
767 		return 0;
768 	}
769 
770 	/* examine the cached a state in this cnode */
771 	cmp_state = decmpfs_cnode_get_vnode_state(cp);
772 	switch (cmp_state) {
773 	case FILE_IS_NOT_COMPRESSED:
774 		return 0;
775 	case FILE_IS_COMPRESSED:
776 		return 1;
777 	case FILE_IS_CONVERTING:
778 		/* treat the file as compressed, because this gives us a way to block future reads until decompression is done */
779 		return 1;
780 	case FILE_TYPE_UNKNOWN:
781 		/* the first time we encountered this vnode, so we need to check it out */
782 		break;
783 	default:
784 		/* unknown state, assume file is not compressed */
785 		ErrorLogWithPath("unknown cmp_state %d\n", cmp_state);
786 		return 0;
787 	}
788 
789 	is_mounted = false;
790 	is_local_fs = false;
791 	mp = vnode_mount(vp);
792 	if (mp) {
793 		is_mounted = true;
794 	}
795 	if (is_mounted) {
796 		is_local_fs = ((mp->mnt_flag & MNT_LOCAL));
797 	}
798 	/*
799 	 * Trace the following parameters on entry with event-id 0x03120014.
800 	 *
801 	 * @vp->v_id:       vnode-id of the file being queried.
802 	 * @is_mounted:     set to true if @vp belongs to a mounted fs.
803 	 * @is_local_fs:    set to true if @vp belongs to local fs.
804 	 */
805 	DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id,
806 	    is_mounted, is_local_fs);
807 
808 	if (!is_mounted) {
809 		/*
810 		 *  this should only be true before we mount the root filesystem
811 		 *  we short-cut this return to avoid the call to getattr below, which
812 		 *  will fail before root is mounted
813 		 */
814 		ret = FILE_IS_NOT_COMPRESSED;
815 		goto done;
816 	}
817 
818 	if (!is_local_fs) {
819 		/* compression only supported on local filesystems */
820 		ret = FILE_IS_NOT_COMPRESSED;
821 		goto done;
822 	}
823 
824 	/* lock our cnode data so that another caller doesn't change the state under us */
825 	decmpfs_lock_compressed_data(cp, 1);
826 	cnode_locked = 1;
827 
828 	VATTR_INIT(&va_fetch);
829 	VATTR_WANTED(&va_fetch, va_flags);
830 	error = vnode_getattr(vp, &va_fetch, decmpfs_ctx);
831 	if (error) {
832 		/* failed to get the bsd flags so the file is not compressed */
833 		ret = FILE_IS_NOT_COMPRESSED;
834 		goto done;
835 	}
836 	if (va_fetch.va_flags & UF_COMPRESSED) {
837 		/* UF_COMPRESSED is on, make sure the file has the DECMPFS_XATTR_NAME xattr */
838 		error = decmpfs_fetch_compressed_header(vp, cp, &hdr, 1, &alloc_size);
839 		if ((hdr != NULL) && (error == ERANGE)) {
840 			saveInvalid = 1;
841 		}
842 		if (error) {
843 			/* failed to get the xattr so the file is not compressed */
844 			ret = FILE_IS_NOT_COMPRESSED;
845 			goto done;
846 		}
847 		/*
848 		 * We got the xattr, so the file is at least tagged compressed.
849 		 * For DATALESS, regular files and directories can be "compressed".
850 		 * For all other types, only files are allowed.
851 		 */
852 		if (!vnode_isreg(vp) &&
853 		    !(decmpfs_type_is_dataless(hdr->compression_type) && vnode_isdir(vp))) {
854 			ret = FILE_IS_NOT_COMPRESSED;
855 			goto done;
856 		}
857 		ret = FILE_IS_COMPRESSED;
858 		goto done;
859 	}
860 	/* UF_COMPRESSED isn't on, so the file isn't compressed */
861 	ret = FILE_IS_NOT_COMPRESSED;
862 
863 done:
864 	if (((ret == FILE_IS_COMPRESSED) || saveInvalid) && hdr) {
865 		/*
866 		 *  cache the uncompressed size away in the cnode
867 		 */
868 
869 		if (!cnode_locked) {
870 			/*
871 			 *  we should never get here since the only place ret is set to FILE_IS_COMPRESSED
872 			 *  is after the call to decmpfs_lock_compressed_data above
873 			 */
874 			decmpfs_lock_compressed_data(cp, 1);
875 			cnode_locked = 1;
876 		}
877 
878 		if (vnode_isdir(vp)) {
879 			decmpfs_cnode_set_vnode_cached_size(cp, 64);
880 			decmpfs_cnode_set_vnode_cached_nchildren(cp, decmpfs_get_directory_entries(hdr));
881 			if (hdr->compression_type == DATALESS_PKG_CMPFS_TYPE) {
882 				decmpfs_cnode_set_vnode_cached_total_size(cp, DECMPFS_PKG_SIZE(hdr->_size));
883 			}
884 		} else {
885 			decmpfs_cnode_set_vnode_cached_size(cp, hdr->uncompressed_size);
886 		}
887 		decmpfs_cnode_set_vnode_state(cp, ret, 1);
888 		decmpfs_cnode_set_vnode_cmp_type(cp, hdr->compression_type, 1);
889 		/* remember if the xattr's size was equal to the minimal xattr */
890 		if (hdr->attr_size == sizeof(decmpfs_disk_header)) {
891 			decmpfs_cnode_set_vnode_minimal_xattr(cp, 1, 1);
892 		}
893 		if (ret == FILE_IS_COMPRESSED) {
894 			/* update the ubc's size for this file */
895 			ubc_setsize(vp, hdr->uncompressed_size);
896 
897 			/* update the decompression flags in the decmpfs cnode */
898 			lck_rw_lock_shared(&decompressorsLock);
899 			decmpfs_get_decompression_flags_func get_flags = decmp_get_func(vp, hdr->compression_type, get_flags);
900 			if (get_flags) {
901 				decompression_flags = get_flags(vp, decmpfs_ctx, hdr);
902 			}
903 			lck_rw_unlock_shared(&decompressorsLock);
904 			decmpfs_cnode_set_decompression_flags(cp, decompression_flags);
905 		}
906 	} else {
907 		/* we might have already taken the lock above; if so, skip taking it again by passing cnode_locked as the skiplock parameter */
908 		decmpfs_cnode_set_vnode_state(cp, ret, cnode_locked);
909 	}
910 
911 	if (cnode_locked) {
912 		decmpfs_unlock_compressed_data(cp, 1);
913 	}
914 
915 	if (hdr != NULL) {
916 		kfree_data(hdr, alloc_size);
917 	}
918 
919 	/*
920 	 * Trace the following parameters on return with event-id 0x03120014.
921 	 *
922 	 * @vp->v_id:       vnode-id of the file being queried.
923 	 * @return:         set to 1 is file is compressed.
924 	 */
925 	switch (ret) {
926 	case FILE_IS_NOT_COMPRESSED:
927 		DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id, 0);
928 		return 0;
929 	case FILE_IS_COMPRESSED:
930 	case FILE_IS_CONVERTING:
931 		DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id, 1);
932 		return 1;
933 	default:
934 		/* unknown state, assume file is not compressed */
935 		DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id, 0);
936 		ErrorLogWithPath("unknown ret %d\n", ret);
937 		return 0;
938 	}
939 }
940 
941 int
942 decmpfs_update_attributes(vnode_t vp, struct vnode_attr *vap)
943 {
944 	int error = 0;
945 
946 	if (VATTR_IS_ACTIVE(vap, va_flags)) {
947 		/* the BSD flags are being updated */
948 		if (vap->va_flags & UF_COMPRESSED) {
949 			/* the compressed bit is being set, did it change? */
950 			struct vnode_attr va_fetch;
951 			int old_flags = 0;
952 			VATTR_INIT(&va_fetch);
953 			VATTR_WANTED(&va_fetch, va_flags);
954 			error = vnode_getattr(vp, &va_fetch, decmpfs_ctx);
955 			if (error) {
956 				return error;
957 			}
958 
959 			old_flags = va_fetch.va_flags;
960 
961 			if (!(old_flags & UF_COMPRESSED)) {
962 				/*
963 				 * Compression bit was turned on, make sure the file has the DECMPFS_XATTR_NAME attribute.
964 				 * This precludes anyone from using the UF_COMPRESSED bit for anything else, and it enforces
965 				 * an order of operation -- you must first do the setxattr and then the chflags.
966 				 */
967 
968 				if (VATTR_IS_ACTIVE(vap, va_data_size)) {
969 					/*
970 					 * don't allow the caller to set the BSD flag and the size in the same call
971 					 * since this doesn't really make sense
972 					 */
973 					vap->va_flags &= ~UF_COMPRESSED;
974 					return 0;
975 				}
976 
977 				decmpfs_header *hdr = NULL;
978 				size_t alloc_size = 0;
979 				error = decmpfs_fetch_compressed_header(vp, NULL, &hdr, 1, &alloc_size);
980 				if (error == 0) {
981 					/*
982 					 * Allow the flag to be set since the decmpfs attribute
983 					 * is present.
984 					 *
985 					 * If we're creating a dataless file we do not want to
986 					 * truncate it to zero which allows the file resolver to
987 					 * have more control over when truncation should happen.
988 					 * All other types of compressed files are truncated to
989 					 * zero.
990 					 */
991 					if (!decmpfs_type_is_dataless(hdr->compression_type)) {
992 						VATTR_SET_ACTIVE(vap, va_data_size);
993 						vap->va_data_size = 0;
994 					}
995 				} else if (error == ERANGE) {
996 					/* the file had a decmpfs attribute but the type was out of range, so don't muck with the file's data size */
997 				} else {
998 					/* no DECMPFS_XATTR_NAME attribute, so deny the update */
999 					vap->va_flags &= ~UF_COMPRESSED;
1000 				}
1001 				if (hdr != NULL) {
1002 					kfree_data(hdr, alloc_size);
1003 				}
1004 			}
1005 		}
1006 	}
1007 
1008 	return 0;
1009 }
1010 
1011 static int
1012 wait_for_decompress(decmpfs_cnode *cp)
1013 {
1014 	int state;
1015 	lck_mtx_lock(&decompress_channel_mtx);
1016 	do {
1017 		state = decmpfs_fast_get_state(cp);
1018 		if (state != FILE_IS_CONVERTING) {
1019 			/* file is not decompressing */
1020 			lck_mtx_unlock(&decompress_channel_mtx);
1021 			return state;
1022 		}
1023 		msleep((caddr_t)&decompress_channel, &decompress_channel_mtx, PINOD, "wait_for_decompress", NULL);
1024 	} while (1);
1025 }
1026 
1027 #pragma mark --- decmpfs hide query routines ---
1028 
1029 int
1030 decmpfs_hides_rsrc(vfs_context_t ctx, decmpfs_cnode *cp)
1031 {
1032 	/*
1033 	 *  WARNING!!!
1034 	 *  callers may (and do) pass NULL for ctx, so we should only use it
1035 	 *  for this equality comparison
1036 	 *
1037 	 *  This routine should only be called after a file has already been through decmpfs_file_is_compressed
1038 	 */
1039 
1040 	if (ctx == decmpfs_ctx) {
1041 		return 0;
1042 	}
1043 
1044 	if (!decmpfs_fast_file_is_compressed(cp)) {
1045 		return 0;
1046 	}
1047 
1048 	/* all compressed files hide their resource fork */
1049 	return 1;
1050 }
1051 
1052 int
1053 decmpfs_hides_xattr(vfs_context_t ctx, decmpfs_cnode *cp, const char *xattr)
1054 {
1055 	/*
1056 	 *  WARNING!!!
1057 	 *  callers may (and do) pass NULL for ctx, so we should only use it
1058 	 *  for this equality comparison
1059 	 *
1060 	 *  This routine should only be called after a file has already been through decmpfs_file_is_compressed
1061 	 */
1062 
1063 	if (ctx == decmpfs_ctx) {
1064 		return 0;
1065 	}
1066 	if (strncmp(xattr, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME) - 1) == 0) {
1067 		return decmpfs_hides_rsrc(ctx, cp);
1068 	}
1069 	if (!decmpfs_fast_file_is_compressed(cp)) {
1070 		/* file is not compressed, so don't hide this xattr */
1071 		return 0;
1072 	}
1073 	if (strncmp(xattr, DECMPFS_XATTR_NAME, sizeof(DECMPFS_XATTR_NAME) - 1) == 0) {
1074 		/* it's our xattr, so hide it */
1075 		return 1;
1076 	}
1077 	/* don't hide this xattr */
1078 	return 0;
1079 }
1080 
1081 #pragma mark --- registration/validation routines ---
1082 
1083 static inline int
1084 registration_valid(const decmpfs_registration *registration)
1085 {
1086 	return registration && ((registration->decmpfs_registration == DECMPFS_REGISTRATION_VERSION_V1) || (registration->decmpfs_registration == DECMPFS_REGISTRATION_VERSION_V3));
1087 }
1088 
1089 errno_t
1090 register_decmpfs_decompressor(uint32_t compression_type, const decmpfs_registration *registration)
1091 {
1092 	/* called by kexts to register decompressors */
1093 
1094 	errno_t ret = 0;
1095 	int locked = 0;
1096 	char resourceName[80];
1097 
1098 	if ((compression_type >= CMP_MAX) || !registration_valid(registration)) {
1099 		ret = EINVAL;
1100 		goto out;
1101 	}
1102 
1103 	lck_rw_lock_exclusive(&decompressorsLock); locked = 1;
1104 
1105 	/* make sure the registration for this type is zero */
1106 	if (decompressors[compression_type] != NULL) {
1107 		ret = EEXIST;
1108 		goto out;
1109 	}
1110 	decompressors[compression_type] = registration;
1111 	snprintf(resourceName, sizeof(resourceName), "com.apple.AppleFSCompression.Type%u", compression_type);
1112 	IOServicePublishResource(resourceName, TRUE);
1113 
1114 out:
1115 	if (locked) {
1116 		lck_rw_unlock_exclusive(&decompressorsLock);
1117 	}
1118 	return ret;
1119 }
1120 
1121 errno_t
1122 unregister_decmpfs_decompressor(uint32_t compression_type, decmpfs_registration *registration)
1123 {
1124 	/* called by kexts to unregister decompressors */
1125 
1126 	errno_t ret = 0;
1127 	int locked = 0;
1128 	char resourceName[80];
1129 
1130 	if ((compression_type >= CMP_MAX) || !registration_valid(registration)) {
1131 		ret = EINVAL;
1132 		goto out;
1133 	}
1134 
1135 	lck_rw_lock_exclusive(&decompressorsLock); locked = 1;
1136 	if (decompressors[compression_type] != registration) {
1137 		ret = EEXIST;
1138 		goto out;
1139 	}
1140 	decompressors[compression_type] = NULL;
1141 	snprintf(resourceName, sizeof(resourceName), "com.apple.AppleFSCompression.Type%u", compression_type);
1142 	IOServicePublishResource(resourceName, FALSE);
1143 
1144 out:
1145 	if (locked) {
1146 		lck_rw_unlock_exclusive(&decompressorsLock);
1147 	}
1148 	return ret;
1149 }
1150 
1151 static int
1152 compression_type_valid(vnode_t vp, decmpfs_header *hdr)
1153 {
1154 	/* fast pre-check to determine if the given compressor has checked in */
1155 	int ret = 0;
1156 
1157 	/* every compressor must have at least a fetch function */
1158 	lck_rw_lock_shared(&decompressorsLock);
1159 	if (decmp_get_func(vp, hdr->compression_type, fetch) != NULL) {
1160 		ret = 1;
1161 	}
1162 	lck_rw_unlock_shared(&decompressorsLock);
1163 
1164 	return ret;
1165 }
1166 
1167 #pragma mark --- compression/decompression routines ---
1168 
1169 static int
1170 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)
1171 {
1172 	/* get the uncompressed bytes for the specified region of vp by calling out to the registered compressor */
1173 
1174 	int err          = 0;
1175 
1176 	*bytes_read = 0;
1177 
1178 	if (offset >= (off_t)hdr->uncompressed_size) {
1179 		/* reading past end of file; nothing to do */
1180 		err = 0;
1181 		goto out;
1182 	}
1183 	if (offset < 0) {
1184 		/* tried to read from before start of file */
1185 		err = EINVAL;
1186 		goto out;
1187 	}
1188 	if (hdr->uncompressed_size - offset < size) {
1189 		/* adjust size so we don't read past the end of the file */
1190 		size = (user_ssize_t)(hdr->uncompressed_size - offset);
1191 	}
1192 	if (size == 0) {
1193 		/* nothing to read */
1194 		err = 0;
1195 		goto out;
1196 	}
1197 
1198 	/*
1199 	 * Trace the following parameters on entry with event-id 0x03120008.
1200 	 *
1201 	 * @vp->v_id:       vnode-id of the file being decompressed.
1202 	 * @hdr->compression_type: compression type.
1203 	 * @offset:         offset from where to fetch uncompressed data.
1204 	 * @size:           amount of uncompressed data to fetch.
1205 	 *
1206 	 * Please NOTE: @offset and @size can overflow in theory but
1207 	 * here it is safe.
1208 	 */
1209 	DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FETCH_UNCOMPRESSED_DATA, vp->v_id,
1210 	    hdr->compression_type, (int)offset, (int)size);
1211 	lck_rw_lock_shared(&decompressorsLock);
1212 	decmpfs_fetch_uncompressed_data_func fetch = decmp_get_func(vp, hdr->compression_type, fetch);
1213 	if (fetch) {
1214 		err = fetch(vp, decmpfs_ctx, hdr, offset, size, nvec, vec, bytes_read);
1215 		lck_rw_unlock_shared(&decompressorsLock);
1216 		if (err == 0) {
1217 			uint64_t decompression_flags = decmpfs_cnode_get_decompression_flags(cp);
1218 			if (decompression_flags & DECMPFS_FLAGS_FORCE_FLUSH_ON_DECOMPRESS) {
1219 #if     !defined(__i386__) && !defined(__x86_64__)
1220 				int i;
1221 				for (i = 0; i < nvec; i++) {
1222 					assert(vec[i].size >= 0 && vec[i].size <= UINT_MAX);
1223 					flush_dcache64((addr64_t)(uintptr_t)vec[i].buf, (unsigned int)vec[i].size, FALSE);
1224 				}
1225 #endif
1226 			}
1227 		} else {
1228 			decmpfs_ktriage_record(KTRIAGE_DECMPFS_FETCH_CALLBACK_FAILED, err);
1229 		}
1230 	} else {
1231 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_COMPRESSOR_NOT_REGISTERED, hdr->compression_type);
1232 		err = ENOTSUP;
1233 		lck_rw_unlock_shared(&decompressorsLock);
1234 	}
1235 	/*
1236 	 * Trace the following parameters on return with event-id 0x03120008.
1237 	 *
1238 	 * @vp->v_id:       vnode-id of the file being decompressed.
1239 	 * @bytes_read:     amount of uncompressed bytes fetched in bytes.
1240 	 * @err:            value returned from this function.
1241 	 *
1242 	 * Please NOTE: @bytes_read can overflow in theory but here it is safe.
1243 	 */
1244 	DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FETCH_UNCOMPRESSED_DATA, vp->v_id,
1245 	    (int)*bytes_read, err);
1246 out:
1247 	return err;
1248 }
1249 
1250 static kern_return_t
1251 commit_upl(upl_t upl, upl_offset_t pl_offset, size_t uplSize, int flags, int abort)
1252 {
1253 	kern_return_t kr = 0;
1254 
1255 #if CONFIG_IOSCHED
1256 	upl_unmark_decmp(upl);
1257 #endif /* CONFIG_IOSCHED */
1258 
1259 	/* commit the upl pages */
1260 	if (abort) {
1261 		VerboseLog("aborting upl, flags 0x%08x\n", flags);
1262 		kr = ubc_upl_abort_range(upl, pl_offset, (upl_size_t)uplSize, flags);
1263 		if (kr != KERN_SUCCESS) {
1264 			ErrorLog("ubc_upl_abort_range error %d\n", (int)kr);
1265 		}
1266 	} else {
1267 		VerboseLog("committing upl, flags 0x%08x\n", flags | UPL_COMMIT_CLEAR_DIRTY);
1268 		kr = ubc_upl_commit_range(upl, pl_offset, (upl_size_t)uplSize, flags | UPL_COMMIT_CLEAR_DIRTY | UPL_COMMIT_WRITTEN_BY_KERNEL);
1269 		if (kr != KERN_SUCCESS) {
1270 			ErrorLog("ubc_upl_commit_range error %d\n", (int)kr);
1271 		}
1272 	}
1273 	return kr;
1274 }
1275 
1276 
1277 errno_t
1278 decmpfs_pagein_compressed(struct vnop_pagein_args *ap, int *is_compressed, decmpfs_cnode *cp)
1279 {
1280 	/* handles a page-in request from vfs for a compressed file */
1281 
1282 	int err                      = 0;
1283 	vnode_t vp                   = ap->a_vp;
1284 	upl_t pl                     = ap->a_pl;
1285 	upl_offset_t pl_offset       = ap->a_pl_offset;
1286 	off_t f_offset               = ap->a_f_offset;
1287 	size_t size                  = ap->a_size;
1288 	int flags                    = ap->a_flags;
1289 	off_t uplPos                 = 0;
1290 	user_ssize_t uplSize         = 0;
1291 	user_ssize_t rounded_uplSize = 0;
1292 	size_t verify_block_size     = 0;
1293 	void *data                   = NULL;
1294 	decmpfs_header *hdr = NULL;
1295 	size_t alloc_size            = 0;
1296 	uint64_t cachedSize          = 0;
1297 	uint32_t fs_bsize            = 0;
1298 	int cmpdata_locked           = 0;
1299 	int  num_valid_pages         = 0;
1300 	int  num_invalid_pages       = 0;
1301 	bool file_tail_page_valid    = false;
1302 
1303 	if (!decmpfs_trylock_compressed_data(cp, 0)) {
1304 		return EAGAIN;
1305 	}
1306 	cmpdata_locked = 1;
1307 
1308 
1309 	if (flags & ~(UPL_IOSYNC | UPL_NOCOMMIT | UPL_NORDAHEAD)) {
1310 		DebugLogWithPath("pagein: unknown flags 0x%08x\n", (flags & ~(UPL_IOSYNC | UPL_NOCOMMIT | UPL_NORDAHEAD)));
1311 	}
1312 
1313 	err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0, &alloc_size);
1314 	if (err != 0) {
1315 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_FETCH_HEADER_FAILED, err);
1316 		goto out;
1317 	}
1318 
1319 	cachedSize = hdr->uncompressed_size;
1320 
1321 	if (!compression_type_valid(vp, hdr)) {
1322 		/* compressor not registered */
1323 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_COMPRESSOR_NOT_REGISTERED, hdr->compression_type);
1324 		err = ENOTSUP;
1325 		goto out;
1326 	}
1327 
1328 	/*
1329 	 * can't page-in from a negative offset
1330 	 * or if we're starting beyond the EOF
1331 	 * or if the file offset isn't page aligned
1332 	 * or the size requested isn't a multiple of PAGE_SIZE
1333 	 */
1334 	if (f_offset < 0 || f_offset >= cachedSize ||
1335 	    (f_offset & PAGE_MASK_64) || (size & PAGE_MASK) || (pl_offset & PAGE_MASK)) {
1336 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_IVALID_OFFSET, 0);
1337 		err = EINVAL;
1338 		goto out;
1339 	}
1340 
1341 	/*
1342 	 * If the verify block size is larger than the page size, the UPL needs
1343 	 * to be aligned to it, Since the UPL has been created by the filesystem,
1344 	 * we will only check if the passed in UPL length conforms to the
1345 	 * alignment requirements.
1346 	 */
1347 	err = VNOP_VERIFY(vp, f_offset, NULL, 0, &verify_block_size, NULL,
1348 	    VNODE_VERIFY_DEFAULT, NULL);
1349 	if (err) {
1350 		ErrorLogWithPath("VNOP_VERIFY returned error = %d\n", err);
1351 		goto out;
1352 	} else if (verify_block_size) {
1353 		if (vp->v_mount->mnt_vfsstat.f_bsize > PAGE_SIZE) {
1354 			fs_bsize = vp->v_mount->mnt_vfsstat.f_bsize;
1355 		}
1356 		if (verify_block_size & (verify_block_size - 1)) {
1357 			ErrorLogWithPath("verify block size (%zu) is not power of 2, no verification will be done\n", verify_block_size);
1358 			err = EINVAL;
1359 		} else if (size % verify_block_size) {
1360 			ErrorLogWithPath("upl size (%zu) is not a multiple of verify block size (%zu)\n", (size_t)size, verify_block_size);
1361 			err = EINVAL;
1362 		} else if (fs_bsize) {
1363 			/*
1364 			 * Filesystems requesting verification have to provide
1365 			 * values for block sizes which are powers of 2.
1366 			 */
1367 			if (fs_bsize & (fs_bsize - 1)) {
1368 				ErrorLogWithPath("FS block size (%u) is greater than PAGE_SIZE (%d) and is not power of 2, no verification will be done\n",
1369 				    fs_bsize, PAGE_SIZE);
1370 				err = EINVAL;
1371 			} else if (fs_bsize > verify_block_size) {
1372 				ErrorLogWithPath("FS block size (%u) is greater than verify block size (%zu), no verification will be done\n",
1373 				    fs_bsize, verify_block_size);
1374 				err = EINVAL;
1375 			}
1376 		}
1377 		if (err) {
1378 			goto out;
1379 		}
1380 	}
1381 
1382 #if CONFIG_IOSCHED
1383 	/* Mark the UPL as the requesting UPL for decompression */
1384 	upl_mark_decmp(pl);
1385 #endif /* CONFIG_IOSCHED */
1386 
1387 	/* map the upl so we can fetch into it */
1388 	kern_return_t kr = ubc_upl_map(pl, (vm_offset_t*)&data);
1389 	if ((kr != KERN_SUCCESS) || (data == NULL)) {
1390 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_UBC_UPL_MAP_FAILED, kr);
1391 		err = ENOSPC;
1392 		data = NULL;
1393 #if CONFIG_IOSCHED
1394 		upl_unmark_decmp(pl);
1395 #endif /* CONFIG_IOSCHED */
1396 		goto out;
1397 	}
1398 
1399 	uplPos = f_offset;
1400 	off_t max_size = cachedSize - f_offset;
1401 
1402 	if (size < max_size) {
1403 		rounded_uplSize = uplSize = size;
1404 		file_tail_page_valid = true;
1405 	} else {
1406 		uplSize = (user_ssize_t)max_size;
1407 		if (fs_bsize) {
1408 			/* First round up to fs_bsize */
1409 			rounded_uplSize = (uplSize + (fs_bsize - 1)) & ~(fs_bsize - 1);
1410 			/* then to PAGE_SIZE */
1411 			rounded_uplSize = MIN(size, round_page((vm_offset_t)rounded_uplSize));
1412 		} else {
1413 			rounded_uplSize = round_page((vm_offset_t)uplSize);
1414 		}
1415 	}
1416 
1417 	/* do the fetch */
1418 	decmpfs_vector vec;
1419 
1420 decompress:
1421 	/* 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 */
1422 	vec = (decmpfs_vector) {
1423 		.buf = (char*)data + pl_offset,
1424 		.size = size,
1425 	};
1426 
1427 	uint64_t did_read = 0;
1428 	if (decmpfs_fast_get_state(cp) == FILE_IS_CONVERTING) {
1429 		ErrorLogWithPath("unexpected pagein during decompress\n");
1430 		/*
1431 		 *  if the file is converting, this must be a recursive call to pagein from underneath a call to decmpfs_decompress_file;
1432 		 *  pretend that it succeeded but don't do anything since we're just going to write over the pages anyway
1433 		 */
1434 		err = 0;
1435 	} else {
1436 		if (verify_block_size <= PAGE_SIZE) {
1437 			err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, uplPos, uplSize, 1, &vec, &did_read);
1438 			/* zero out whatever wasn't read */
1439 			if (did_read < rounded_uplSize) {
1440 				memset((char*)vec.buf + did_read, 0, (size_t)(rounded_uplSize - did_read));
1441 			}
1442 		} else {
1443 			off_t l_uplPos = uplPos;
1444 			off_t l_pl_offset = pl_offset;
1445 			user_ssize_t l_uplSize = uplSize;
1446 			upl_page_info_t *pl_info = ubc_upl_pageinfo(pl);
1447 
1448 			err = 0;
1449 			/*
1450 			 * When the system page size is less than the "verify block size",
1451 			 * the UPL passed may not consist solely of absent pages.
1452 			 * We have to detect the "absent" pages and only decompress
1453 			 * into those absent/invalid page ranges.
1454 			 *
1455 			 * Things that will change in each iteration of the loop :
1456 			 *
1457 			 * l_pl_offset = where we are inside the UPL [0, caller_upl_created_size)
1458 			 * l_uplPos = the file offset the l_pl_offset corresponds to.
1459 			 * l_uplSize = the size of the upl still unprocessed;
1460 			 *
1461 			 * In this picture, we have to do the transfer on 2 ranges
1462 			 * (One 2 page range and one 3 page range) and the loop
1463 			 * below will skip the first two pages and then identify
1464 			 * the next two as invalid and fill those in and
1465 			 * then skip the next one and then do the last pages.
1466 			 *
1467 			 *                          uplPos(file_offset)
1468 			 *                            |   uplSize
1469 			 * 0                          V<-------------->    file_size
1470 			 * |--------------------------------------------------->
1471 			 *                        | | |V|V|I|I|V|I|I|I|
1472 			 *                            ^
1473 			 *                            |    upl
1474 			 *                        <------------------->
1475 			 *                            |
1476 			 *                          pl_offset
1477 			 *
1478 			 * uplSize will be clipped in case the UPL range exceeds
1479 			 * the file size.
1480 			 *
1481 			 */
1482 			while (l_uplSize) {
1483 				uint64_t l_did_read = 0;
1484 				int pl_offset_pg = (int)(l_pl_offset / PAGE_SIZE);
1485 				int pages_left_in_upl;
1486 				int start_pg;
1487 				int last_pg;
1488 
1489 				/*
1490 				 * l_uplSize may start off less than the size of the upl,
1491 				 * we have to round it up to PAGE_SIZE to calculate
1492 				 * how many more pages are left.
1493 				 */
1494 				pages_left_in_upl = (int)(round_page((vm_offset_t)l_uplSize) / PAGE_SIZE);
1495 
1496 				/*
1497 				 * scan from the beginning of the upl looking for the first
1498 				 * non-valid page.... this will become the first page in
1499 				 * the request we're going to make to
1500 				 * 'decmpfs_fetch_uncompressed_data'... if all
1501 				 * of the pages are valid, we won't call through
1502 				 * to 'decmpfs_fetch_uncompressed_data'
1503 				 */
1504 				for (start_pg = 0; start_pg < pages_left_in_upl; start_pg++) {
1505 					if (!upl_valid_page(pl_info, pl_offset_pg + start_pg)) {
1506 						break;
1507 					}
1508 				}
1509 
1510 				num_valid_pages += start_pg;
1511 
1512 				/*
1513 				 * scan from the starting invalid page looking for
1514 				 * a valid page before the end of the upl is
1515 				 * reached, if we find one, then it will be the
1516 				 * last page of the request to 'decmpfs_fetch_uncompressed_data'
1517 				 */
1518 				for (last_pg = start_pg; last_pg < pages_left_in_upl; last_pg++) {
1519 					if (upl_valid_page(pl_info, pl_offset_pg + last_pg)) {
1520 						break;
1521 					}
1522 				}
1523 
1524 				if (start_pg < last_pg) {
1525 					off_t inval_offset = start_pg * PAGE_SIZE;
1526 					int inval_pages = last_pg - start_pg;
1527 					int inval_size = inval_pages * PAGE_SIZE;
1528 					decmpfs_vector l_vec;
1529 
1530 					num_invalid_pages += inval_pages;
1531 					if (inval_offset) {
1532 						did_read += inval_offset;
1533 						l_pl_offset += inval_offset;
1534 						l_uplPos += inval_offset;
1535 						l_uplSize -= inval_offset;
1536 					}
1537 
1538 					l_vec = (decmpfs_vector) {
1539 						.buf = (char*)data + l_pl_offset,
1540 						.size = inval_size,
1541 					};
1542 
1543 					err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, l_uplPos,
1544 					    MIN(l_uplSize, inval_size), 1, &l_vec, &l_did_read);
1545 
1546 					if (!err && (l_did_read != inval_size) && (l_uplSize > inval_size)) {
1547 						ErrorLogWithPath("Unexpected size fetch of decompressed data, l_uplSize = %d, l_did_read = %d, inval_size = %d\n",
1548 						    (int)l_uplSize, (int)l_did_read, (int)inval_size);
1549 						err = EINVAL;
1550 					}
1551 				} else {
1552 					/* no invalid pages left */
1553 					l_did_read = l_uplSize;
1554 					if (!file_tail_page_valid) {
1555 						file_tail_page_valid = true;
1556 					}
1557 				}
1558 
1559 				if (err) {
1560 					break;
1561 				}
1562 
1563 				did_read += l_did_read;
1564 				l_pl_offset += l_did_read;
1565 				l_uplPos += l_did_read;
1566 				l_uplSize -= l_did_read;
1567 			}
1568 
1569 			/* Zero out the region after EOF in the last page (if needed) */
1570 			if (!err && !file_tail_page_valid && (uplSize < rounded_uplSize)) {
1571 				memset((char*)vec.buf + uplSize, 0, (size_t)(rounded_uplSize - uplSize));
1572 			}
1573 		}
1574 	}
1575 	if (err) {
1576 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_FETCH_UNCOMPRESSED_DATA_FAILED, err)
1577 		DebugLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err);
1578 		int cmp_state = decmpfs_fast_get_state(cp);
1579 		if (cmp_state == FILE_IS_CONVERTING) {
1580 			DebugLogWithPath("cmp_state == FILE_IS_CONVERTING\n");
1581 			cmp_state = wait_for_decompress(cp);
1582 			if (cmp_state == FILE_IS_COMPRESSED) {
1583 				DebugLogWithPath("cmp_state == FILE_IS_COMPRESSED\n");
1584 				/* a decompress was attempted but it failed, let's try calling fetch again */
1585 				goto decompress;
1586 			}
1587 		}
1588 		if (cmp_state == FILE_IS_NOT_COMPRESSED) {
1589 			DebugLogWithPath("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1590 			/* the file was decompressed after we started reading it */
1591 			*is_compressed = 0; /* instruct caller to fall back to its normal path */
1592 		}
1593 	}
1594 
1595 	if (!err && verify_block_size) {
1596 		size_t cur_verify_block_size = verify_block_size;
1597 
1598 		if ((err = VNOP_VERIFY(vp, uplPos, vec.buf, rounded_uplSize, &cur_verify_block_size, NULL, 0, NULL))) {
1599 			ErrorLogWithPath("Verification failed with error %d, uplPos = %lld, uplSize = %d, did_read = %d, valid_pages = %d, invalid_pages = %d, tail_page_valid = %d\n",
1600 			    err, (long long)uplPos, (int)rounded_uplSize, (int)did_read, num_valid_pages, num_invalid_pages, file_tail_page_valid);
1601 		}
1602 		/* XXX : If the verify block size changes, redo the read */
1603 	}
1604 
1605 #if CONFIG_IOSCHED
1606 	upl_unmark_decmp(pl);
1607 #endif /* CONFIG_IOSCHED */
1608 
1609 	kr = ubc_upl_unmap(pl); data = NULL; /* make sure to set data to NULL so we don't try to unmap again below */
1610 	if (kr != KERN_SUCCESS) {
1611 		ErrorLogWithPath("ubc_upl_unmap error %d\n", (int)kr);
1612 	} else {
1613 		if (!err) {
1614 			/* commit our pages */
1615 			kr = commit_upl(pl, pl_offset, (size_t)rounded_uplSize, UPL_COMMIT_FREE_ON_EMPTY, 0 /* commit */);
1616 			/* If there were any pages after the page containing EOF, abort them. */
1617 			if (rounded_uplSize < size) {
1618 				kr = commit_upl(pl, (upl_offset_t)(pl_offset + rounded_uplSize), (size_t)(size - rounded_uplSize),
1619 				    UPL_ABORT_FREE_ON_EMPTY | UPL_ABORT_ERROR, 1 /* abort */);
1620 			}
1621 		}
1622 	}
1623 
1624 out:
1625 	if (data) {
1626 		ubc_upl_unmap(pl);
1627 	}
1628 	if (hdr != NULL) {
1629 		kfree_data(hdr, alloc_size);
1630 	}
1631 	if (cmpdata_locked) {
1632 		decmpfs_unlock_compressed_data(cp, 0);
1633 	}
1634 	if (err) {
1635 #if 0
1636 		if (err != ENXIO && err != ENOSPC) {
1637 			char *path = zalloc(ZV_NAMEI);
1638 			panic("%s: decmpfs_pagein_compressed: err %d", vnpath(vp, path, PATH_MAX), err);
1639 			zfree(ZV_NAMEI, path);
1640 		}
1641 #endif /* 0 */
1642 		ErrorLogWithPath("err %d\n", err);
1643 	}
1644 	return err;
1645 }
1646 
1647 errno_t
1648 decmpfs_read_compressed(struct vnop_read_args *ap, int *is_compressed, decmpfs_cnode *cp)
1649 {
1650 	/* handles a read request from vfs for a compressed file */
1651 
1652 	uio_t uio                    = ap->a_uio;
1653 	vnode_t vp                   = ap->a_vp;
1654 	int err                      = 0;
1655 	int countInt                 = 0;
1656 	off_t uplPos                 = 0;
1657 	user_ssize_t uplSize         = 0;
1658 	user_ssize_t uplRemaining    = 0;
1659 	off_t curUplPos              = 0;
1660 	user_ssize_t curUplSize      = 0;
1661 	kern_return_t kr             = KERN_SUCCESS;
1662 	int abort_read               = 0;
1663 	void *data                   = NULL;
1664 	uint64_t did_read            = 0;
1665 	upl_t upl                    = NULL;
1666 	upl_page_info_t *pli         = NULL;
1667 	decmpfs_header *hdr          = NULL;
1668 	size_t alloc_size            = 0;
1669 	uint64_t cachedSize          = 0;
1670 	off_t uioPos                 = 0;
1671 	user_ssize_t uioRemaining    = 0;
1672 	size_t verify_block_size     = 0;
1673 	size_t alignment_size        = PAGE_SIZE;
1674 	int cmpdata_locked           = 0;
1675 
1676 	decmpfs_lock_compressed_data(cp, 0); cmpdata_locked = 1;
1677 
1678 	uplPos = uio_offset(uio);
1679 	uplSize = uio_resid(uio);
1680 	VerboseLogWithPath("uplPos %lld uplSize %lld\n", uplPos, uplSize);
1681 
1682 	cachedSize = decmpfs_cnode_get_vnode_cached_size(cp);
1683 
1684 	if ((uint64_t)uplPos + uplSize > cachedSize) {
1685 		/* truncate the read to the size of the file */
1686 		uplSize = (user_ssize_t)(cachedSize - uplPos);
1687 	}
1688 
1689 	/* give the cluster layer a chance to fill in whatever it already has */
1690 	countInt = (uplSize > INT_MAX) ? INT_MAX : (int)uplSize;
1691 	err = cluster_copy_ubc_data(vp, uio, &countInt, 0);
1692 	if (err != 0) {
1693 		goto out;
1694 	}
1695 
1696 	/* figure out what's left */
1697 	uioPos = uio_offset(uio);
1698 	uioRemaining = uio_resid(uio);
1699 	if ((uint64_t)uioPos + uioRemaining > cachedSize) {
1700 		/* truncate the read to the size of the file */
1701 		uioRemaining = (user_ssize_t)(cachedSize - uioPos);
1702 	}
1703 
1704 	if (uioRemaining <= 0) {
1705 		/* nothing left */
1706 		goto out;
1707 	}
1708 
1709 	err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0, &alloc_size);
1710 	if (err != 0) {
1711 		goto out;
1712 	}
1713 	if (!compression_type_valid(vp, hdr)) {
1714 		err = ENOTSUP;
1715 		goto out;
1716 	}
1717 
1718 	uplPos = uioPos;
1719 	uplSize = uioRemaining;
1720 #if COMPRESSION_DEBUG
1721 	DebugLogWithPath("uplPos %lld uplSize %lld\n", (uint64_t)uplPos, (uint64_t)uplSize);
1722 #endif
1723 
1724 	lck_rw_lock_shared(&decompressorsLock);
1725 	decmpfs_adjust_fetch_region_func adjust_fetch = decmp_get_func(vp, hdr->compression_type, adjust_fetch);
1726 	if (adjust_fetch) {
1727 		/* give the compressor a chance to adjust the portion of the file that we read */
1728 		adjust_fetch(vp, decmpfs_ctx, hdr, &uplPos, &uplSize);
1729 		VerboseLogWithPath("adjusted uplPos %lld uplSize %lld\n", (uint64_t)uplPos, (uint64_t)uplSize);
1730 	}
1731 	lck_rw_unlock_shared(&decompressorsLock);
1732 
1733 	/* clip the adjusted size to the size of the file */
1734 	if ((uint64_t)uplPos + uplSize > cachedSize) {
1735 		/* truncate the read to the size of the file */
1736 		uplSize = (user_ssize_t)(cachedSize - uplPos);
1737 	}
1738 
1739 	if (uplSize <= 0) {
1740 		/* nothing left */
1741 		goto out;
1742 	}
1743 
1744 	/*
1745 	 *  since we're going to create a upl for the given region of the file,
1746 	 *  make sure we're on page boundaries
1747 	 */
1748 
1749 	/* If the verify block size is larger than the page size, the UPL needs to aligned to it */
1750 	err = VNOP_VERIFY(vp, uplPos, NULL, 0, &verify_block_size, NULL, VNODE_VERIFY_DEFAULT, NULL);
1751 	if (err) {
1752 		goto out;
1753 	} else if (verify_block_size) {
1754 		if (verify_block_size & (verify_block_size - 1)) {
1755 			ErrorLogWithPath("verify block size is not power of 2, no verification will be done\n");
1756 			verify_block_size = 0;
1757 		} else if (verify_block_size > PAGE_SIZE) {
1758 			alignment_size = verify_block_size;
1759 		}
1760 	}
1761 
1762 	if (uplPos & (alignment_size - 1)) {
1763 		/* round position down to page boundary */
1764 		uplSize += (uplPos & (alignment_size - 1));
1765 		uplPos &= ~(alignment_size - 1);
1766 	}
1767 
1768 	/* round size up to alignement_size multiple */
1769 	uplSize = (uplSize + (alignment_size - 1)) & ~(alignment_size - 1);
1770 
1771 	VerboseLogWithPath("new uplPos %lld uplSize %lld\n", (uint64_t)uplPos, (uint64_t)uplSize);
1772 
1773 	uplRemaining = uplSize;
1774 	curUplPos = uplPos;
1775 	curUplSize = 0;
1776 
1777 	while (uplRemaining > 0) {
1778 		/* start after the last upl */
1779 		curUplPos += curUplSize;
1780 
1781 		/* clip to max upl size */
1782 		curUplSize = uplRemaining;
1783 		if (curUplSize > MAX_UPL_SIZE_BYTES) {
1784 			curUplSize = MAX_UPL_SIZE_BYTES;
1785 		}
1786 
1787 		/* create the upl */
1788 		kr = ubc_create_upl_kernel(vp, curUplPos, (int)curUplSize, &upl, &pli, UPL_SET_LITE, VM_KERN_MEMORY_FILE);
1789 		if (kr != KERN_SUCCESS) {
1790 			ErrorLogWithPath("ubc_create_upl error %d\n", (int)kr);
1791 			err = EINVAL;
1792 			goto out;
1793 		}
1794 		VerboseLogWithPath("curUplPos %lld curUplSize %lld\n", (uint64_t)curUplPos, (uint64_t)curUplSize);
1795 
1796 #if CONFIG_IOSCHED
1797 		/* Mark the UPL as the requesting UPL for decompression */
1798 		upl_mark_decmp(upl);
1799 #endif /* CONFIG_IOSCHED */
1800 
1801 		/* map the upl */
1802 		kr = ubc_upl_map(upl, (vm_offset_t*)&data);
1803 		if (kr != KERN_SUCCESS) {
1804 			commit_upl(upl, 0, curUplSize, UPL_ABORT_FREE_ON_EMPTY, 1);
1805 #if 0
1806 			char *path = zalloc(ZV_NAMEI);
1807 			panic("%s: decmpfs_read_compressed: ubc_upl_map error %d", vnpath(vp, path, PATH_MAX), (int)kr);
1808 			zfree(ZV_NAMEI, path);
1809 #else /* 0 */
1810 			ErrorLogWithPath("ubc_upl_map kr=0x%x\n", (int)kr);
1811 #endif /* 0 */
1812 			err = EINVAL;
1813 			goto out;
1814 		}
1815 
1816 		/* make sure the map succeeded */
1817 		if (!data) {
1818 			commit_upl(upl, 0, curUplSize, UPL_ABORT_FREE_ON_EMPTY, 1);
1819 
1820 			ErrorLogWithPath("ubc_upl_map mapped null\n");
1821 			err = EINVAL;
1822 			goto out;
1823 		}
1824 
1825 		/* fetch uncompressed data into the mapped upl */
1826 		decmpfs_vector vec;
1827 decompress:
1828 		vec = (decmpfs_vector){ .buf = data, .size = curUplSize };
1829 		err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, curUplPos, curUplSize, 1, &vec, &did_read);
1830 		if (err) {
1831 			ErrorLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err);
1832 
1833 			/* maybe the file is converting to decompressed */
1834 			int cmp_state = decmpfs_fast_get_state(cp);
1835 			if (cmp_state == FILE_IS_CONVERTING) {
1836 				ErrorLogWithPath("cmp_state == FILE_IS_CONVERTING\n");
1837 				cmp_state = wait_for_decompress(cp);
1838 				if (cmp_state == FILE_IS_COMPRESSED) {
1839 					ErrorLogWithPath("cmp_state == FILE_IS_COMPRESSED\n");
1840 					/* a decompress was attempted but it failed, let's try fetching again */
1841 					goto decompress;
1842 				}
1843 			}
1844 			if (cmp_state == FILE_IS_NOT_COMPRESSED) {
1845 				ErrorLogWithPath("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1846 				/* the file was decompressed after we started reading it */
1847 				abort_read = 1; /* we're not going to commit our data */
1848 				*is_compressed = 0; /* instruct caller to fall back to its normal path */
1849 			}
1850 			kr = KERN_FAILURE;
1851 			did_read = 0;
1852 		}
1853 
1854 		/* zero out the remainder of the last page */
1855 		memset((char*)data + did_read, 0, (size_t)(curUplSize - did_read));
1856 		if (!err && verify_block_size) {
1857 			size_t cur_verify_block_size = verify_block_size;
1858 
1859 			if ((err = VNOP_VERIFY(vp, curUplPos, data, curUplSize, &cur_verify_block_size, NULL, 0, NULL))) {
1860 				ErrorLogWithPath("Verification failed with error %d\n", err);
1861 				abort_read = 1;
1862 			}
1863 			/* XXX : If the verify block size changes, redo the read */
1864 		}
1865 
1866 		kr = ubc_upl_unmap(upl);
1867 		if (kr == KERN_SUCCESS) {
1868 			if (abort_read) {
1869 				kr = commit_upl(upl, 0, curUplSize, UPL_ABORT_FREE_ON_EMPTY, 1);
1870 			} else {
1871 				VerboseLogWithPath("uioPos %lld uioRemaining %lld\n", (uint64_t)uioPos, (uint64_t)uioRemaining);
1872 				if (uioRemaining) {
1873 					off_t uplOff = uioPos - curUplPos;
1874 					if (uplOff < 0) {
1875 						ErrorLogWithPath("uplOff %lld should never be negative\n", (int64_t)uplOff);
1876 						err = EINVAL;
1877 					} else if (uplOff > INT_MAX) {
1878 						ErrorLogWithPath("uplOff %lld too large\n", (int64_t)uplOff);
1879 						err = EINVAL;
1880 					} else {
1881 						off_t count = curUplPos + curUplSize - uioPos;
1882 						if (count < 0) {
1883 							/* this upl is entirely before the uio */
1884 						} else {
1885 							if (count > uioRemaining) {
1886 								count = uioRemaining;
1887 							}
1888 							int icount = (count > INT_MAX) ? INT_MAX : (int)count;
1889 							int io_resid = icount;
1890 							err = cluster_copy_upl_data(uio, upl, (int)uplOff, &io_resid);
1891 							int copied = icount - io_resid;
1892 							VerboseLogWithPath("uplOff %lld count %lld copied %lld\n", (uint64_t)uplOff, (uint64_t)count, (uint64_t)copied);
1893 							if (err) {
1894 								ErrorLogWithPath("cluster_copy_upl_data err %d\n", err);
1895 							}
1896 							uioPos += copied;
1897 							uioRemaining -= copied;
1898 						}
1899 					}
1900 				}
1901 				kr = commit_upl(upl, 0, curUplSize, UPL_COMMIT_FREE_ON_EMPTY | UPL_COMMIT_INACTIVATE, 0);
1902 				if (err) {
1903 					goto out;
1904 				}
1905 			}
1906 		} else {
1907 			ErrorLogWithPath("ubc_upl_unmap error %d\n", (int)kr);
1908 		}
1909 
1910 		uplRemaining -= curUplSize;
1911 	}
1912 
1913 out:
1914 
1915 	if (hdr != NULL) {
1916 		kfree_data(hdr, alloc_size);
1917 	}
1918 	if (cmpdata_locked) {
1919 		decmpfs_unlock_compressed_data(cp, 0);
1920 	}
1921 	if (err) {/* something went wrong */
1922 		ErrorLogWithPath("err %d\n", err);
1923 		return err;
1924 	}
1925 
1926 #if COMPRESSION_DEBUG
1927 	uplSize = uio_resid(uio);
1928 	if (uplSize) {
1929 		VerboseLogWithPath("still %lld bytes to copy\n", uplSize);
1930 	}
1931 #endif
1932 	return 0;
1933 }
1934 
1935 int
1936 decmpfs_free_compressed_data(vnode_t vp, decmpfs_cnode *cp)
1937 {
1938 	/*
1939 	 *  call out to the decompressor to free remove any data associated with this compressed file
1940 	 *  then delete the file's compression xattr
1941 	 */
1942 	decmpfs_header *hdr = NULL;
1943 	size_t alloc_size = 0;
1944 
1945 	/*
1946 	 * Trace the following parameters on entry with event-id 0x03120010.
1947 	 *
1948 	 * @vp->v_id:       vnode-id of the file for which to free compressed data.
1949 	 */
1950 	DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FREE_COMPRESSED_DATA, vp->v_id);
1951 
1952 	int err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0, &alloc_size);
1953 	if (err) {
1954 		ErrorLogWithPath("decmpfs_fetch_compressed_header err %d\n", err);
1955 	} else {
1956 		lck_rw_lock_shared(&decompressorsLock);
1957 		decmpfs_free_compressed_data_func free_data = decmp_get_func(vp, hdr->compression_type, free_data);
1958 		if (free_data) {
1959 			err = free_data(vp, decmpfs_ctx, hdr);
1960 		} else {
1961 			/* nothing to do, so no error */
1962 			err = 0;
1963 		}
1964 		lck_rw_unlock_shared(&decompressorsLock);
1965 
1966 		if (err != 0) {
1967 			ErrorLogWithPath("decompressor err %d\n", err);
1968 		}
1969 	}
1970 	/*
1971 	 * Trace the following parameters on return with event-id 0x03120010.
1972 	 *
1973 	 * @vp->v_id:       vnode-id of the file for which to free compressed data.
1974 	 * @err:            value returned from this function.
1975 	 */
1976 	DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FREE_COMPRESSED_DATA, vp->v_id, err);
1977 
1978 	/* delete the xattr */
1979 	err = vn_removexattr(vp, DECMPFS_XATTR_NAME, 0, decmpfs_ctx);
1980 
1981 	if (hdr != NULL) {
1982 		kfree_data(hdr, alloc_size);
1983 	}
1984 	return err;
1985 }
1986 
1987 #pragma mark --- file conversion routines ---
1988 
1989 static int
1990 unset_compressed_flag(vnode_t vp)
1991 {
1992 	int err = 0;
1993 	struct vnode_attr va;
1994 	struct fsioc_cas_bsdflags cas;
1995 	int i;
1996 
1997 # define MAX_CAS_BSDFLAGS_LOOPS 4
1998 	/* UF_COMPRESSED should be manipulated only with FSIOC_CAS_BSDFLAGS */
1999 	for (i = 0; i < MAX_CAS_BSDFLAGS_LOOPS; i++) {
2000 		VATTR_INIT(&va);
2001 		VATTR_WANTED(&va, va_flags);
2002 		err = vnode_getattr(vp, &va, decmpfs_ctx);
2003 		if (err != 0) {
2004 			ErrorLogWithPath("vnode_getattr err %d, num retries %d\n", err, i);
2005 			goto out;
2006 		}
2007 
2008 		cas.expected_flags = va.va_flags;
2009 		cas.new_flags = va.va_flags & ~UF_COMPRESSED;
2010 		err = VNOP_IOCTL(vp, FSIOC_CAS_BSDFLAGS, (caddr_t)&cas, FWRITE, decmpfs_ctx);
2011 
2012 		if ((err == 0) && (va.va_flags == cas.actual_flags)) {
2013 			goto out;
2014 		}
2015 
2016 		if ((err != 0) && (err != EAGAIN)) {
2017 			break;
2018 		}
2019 	}
2020 
2021 	/* fallback to regular chflags if FSIOC_CAS_BSDFLAGS is not supported */
2022 	if (err == ENOTTY) {
2023 		VATTR_INIT(&va);
2024 		VATTR_SET(&va, va_flags, cas.new_flags);
2025 		err = vnode_setattr(vp, &va, decmpfs_ctx);
2026 		if (err != 0) {
2027 			ErrorLogWithPath("vnode_setattr err %d\n", err);
2028 		}
2029 	} else if (va.va_flags != cas.actual_flags) {
2030 		ErrorLogWithPath("FSIOC_CAS_BSDFLAGS err: flags mismatc. actual (%x) expected (%x), num retries %d\n", cas.actual_flags, va.va_flags, i);
2031 	} else if (err != 0) {
2032 		ErrorLogWithPath("FSIOC_CAS_BSDFLAGS err %d, num retries %d\n", err, i);
2033 	}
2034 
2035 out:
2036 	return err;
2037 }
2038 
2039 int
2040 decmpfs_decompress_file(vnode_t vp, decmpfs_cnode *cp, off_t toSize, int truncate_okay, int skiplock)
2041 {
2042 	/* convert a compressed file to an uncompressed file */
2043 
2044 	int err                      = 0;
2045 	char *data                   = NULL;
2046 	uio_t uio_w                  = 0;
2047 	off_t offset                 = 0;
2048 	uint32_t old_state           = 0;
2049 	uint32_t new_state           = 0;
2050 	int update_file_state        = 0;
2051 	size_t allocSize             = 0;
2052 	decmpfs_header *hdr          = NULL;
2053 	size_t hdr_size              = 0;
2054 	int cmpdata_locked           = 0;
2055 	off_t remaining              = 0;
2056 	uint64_t uncompressed_size   = 0;
2057 
2058 	/*
2059 	 * Trace the following parameters on entry with event-id 0x03120000.
2060 	 *
2061 	 * @vp->v_id:		vnode-id of the file being decompressed.
2062 	 * @toSize:		uncompress given bytes of the file.
2063 	 * @truncate_okay:	on error it is OK to truncate.
2064 	 * @skiplock:		compressed data is locked, skip locking again.
2065 	 *
2066 	 * Please NOTE: @toSize can overflow in theory but here it is safe.
2067 	 */
2068 	DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_DECOMPRESS_FILE, vp->v_id,
2069 	    (int)toSize, truncate_okay, skiplock);
2070 
2071 	if (!skiplock) {
2072 		decmpfs_lock_compressed_data(cp, 1); cmpdata_locked = 1;
2073 	}
2074 
2075 decompress:
2076 	old_state = decmpfs_fast_get_state(cp);
2077 
2078 	switch (old_state) {
2079 	case FILE_IS_NOT_COMPRESSED:
2080 	{
2081 		/* someone else decompressed the file */
2082 		err = 0;
2083 		goto out;
2084 	}
2085 
2086 	case FILE_TYPE_UNKNOWN:
2087 	{
2088 		/* the file is in an unknown state, so update the state and retry */
2089 		(void)decmpfs_file_is_compressed(vp, cp);
2090 
2091 		/* try again */
2092 		goto decompress;
2093 	}
2094 
2095 	case FILE_IS_COMPRESSED:
2096 	{
2097 		/* the file is compressed, so decompress it */
2098 		break;
2099 	}
2100 
2101 	default:
2102 	{
2103 		/*
2104 		 *  this shouldn't happen since multiple calls to decmpfs_decompress_file lock each other out,
2105 		 *  and when decmpfs_decompress_file returns, the state should be always be set back to
2106 		 *  FILE_IS_NOT_COMPRESSED or FILE_IS_UNKNOWN
2107 		 */
2108 		err = EINVAL;
2109 		goto out;
2110 	}
2111 	}
2112 
2113 	err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0, &hdr_size);
2114 	if (err != 0) {
2115 		goto out;
2116 	}
2117 
2118 	uncompressed_size = hdr->uncompressed_size;
2119 	if (toSize == -1) {
2120 		toSize = hdr->uncompressed_size;
2121 	}
2122 
2123 	if (toSize == 0) {
2124 		/* special case truncating the file to zero bytes */
2125 		goto nodecmp;
2126 	} else if ((uint64_t)toSize > hdr->uncompressed_size) {
2127 		/* the caller is trying to grow the file, so we should decompress all the data */
2128 		toSize = hdr->uncompressed_size;
2129 	}
2130 
2131 	allocSize = MIN(64 * 1024, (size_t)toSize);
2132 	data = (char *)kalloc_data(allocSize, Z_WAITOK);
2133 	if (!data) {
2134 		err = ENOMEM;
2135 		goto out;
2136 	}
2137 
2138 	uio_w = uio_create(1, 0LL, UIO_SYSSPACE, UIO_WRITE);
2139 	if (!uio_w) {
2140 		err = ENOMEM;
2141 		goto out;
2142 	}
2143 	uio_w->uio_flags |= UIO_FLAGS_IS_COMPRESSED_FILE;
2144 
2145 	remaining = toSize;
2146 
2147 	/* tell the buffer cache that this is an empty file */
2148 	ubc_setsize(vp, 0);
2149 
2150 	/* if we got here, we need to decompress the file */
2151 	decmpfs_cnode_set_vnode_state(cp, FILE_IS_CONVERTING, 1);
2152 
2153 	while (remaining > 0) {
2154 		/* loop decompressing data from the file and writing it into the data fork */
2155 
2156 		uint64_t bytes_read = 0;
2157 		decmpfs_vector vec = { .buf = data, .size = (user_ssize_t)MIN(allocSize, remaining) };
2158 		err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, offset, vec.size, 1, &vec, &bytes_read);
2159 		if (err != 0) {
2160 			ErrorLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err);
2161 			goto out;
2162 		}
2163 
2164 		if (bytes_read == 0) {
2165 			/* we're done reading data */
2166 			break;
2167 		}
2168 
2169 		uio_reset(uio_w, offset, UIO_SYSSPACE, UIO_WRITE);
2170 		err = uio_addiov(uio_w, CAST_USER_ADDR_T(data), (user_size_t)bytes_read);
2171 		if (err != 0) {
2172 			ErrorLogWithPath("uio_addiov err %d\n", err);
2173 			err = ENOMEM;
2174 			goto out;
2175 		}
2176 
2177 		err = VNOP_WRITE(vp, uio_w, 0, decmpfs_ctx);
2178 		if (err != 0) {
2179 			/* if the write failed, truncate the file to zero bytes */
2180 			ErrorLogWithPath("VNOP_WRITE err %d\n", err);
2181 			break;
2182 		}
2183 		offset += bytes_read;
2184 		remaining -= bytes_read;
2185 	}
2186 
2187 	if (err == 0) {
2188 		if (offset != toSize) {
2189 			ErrorLogWithPath("file decompressed to %lld instead of %lld\n", offset, toSize);
2190 			err = EINVAL;
2191 			goto out;
2192 		}
2193 	}
2194 
2195 	if (err == 0) {
2196 		/* sync the data and metadata */
2197 		err = VNOP_FSYNC(vp, MNT_WAIT, decmpfs_ctx);
2198 		if (err != 0) {
2199 			ErrorLogWithPath("VNOP_FSYNC err %d\n", err);
2200 			goto out;
2201 		}
2202 	}
2203 
2204 	if (err != 0) {
2205 		/* write, setattr, or fsync failed */
2206 		ErrorLogWithPath("aborting decompress, err %d\n", err);
2207 		if (truncate_okay) {
2208 			/* truncate anything we might have written */
2209 			int error = vnode_setsize(vp, 0, 0, decmpfs_ctx);
2210 			ErrorLogWithPath("vnode_setsize err %d\n", error);
2211 		}
2212 		goto out;
2213 	}
2214 
2215 nodecmp:
2216 	/* if we're truncating the file to zero bytes, we'll skip ahead to here */
2217 
2218 	/* unset the compressed flag */
2219 	unset_compressed_flag(vp);
2220 
2221 	/* free the compressed data associated with this file */
2222 	err = decmpfs_free_compressed_data(vp, cp);
2223 	if (err != 0) {
2224 		ErrorLogWithPath("decmpfs_free_compressed_data err %d\n", err);
2225 	}
2226 
2227 	/*
2228 	 *  even if free_compressed_data or vnode_getattr/vnode_setattr failed, return success
2229 	 *  since we succeeded in writing all of the file data to the data fork
2230 	 */
2231 	err = 0;
2232 
2233 	/* if we got this far, the file was successfully decompressed */
2234 	update_file_state = 1;
2235 	new_state = FILE_IS_NOT_COMPRESSED;
2236 
2237 #if COMPRESSION_DEBUG
2238 	{
2239 		uint64_t filesize = 0;
2240 		vnsize(vp, &filesize);
2241 		DebugLogWithPath("new file size %lld\n", filesize);
2242 	}
2243 #endif
2244 
2245 out:
2246 	if (hdr != NULL) {
2247 		kfree_data(hdr, hdr_size);
2248 	}
2249 	kfree_data(data, allocSize);
2250 
2251 	if (uio_w) {
2252 		uio_free(uio_w);
2253 	}
2254 
2255 	if (err != 0) {
2256 		/* if there was a failure, reset compression flags to unknown and clear the buffer cache data */
2257 		update_file_state = 1;
2258 		new_state = FILE_TYPE_UNKNOWN;
2259 		if (uncompressed_size) {
2260 			ubc_setsize(vp, 0);
2261 			ubc_setsize(vp, uncompressed_size);
2262 		}
2263 	}
2264 
2265 	if (update_file_state) {
2266 		lck_mtx_lock(&decompress_channel_mtx);
2267 		decmpfs_cnode_set_vnode_state(cp, new_state, 1);
2268 		wakeup((caddr_t)&decompress_channel); /* wake up anyone who might have been waiting for decompression */
2269 		lck_mtx_unlock(&decompress_channel_mtx);
2270 	}
2271 
2272 	if (cmpdata_locked) {
2273 		decmpfs_unlock_compressed_data(cp, 1);
2274 	}
2275 	/*
2276 	 * Trace the following parameters on return with event-id 0x03120000.
2277 	 *
2278 	 * @vp->v_id:	vnode-id of the file being decompressed.
2279 	 * @err:	value returned from this function.
2280 	 */
2281 	DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_DECOMPRESS_FILE, vp->v_id, err);
2282 	return err;
2283 }
2284 
2285 #pragma mark --- Type1 compressor ---
2286 
2287 /*
2288  *  The "Type1" compressor stores the data fork directly in the compression xattr
2289  */
2290 
2291 static int
2292 decmpfs_validate_compressed_file_Type1(__unused vnode_t vp, __unused vfs_context_t ctx, decmpfs_header *hdr)
2293 {
2294 	int err          = 0;
2295 
2296 	if (hdr->uncompressed_size + sizeof(decmpfs_disk_header) != (uint64_t)hdr->attr_size) {
2297 		err = EINVAL;
2298 		goto out;
2299 	}
2300 out:
2301 	return err;
2302 }
2303 
2304 static int
2305 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)
2306 {
2307 	int err          = 0;
2308 	int i;
2309 	user_ssize_t remaining;
2310 
2311 	if (hdr->uncompressed_size + sizeof(decmpfs_disk_header) != (uint64_t)hdr->attr_size) {
2312 		err = EINVAL;
2313 		goto out;
2314 	}
2315 
2316 #if COMPRESSION_DEBUG
2317 	static int dummy = 0; // prevent syslog from coalescing printfs
2318 	DebugLogWithPath("%d memcpy %lld at %lld\n", dummy++, size, (uint64_t)offset);
2319 #endif
2320 
2321 	remaining = size;
2322 	for (i = 0; (i < nvec) && (remaining > 0); i++) {
2323 		user_ssize_t curCopy = vec[i].size;
2324 		if (curCopy > remaining) {
2325 			curCopy = remaining;
2326 		}
2327 		memcpy(vec[i].buf, hdr->attr_bytes + offset, curCopy);
2328 		offset += curCopy;
2329 		remaining -= curCopy;
2330 	}
2331 
2332 	if ((bytes_read) && (err == 0)) {
2333 		*bytes_read = (size - remaining);
2334 	}
2335 
2336 out:
2337 	return err;
2338 }
2339 
2340 SECURITY_READ_ONLY_EARLY(static decmpfs_registration) Type1Reg =
2341 {
2342 	.decmpfs_registration = DECMPFS_REGISTRATION_VERSION,
2343 	.validate          = decmpfs_validate_compressed_file_Type1,
2344 	.adjust_fetch      = NULL,/* no adjust necessary */
2345 	.fetch             = decmpfs_fetch_uncompressed_data_Type1,
2346 	.free_data         = NULL,/* no free necessary */
2347 	.get_flags         = NULL/* no flags */
2348 };
2349 
2350 #pragma mark --- decmpfs initialization ---
2351 
2352 void
2353 decmpfs_init(void)
2354 {
2355 	static int done = 0;
2356 	if (done) {
2357 		return;
2358 	}
2359 
2360 	decmpfs_ctx = vfs_context_create(vfs_context_kernel());
2361 
2362 	register_decmpfs_decompressor(CMP_Type1, &Type1Reg);
2363 
2364 	ktriage_register_subsystem_strings(KDBG_TRIAGE_SUBSYS_DECMPFS, &ktriage_decmpfs_subsystem_strings);
2365 
2366 	done = 1;
2367 }
2368 #endif /* FS_COMPRESSION */
2369