xref: /xnu-11215.41.3/bsd/kern/decmpfs.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
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(uio_buf, 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 | Z_NOFAIL);
536 		hdr = (decmpfs_header*)data;
537 		hdr->attr_size = sizeof(decmpfs_disk_header);
538 		hdr->compression_magic = DECMPFS_MAGIC;
539 		hdr->compression_type  = cp->cmp_type;
540 		if (hdr->compression_type == DATALESS_PKG_CMPFS_TYPE) {
541 			if (!vnode_isdir(vp)) {
542 				err = EINVAL;
543 				goto out;
544 			}
545 			hdr->_size.value = DECMPFS_PKG_VALUE_FROM_SIZE_COUNT(
546 				decmpfs_cnode_get_vnode_cached_size(cp),
547 				decmpfs_cnode_get_vnode_cached_nchildren(cp));
548 		} else if (vnode_isdir(vp)) {
549 			hdr->_size.value = decmpfs_cnode_get_vnode_cached_nchildren(cp);
550 		} else {
551 			hdr->_size.value = decmpfs_cnode_get_vnode_cached_size(cp);
552 		}
553 	} else {
554 		/* figure out how big the xattr is on disk */
555 		err = vn_getxattr(vp, DECMPFS_XATTR_NAME, NULL, &attr_size, XATTR_NOSECURITY, decmpfs_ctx);
556 		if (err != 0) {
557 			goto out;
558 		}
559 		alloc_size = attr_size + sizeof(hdr->attr_size);
560 
561 		if (attr_size < sizeof(decmpfs_disk_header) || attr_size > MAX_DECMPFS_XATTR_SIZE) {
562 			err = EINVAL;
563 			goto out;
564 		}
565 
566 		/* allocation includes space for the extra attr_size field of a compressed_header */
567 		data = (char *)kalloc_data(alloc_size, Z_WAITOK);
568 		/*
569 		 * we know the decmpfs sizes tend to be "small", and
570 		 * vm_page_alloc_list() will actually wait for memory
571 		 * for asks of less than 1/4th of the physical memory.
572 		 *
573 		 * This is important because decmpfs failures result
574 		 * in pageins failing spuriously and eventually SIGBUS
575 		 * errors for example when faulting in TEXT.
576 		 */
577 		assert(data);
578 
579 		/* read the xattr into our buffer, skipping over the attr_size field at the beginning */
580 		attr_uio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
581 		uio_addiov(attr_uio, CAST_USER_ADDR_T(data + sizeof(hdr->attr_size)), attr_size);
582 
583 		err = vn_getxattr(vp, DECMPFS_XATTR_NAME, attr_uio, &read_size, XATTR_NOSECURITY, decmpfs_ctx);
584 		if (err != 0) {
585 			goto out;
586 		}
587 		if (read_size != attr_size) {
588 			err = EINVAL;
589 			goto out;
590 		}
591 		hdr = (decmpfs_header*)data;
592 		hdr->attr_size = (uint32_t)attr_size;
593 		/* swap the fields to native endian */
594 		hdr->compression_magic = OSSwapLittleToHostInt32(hdr->compression_magic);
595 		hdr->compression_type  = OSSwapLittleToHostInt32(hdr->compression_type);
596 		hdr->uncompressed_size = OSSwapLittleToHostInt64(hdr->uncompressed_size);
597 	}
598 
599 	if (hdr->compression_magic != DECMPFS_MAGIC) {
600 		ErrorLogWithPath("invalid compression_magic 0x%08x, should be 0x%08x\n", hdr->compression_magic, DECMPFS_MAGIC);
601 		err = EINVAL;
602 		goto out;
603 	}
604 
605 	/*
606 	 * Special-case the DATALESS compressor here; that is a valid type,
607 	 * even through there will never be an entry in the decompressor
608 	 * handler table for it.  If we don't do this, then the cmp_state
609 	 * for this cnode will end up being marked NOT_COMPRESSED, and
610 	 * we'll be stuck in limbo.
611 	 */
612 	if (hdr->compression_type >= CMP_MAX && !decmpfs_type_is_dataless(hdr->compression_type)) {
613 		if (returnInvalid) {
614 			/* return the header even though the type is out of range */
615 			err = ERANGE;
616 		} else {
617 			ErrorLogWithPath("compression_type %d out of range\n", hdr->compression_type);
618 			err = EINVAL;
619 		}
620 		goto out;
621 	}
622 
623 out:
624 	if (err && (err != ERANGE)) {
625 		DebugLogWithPath("err %d\n", err);
626 		kfree_data(data, alloc_size);
627 		*hdrOut = NULL;
628 	} else {
629 		*hdrOut = hdr;
630 		*hdr_size = alloc_size;
631 	}
632 	/*
633 	 * Trace the following parameters on return with event-id 0x03120004.
634 	 *
635 	 * @vp->v_id:       vnode-id for which to fetch compressed header.
636 	 * @err:            value returned from this function.
637 	 */
638 	DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FETCH_COMPRESSED_HEADER, vp->v_id, err);
639 	return err;
640 }
641 
642 static int
643 decmpfs_fast_get_state(decmpfs_cnode *cp)
644 {
645 	/*
646 	 *  return the cached state
647 	 *  this should *only* be called when we know that decmpfs_file_is_compressed has already been called,
648 	 *  because this implies that the cached state is valid
649 	 */
650 	int cmp_state = decmpfs_cnode_get_vnode_state(cp);
651 
652 	switch (cmp_state) {
653 	case FILE_IS_NOT_COMPRESSED:
654 	case FILE_IS_COMPRESSED:
655 	case FILE_IS_CONVERTING:
656 		return cmp_state;
657 	case FILE_TYPE_UNKNOWN:
658 		/*
659 		 *  we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
660 		 *  which should not be possible
661 		 */
662 		ErrorLog("decmpfs_fast_get_state called on unknown file\n");
663 		return FILE_IS_NOT_COMPRESSED;
664 	default:
665 		/* */
666 		ErrorLog("unknown cmp_state %d\n", cmp_state);
667 		return FILE_IS_NOT_COMPRESSED;
668 	}
669 }
670 
671 static int
672 decmpfs_fast_file_is_compressed(decmpfs_cnode *cp)
673 {
674 	int cmp_state = decmpfs_cnode_get_vnode_state(cp);
675 
676 	switch (cmp_state) {
677 	case FILE_IS_NOT_COMPRESSED:
678 		return 0;
679 	case FILE_IS_COMPRESSED:
680 	case FILE_IS_CONVERTING:
681 		return 1;
682 	case FILE_TYPE_UNKNOWN:
683 		/*
684 		 *  we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
685 		 *  which should not be possible
686 		 */
687 		ErrorLog("decmpfs_fast_get_state called on unknown file\n");
688 		return 0;
689 	default:
690 		/* */
691 		ErrorLog("unknown cmp_state %d\n", cmp_state);
692 		return 0;
693 	}
694 }
695 
696 errno_t
697 decmpfs_validate_compressed_file(vnode_t vp, decmpfs_cnode *cp)
698 {
699 	/* give a compressor a chance to indicate that a compressed file is invalid */
700 	decmpfs_header *hdr = NULL;
701 	size_t alloc_size = 0;
702 	errno_t err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0, &alloc_size);
703 
704 	if (err) {
705 		/* we couldn't get the header */
706 		if (decmpfs_fast_get_state(cp) == FILE_IS_NOT_COMPRESSED) {
707 			/* the file is no longer compressed, so return success */
708 			err = 0;
709 		}
710 		goto out;
711 	}
712 
713 	if (!decmpfs_type_is_dataless(hdr->compression_type)) {
714 		lck_rw_lock_shared(&decompressorsLock);
715 		decmpfs_validate_compressed_file_func validate = decmp_get_func(vp, hdr->compression_type, validate);
716 		if (validate) { /* make sure this validation function is valid */
717 			/* is the data okay? */
718 			err = validate(vp, decmpfs_ctx, hdr);
719 		} else if (decmp_get_func(vp, hdr->compression_type, fetch) == NULL) {
720 			/* the type isn't registered */
721 			err = EIO;
722 		} else {
723 			/* no validate registered, so nothing to do */
724 			err = 0;
725 		}
726 		lck_rw_unlock_shared(&decompressorsLock);
727 	}
728 out:
729 	if (hdr != NULL) {
730 		kfree_data(hdr, alloc_size);
731 	}
732 #if COMPRESSION_DEBUG
733 	if (err) {
734 		DebugLogWithPath("decmpfs_validate_compressed_file ret %d, vp->v_flag %d\n", err, vp->v_flag);
735 	}
736 #endif
737 	return err;
738 }
739 
740 int
741 decmpfs_file_is_compressed(vnode_t vp, decmpfs_cnode *cp)
742 {
743 	/*
744 	 *  determines whether vp points to a compressed file
745 	 *
746 	 *  to speed up this operation, we cache the result in the cnode, and do as little as possible
747 	 *  in the case where the cnode already has a valid cached state
748 	 *
749 	 */
750 
751 	int ret = 0;
752 	int error = 0;
753 	uint32_t cmp_state;
754 	struct vnode_attr va_fetch;
755 	decmpfs_header *hdr = NULL;
756 	size_t alloc_size = 0;
757 	mount_t mp = NULL;
758 	int cnode_locked = 0;
759 	int saveInvalid = 0; // save the header data even though the type was out of range
760 	uint64_t decompression_flags = 0;
761 	bool is_mounted, is_local_fs;
762 
763 	if (vnode_isnamedstream(vp)) {
764 		/*
765 		 *  named streams can't be compressed
766 		 *  since named streams of the same file share the same cnode,
767 		 *  we don't want to get/set the state in the cnode, just return 0
768 		 */
769 		return 0;
770 	}
771 
772 	/* examine the cached a state in this cnode */
773 	cmp_state = decmpfs_cnode_get_vnode_state(cp);
774 	switch (cmp_state) {
775 	case FILE_IS_NOT_COMPRESSED:
776 		return 0;
777 	case FILE_IS_COMPRESSED:
778 		return 1;
779 	case FILE_IS_CONVERTING:
780 		/* treat the file as compressed, because this gives us a way to block future reads until decompression is done */
781 		return 1;
782 	case FILE_TYPE_UNKNOWN:
783 		/* the first time we encountered this vnode, so we need to check it out */
784 		break;
785 	default:
786 		/* unknown state, assume file is not compressed */
787 		ErrorLogWithPath("unknown cmp_state %d\n", cmp_state);
788 		return 0;
789 	}
790 
791 	is_mounted = false;
792 	is_local_fs = false;
793 	mp = vnode_mount(vp);
794 	if (mp) {
795 		is_mounted = true;
796 	}
797 	if (is_mounted) {
798 		is_local_fs = ((mp->mnt_flag & MNT_LOCAL));
799 	}
800 	/*
801 	 * Trace the following parameters on entry with event-id 0x03120014.
802 	 *
803 	 * @vp->v_id:       vnode-id of the file being queried.
804 	 * @is_mounted:     set to true if @vp belongs to a mounted fs.
805 	 * @is_local_fs:    set to true if @vp belongs to local fs.
806 	 */
807 	DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id,
808 	    is_mounted, is_local_fs);
809 
810 	if (!is_mounted) {
811 		/*
812 		 *  this should only be true before we mount the root filesystem
813 		 *  we short-cut this return to avoid the call to getattr below, which
814 		 *  will fail before root is mounted
815 		 */
816 		ret = FILE_IS_NOT_COMPRESSED;
817 		goto done;
818 	}
819 
820 	if (!is_local_fs) {
821 		/* compression only supported on local filesystems */
822 		ret = FILE_IS_NOT_COMPRESSED;
823 		goto done;
824 	}
825 
826 	/* lock our cnode data so that another caller doesn't change the state under us */
827 	decmpfs_lock_compressed_data(cp, 1);
828 	cnode_locked = 1;
829 
830 	VATTR_INIT(&va_fetch);
831 	VATTR_WANTED(&va_fetch, va_flags);
832 	error = vnode_getattr(vp, &va_fetch, decmpfs_ctx);
833 	if (error) {
834 		/* failed to get the bsd flags so the file is not compressed */
835 		ret = FILE_IS_NOT_COMPRESSED;
836 		goto done;
837 	}
838 	if (va_fetch.va_flags & UF_COMPRESSED) {
839 		/* UF_COMPRESSED is on, make sure the file has the DECMPFS_XATTR_NAME xattr */
840 		error = decmpfs_fetch_compressed_header(vp, cp, &hdr, 1, &alloc_size);
841 		if ((hdr != NULL) && (error == ERANGE)) {
842 			saveInvalid = 1;
843 		}
844 		if (error) {
845 			/* failed to get the xattr so the file is not compressed */
846 			ret = FILE_IS_NOT_COMPRESSED;
847 			goto done;
848 		}
849 		/*
850 		 * We got the xattr, so the file is at least tagged compressed.
851 		 * For DATALESS, regular files and directories can be "compressed".
852 		 * For all other types, only files are allowed.
853 		 */
854 		if (!vnode_isreg(vp) &&
855 		    !(decmpfs_type_is_dataless(hdr->compression_type) && vnode_isdir(vp))) {
856 			ret = FILE_IS_NOT_COMPRESSED;
857 			goto done;
858 		}
859 		ret = FILE_IS_COMPRESSED;
860 		goto done;
861 	}
862 	/* UF_COMPRESSED isn't on, so the file isn't compressed */
863 	ret = FILE_IS_NOT_COMPRESSED;
864 
865 done:
866 	if (((ret == FILE_IS_COMPRESSED) || saveInvalid) && hdr) {
867 		/*
868 		 *  cache the uncompressed size away in the cnode
869 		 */
870 
871 		if (!cnode_locked) {
872 			/*
873 			 *  we should never get here since the only place ret is set to FILE_IS_COMPRESSED
874 			 *  is after the call to decmpfs_lock_compressed_data above
875 			 */
876 			decmpfs_lock_compressed_data(cp, 1);
877 			cnode_locked = 1;
878 		}
879 
880 		if (vnode_isdir(vp)) {
881 			decmpfs_cnode_set_vnode_cached_size(cp, 64);
882 			decmpfs_cnode_set_vnode_cached_nchildren(cp, decmpfs_get_directory_entries(hdr));
883 			if (hdr->compression_type == DATALESS_PKG_CMPFS_TYPE) {
884 				decmpfs_cnode_set_vnode_cached_total_size(cp, DECMPFS_PKG_SIZE(hdr->_size));
885 			}
886 		} else {
887 			decmpfs_cnode_set_vnode_cached_size(cp, hdr->uncompressed_size);
888 		}
889 		decmpfs_cnode_set_vnode_state(cp, ret, 1);
890 		decmpfs_cnode_set_vnode_cmp_type(cp, hdr->compression_type, 1);
891 		/* remember if the xattr's size was equal to the minimal xattr */
892 		if (hdr->attr_size == sizeof(decmpfs_disk_header)) {
893 			decmpfs_cnode_set_vnode_minimal_xattr(cp, 1, 1);
894 		}
895 		if (ret == FILE_IS_COMPRESSED) {
896 			/* update the ubc's size for this file */
897 			ubc_setsize(vp, hdr->uncompressed_size);
898 
899 			/* update the decompression flags in the decmpfs cnode */
900 			lck_rw_lock_shared(&decompressorsLock);
901 			decmpfs_get_decompression_flags_func get_flags = decmp_get_func(vp, hdr->compression_type, get_flags);
902 			if (get_flags) {
903 				decompression_flags = get_flags(vp, decmpfs_ctx, hdr);
904 			}
905 			lck_rw_unlock_shared(&decompressorsLock);
906 			decmpfs_cnode_set_decompression_flags(cp, decompression_flags);
907 		}
908 	} else {
909 		/* we might have already taken the lock above; if so, skip taking it again by passing cnode_locked as the skiplock parameter */
910 		decmpfs_cnode_set_vnode_state(cp, ret, cnode_locked);
911 	}
912 
913 	if (cnode_locked) {
914 		decmpfs_unlock_compressed_data(cp, 1);
915 	}
916 
917 	if (hdr != NULL) {
918 		kfree_data(hdr, alloc_size);
919 	}
920 
921 	/*
922 	 * Trace the following parameters on return with event-id 0x03120014.
923 	 *
924 	 * @vp->v_id:       vnode-id of the file being queried.
925 	 * @return:         set to 1 is file is compressed.
926 	 */
927 	switch (ret) {
928 	case FILE_IS_NOT_COMPRESSED:
929 		DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id, 0);
930 		return 0;
931 	case FILE_IS_COMPRESSED:
932 	case FILE_IS_CONVERTING:
933 		DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id, 1);
934 		return 1;
935 	default:
936 		/* unknown state, assume file is not compressed */
937 		DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id, 0);
938 		ErrorLogWithPath("unknown ret %d\n", ret);
939 		return 0;
940 	}
941 }
942 
943 int
944 decmpfs_update_attributes(vnode_t vp, struct vnode_attr *vap)
945 {
946 	int error = 0;
947 
948 	if (VATTR_IS_ACTIVE(vap, va_flags)) {
949 		/* the BSD flags are being updated */
950 		if (vap->va_flags & UF_COMPRESSED) {
951 			/* the compressed bit is being set, did it change? */
952 			struct vnode_attr va_fetch;
953 			int old_flags = 0;
954 			VATTR_INIT(&va_fetch);
955 			VATTR_WANTED(&va_fetch, va_flags);
956 			error = vnode_getattr(vp, &va_fetch, decmpfs_ctx);
957 			if (error) {
958 				return error;
959 			}
960 
961 			old_flags = va_fetch.va_flags;
962 
963 			if (!(old_flags & UF_COMPRESSED)) {
964 				/*
965 				 * Compression bit was turned on, make sure the file has the DECMPFS_XATTR_NAME attribute.
966 				 * This precludes anyone from using the UF_COMPRESSED bit for anything else, and it enforces
967 				 * an order of operation -- you must first do the setxattr and then the chflags.
968 				 */
969 
970 				if (VATTR_IS_ACTIVE(vap, va_data_size)) {
971 					/*
972 					 * don't allow the caller to set the BSD flag and the size in the same call
973 					 * since this doesn't really make sense
974 					 */
975 					vap->va_flags &= ~UF_COMPRESSED;
976 					return 0;
977 				}
978 
979 				decmpfs_header *hdr = NULL;
980 				size_t alloc_size = 0;
981 				error = decmpfs_fetch_compressed_header(vp, NULL, &hdr, 1, &alloc_size);
982 				if (error == 0) {
983 					/*
984 					 * Allow the flag to be set since the decmpfs attribute
985 					 * is present.
986 					 *
987 					 * If we're creating a dataless file we do not want to
988 					 * truncate it to zero which allows the file resolver to
989 					 * have more control over when truncation should happen.
990 					 * All other types of compressed files are truncated to
991 					 * zero.
992 					 */
993 					if (!decmpfs_type_is_dataless(hdr->compression_type)) {
994 						VATTR_SET_ACTIVE(vap, va_data_size);
995 						vap->va_data_size = 0;
996 					}
997 				} else if (error == ERANGE) {
998 					/* the file had a decmpfs attribute but the type was out of range, so don't muck with the file's data size */
999 				} else {
1000 					/* no DECMPFS_XATTR_NAME attribute, so deny the update */
1001 					vap->va_flags &= ~UF_COMPRESSED;
1002 				}
1003 				if (hdr != NULL) {
1004 					kfree_data(hdr, alloc_size);
1005 				}
1006 			}
1007 		}
1008 	}
1009 
1010 	return 0;
1011 }
1012 
1013 static int
1014 wait_for_decompress(decmpfs_cnode *cp)
1015 {
1016 	int state;
1017 	lck_mtx_lock(&decompress_channel_mtx);
1018 	do {
1019 		state = decmpfs_fast_get_state(cp);
1020 		if (state != FILE_IS_CONVERTING) {
1021 			/* file is not decompressing */
1022 			lck_mtx_unlock(&decompress_channel_mtx);
1023 			return state;
1024 		}
1025 		msleep((caddr_t)&decompress_channel, &decompress_channel_mtx, PINOD, "wait_for_decompress", NULL);
1026 	} while (1);
1027 }
1028 
1029 #pragma mark --- decmpfs hide query routines ---
1030 
1031 int
1032 decmpfs_hides_rsrc(vfs_context_t ctx, decmpfs_cnode *cp)
1033 {
1034 	/*
1035 	 *  WARNING!!!
1036 	 *  callers may (and do) pass NULL for ctx, so we should only use it
1037 	 *  for this equality comparison
1038 	 *
1039 	 *  This routine should only be called after a file has already been through decmpfs_file_is_compressed
1040 	 */
1041 
1042 	if (ctx == decmpfs_ctx) {
1043 		return 0;
1044 	}
1045 
1046 	if (!decmpfs_fast_file_is_compressed(cp)) {
1047 		return 0;
1048 	}
1049 
1050 	/* all compressed files hide their resource fork */
1051 	return 1;
1052 }
1053 
1054 int
1055 decmpfs_hides_xattr(vfs_context_t ctx, decmpfs_cnode *cp, const char *xattr)
1056 {
1057 	/*
1058 	 *  WARNING!!!
1059 	 *  callers may (and do) pass NULL for ctx, so we should only use it
1060 	 *  for this equality comparison
1061 	 *
1062 	 *  This routine should only be called after a file has already been through decmpfs_file_is_compressed
1063 	 */
1064 
1065 	if (ctx == decmpfs_ctx) {
1066 		return 0;
1067 	}
1068 	if (strncmp(xattr, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME) - 1) == 0) {
1069 		return decmpfs_hides_rsrc(ctx, cp);
1070 	}
1071 	if (!decmpfs_fast_file_is_compressed(cp)) {
1072 		/* file is not compressed, so don't hide this xattr */
1073 		return 0;
1074 	}
1075 	if (strncmp(xattr, DECMPFS_XATTR_NAME, sizeof(DECMPFS_XATTR_NAME) - 1) == 0) {
1076 		/* it's our xattr, so hide it */
1077 		return 1;
1078 	}
1079 	/* don't hide this xattr */
1080 	return 0;
1081 }
1082 
1083 #pragma mark --- registration/validation routines ---
1084 
1085 static inline int
1086 registration_valid(const decmpfs_registration *registration)
1087 {
1088 	return registration && ((registration->decmpfs_registration == DECMPFS_REGISTRATION_VERSION_V1) || (registration->decmpfs_registration == DECMPFS_REGISTRATION_VERSION_V3));
1089 }
1090 
1091 errno_t
1092 register_decmpfs_decompressor(uint32_t compression_type, const decmpfs_registration *registration)
1093 {
1094 	/* called by kexts to register decompressors */
1095 
1096 	errno_t ret = 0;
1097 	int locked = 0;
1098 	char resourceName[80];
1099 
1100 	if ((compression_type >= CMP_MAX) || !registration_valid(registration)) {
1101 		ret = EINVAL;
1102 		goto out;
1103 	}
1104 
1105 	lck_rw_lock_exclusive(&decompressorsLock); locked = 1;
1106 
1107 	/* make sure the registration for this type is zero */
1108 	if (decompressors[compression_type] != NULL) {
1109 		ret = EEXIST;
1110 		goto out;
1111 	}
1112 	decompressors[compression_type] = registration;
1113 	snprintf(resourceName, sizeof(resourceName), "com.apple.AppleFSCompression.Type%u", compression_type);
1114 	IOServicePublishResource(resourceName, TRUE);
1115 
1116 out:
1117 	if (locked) {
1118 		lck_rw_unlock_exclusive(&decompressorsLock);
1119 	}
1120 	return ret;
1121 }
1122 
1123 errno_t
1124 unregister_decmpfs_decompressor(uint32_t compression_type, decmpfs_registration *registration)
1125 {
1126 	/* called by kexts to unregister decompressors */
1127 
1128 	errno_t ret = 0;
1129 	int locked = 0;
1130 	char resourceName[80];
1131 
1132 	if ((compression_type >= CMP_MAX) || !registration_valid(registration)) {
1133 		ret = EINVAL;
1134 		goto out;
1135 	}
1136 
1137 	lck_rw_lock_exclusive(&decompressorsLock); locked = 1;
1138 	if (decompressors[compression_type] != registration) {
1139 		ret = EEXIST;
1140 		goto out;
1141 	}
1142 	decompressors[compression_type] = NULL;
1143 	snprintf(resourceName, sizeof(resourceName), "com.apple.AppleFSCompression.Type%u", compression_type);
1144 	IOServicePublishResource(resourceName, FALSE);
1145 
1146 out:
1147 	if (locked) {
1148 		lck_rw_unlock_exclusive(&decompressorsLock);
1149 	}
1150 	return ret;
1151 }
1152 
1153 static int
1154 compression_type_valid(vnode_t vp, decmpfs_header *hdr)
1155 {
1156 	/* fast pre-check to determine if the given compressor has checked in */
1157 	int ret = 0;
1158 
1159 	/* every compressor must have at least a fetch function */
1160 	lck_rw_lock_shared(&decompressorsLock);
1161 	if (decmp_get_func(vp, hdr->compression_type, fetch) != NULL) {
1162 		ret = 1;
1163 	}
1164 	lck_rw_unlock_shared(&decompressorsLock);
1165 
1166 	return ret;
1167 }
1168 
1169 #pragma mark --- compression/decompression routines ---
1170 
1171 static int
1172 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)
1173 {
1174 	/* get the uncompressed bytes for the specified region of vp by calling out to the registered compressor */
1175 
1176 	int err          = 0;
1177 
1178 	*bytes_read = 0;
1179 
1180 	if (offset >= (off_t)hdr->uncompressed_size) {
1181 		/* reading past end of file; nothing to do */
1182 		err = 0;
1183 		goto out;
1184 	}
1185 	if (offset < 0) {
1186 		/* tried to read from before start of file */
1187 		err = EINVAL;
1188 		goto out;
1189 	}
1190 	if (hdr->uncompressed_size - offset < size) {
1191 		/* adjust size so we don't read past the end of the file */
1192 		size = (user_ssize_t)(hdr->uncompressed_size - offset);
1193 	}
1194 	if (size == 0) {
1195 		/* nothing to read */
1196 		err = 0;
1197 		goto out;
1198 	}
1199 
1200 	/*
1201 	 * Trace the following parameters on entry with event-id 0x03120008.
1202 	 *
1203 	 * @vp->v_id:       vnode-id of the file being decompressed.
1204 	 * @hdr->compression_type: compression type.
1205 	 * @offset:         offset from where to fetch uncompressed data.
1206 	 * @size:           amount of uncompressed data to fetch.
1207 	 *
1208 	 * Please NOTE: @offset and @size can overflow in theory but
1209 	 * here it is safe.
1210 	 */
1211 	DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FETCH_UNCOMPRESSED_DATA, vp->v_id,
1212 	    hdr->compression_type, (int)offset, (int)size);
1213 	lck_rw_lock_shared(&decompressorsLock);
1214 	decmpfs_fetch_uncompressed_data_func fetch = decmp_get_func(vp, hdr->compression_type, fetch);
1215 	if (fetch) {
1216 		err = fetch(vp, decmpfs_ctx, hdr, offset, size, nvec, vec, bytes_read);
1217 		lck_rw_unlock_shared(&decompressorsLock);
1218 		if (err == 0) {
1219 			uint64_t decompression_flags = decmpfs_cnode_get_decompression_flags(cp);
1220 			if (decompression_flags & DECMPFS_FLAGS_FORCE_FLUSH_ON_DECOMPRESS) {
1221 #if     !defined(__i386__) && !defined(__x86_64__)
1222 				int i;
1223 				for (i = 0; i < nvec; i++) {
1224 					assert(vec[i].size >= 0 && vec[i].size <= UINT_MAX);
1225 					flush_dcache64((addr64_t)(uintptr_t)vec[i].buf, (unsigned int)vec[i].size, FALSE);
1226 				}
1227 #endif
1228 			}
1229 		} else {
1230 			decmpfs_ktriage_record(KTRIAGE_DECMPFS_FETCH_CALLBACK_FAILED, err);
1231 		}
1232 	} else {
1233 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_COMPRESSOR_NOT_REGISTERED, hdr->compression_type);
1234 		err = ENOTSUP;
1235 		lck_rw_unlock_shared(&decompressorsLock);
1236 	}
1237 	/*
1238 	 * Trace the following parameters on return with event-id 0x03120008.
1239 	 *
1240 	 * @vp->v_id:       vnode-id of the file being decompressed.
1241 	 * @bytes_read:     amount of uncompressed bytes fetched in bytes.
1242 	 * @err:            value returned from this function.
1243 	 *
1244 	 * Please NOTE: @bytes_read can overflow in theory but here it is safe.
1245 	 */
1246 	DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FETCH_UNCOMPRESSED_DATA, vp->v_id,
1247 	    (int)*bytes_read, err);
1248 out:
1249 	return err;
1250 }
1251 
1252 static kern_return_t
1253 commit_upl(upl_t upl, upl_offset_t pl_offset, size_t uplSize, int flags, int abort)
1254 {
1255 	kern_return_t kr = 0;
1256 
1257 #if CONFIG_IOSCHED
1258 	upl_unmark_decmp(upl);
1259 #endif /* CONFIG_IOSCHED */
1260 
1261 	/* commit the upl pages */
1262 	if (abort) {
1263 		VerboseLog("aborting upl, flags 0x%08x\n", flags);
1264 		kr = ubc_upl_abort_range(upl, pl_offset, (upl_size_t)uplSize, flags);
1265 		if (kr != KERN_SUCCESS) {
1266 			ErrorLog("ubc_upl_abort_range error %d\n", (int)kr);
1267 		}
1268 	} else {
1269 		VerboseLog("committing upl, flags 0x%08x\n", flags | UPL_COMMIT_CLEAR_DIRTY);
1270 		kr = ubc_upl_commit_range(upl, pl_offset, (upl_size_t)uplSize, flags | UPL_COMMIT_CLEAR_DIRTY | UPL_COMMIT_WRITTEN_BY_KERNEL);
1271 		if (kr != KERN_SUCCESS) {
1272 			ErrorLog("ubc_upl_commit_range error %d\n", (int)kr);
1273 		}
1274 	}
1275 	return kr;
1276 }
1277 
1278 
1279 errno_t
1280 decmpfs_pagein_compressed(struct vnop_pagein_args *ap, int *is_compressed, decmpfs_cnode *cp)
1281 {
1282 	/* handles a page-in request from vfs for a compressed file */
1283 
1284 	int err                      = 0;
1285 	vnode_t vp                   = ap->a_vp;
1286 	upl_t pl                     = ap->a_pl;
1287 	upl_offset_t pl_offset       = ap->a_pl_offset;
1288 	off_t f_offset               = ap->a_f_offset;
1289 	size_t size                  = ap->a_size;
1290 	int flags                    = ap->a_flags;
1291 	off_t uplPos                 = 0;
1292 	user_ssize_t uplSize         = 0;
1293 	user_ssize_t rounded_uplSize = 0;
1294 	size_t verify_block_size     = 0;
1295 	void *data                   = NULL;
1296 	decmpfs_header *hdr = NULL;
1297 	size_t alloc_size            = 0;
1298 	uint64_t cachedSize          = 0;
1299 	uint32_t fs_bsize            = 0;
1300 	int cmpdata_locked           = 0;
1301 	int  num_valid_pages         = 0;
1302 	int  num_invalid_pages       = 0;
1303 	bool file_tail_page_valid    = false;
1304 
1305 	if (!decmpfs_trylock_compressed_data(cp, 0)) {
1306 		return EAGAIN;
1307 	}
1308 	cmpdata_locked = 1;
1309 
1310 
1311 	if (flags & ~(UPL_IOSYNC | UPL_NOCOMMIT | UPL_NORDAHEAD)) {
1312 		DebugLogWithPath("pagein: unknown flags 0x%08x\n", (flags & ~(UPL_IOSYNC | UPL_NOCOMMIT | UPL_NORDAHEAD)));
1313 	}
1314 
1315 	err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0, &alloc_size);
1316 	if (err != 0) {
1317 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_FETCH_HEADER_FAILED, err);
1318 		goto out;
1319 	}
1320 
1321 	cachedSize = hdr->uncompressed_size;
1322 
1323 	if (!compression_type_valid(vp, hdr)) {
1324 		/* compressor not registered */
1325 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_COMPRESSOR_NOT_REGISTERED, hdr->compression_type);
1326 		err = ENOTSUP;
1327 		goto out;
1328 	}
1329 
1330 	/*
1331 	 * can't page-in from a negative offset
1332 	 * or if we're starting beyond the EOF
1333 	 * or if the file offset isn't page aligned
1334 	 * or the size requested isn't a multiple of PAGE_SIZE
1335 	 */
1336 	if (f_offset < 0 || f_offset >= cachedSize ||
1337 	    (f_offset & PAGE_MASK_64) || (size & PAGE_MASK) || (pl_offset & PAGE_MASK)) {
1338 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_IVALID_OFFSET, 0);
1339 		err = EINVAL;
1340 		goto out;
1341 	}
1342 
1343 	/*
1344 	 * If the verify block size is larger than the page size, the UPL needs
1345 	 * to be aligned to it, Since the UPL has been created by the filesystem,
1346 	 * we will only check if the passed in UPL length conforms to the
1347 	 * alignment requirements.
1348 	 */
1349 	err = VNOP_VERIFY(vp, f_offset, NULL, 0, &verify_block_size, NULL,
1350 	    VNODE_VERIFY_DEFAULT, NULL);
1351 	if (err) {
1352 		ErrorLogWithPath("VNOP_VERIFY returned error = %d\n", err);
1353 		goto out;
1354 	} else if (verify_block_size) {
1355 		if (vp->v_mount->mnt_vfsstat.f_bsize > PAGE_SIZE) {
1356 			fs_bsize = vp->v_mount->mnt_vfsstat.f_bsize;
1357 		}
1358 		if (verify_block_size & (verify_block_size - 1)) {
1359 			ErrorLogWithPath("verify block size (%zu) is not power of 2, no verification will be done\n", verify_block_size);
1360 			err = EINVAL;
1361 		} else if (size % verify_block_size) {
1362 			ErrorLogWithPath("upl size (%zu) is not a multiple of verify block size (%zu)\n", (size_t)size, verify_block_size);
1363 			err = EINVAL;
1364 		} else if (fs_bsize) {
1365 			/*
1366 			 * Filesystems requesting verification have to provide
1367 			 * values for block sizes which are powers of 2.
1368 			 */
1369 			if (fs_bsize & (fs_bsize - 1)) {
1370 				ErrorLogWithPath("FS block size (%u) is greater than PAGE_SIZE (%d) and is not power of 2, no verification will be done\n",
1371 				    fs_bsize, PAGE_SIZE);
1372 				err = EINVAL;
1373 			} else if (fs_bsize > verify_block_size) {
1374 				ErrorLogWithPath("FS block size (%u) is greater than verify block size (%zu), no verification will be done\n",
1375 				    fs_bsize, verify_block_size);
1376 				err = EINVAL;
1377 			}
1378 		}
1379 		if (err) {
1380 			goto out;
1381 		}
1382 	}
1383 
1384 #if CONFIG_IOSCHED
1385 	/* Mark the UPL as the requesting UPL for decompression */
1386 	upl_mark_decmp(pl);
1387 #endif /* CONFIG_IOSCHED */
1388 
1389 	/* map the upl so we can fetch into it */
1390 	kern_return_t kr = ubc_upl_map(pl, (vm_offset_t*)&data);
1391 	if ((kr != KERN_SUCCESS) || (data == NULL)) {
1392 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_UBC_UPL_MAP_FAILED, kr);
1393 		err = ENOSPC;
1394 		data = NULL;
1395 #if CONFIG_IOSCHED
1396 		upl_unmark_decmp(pl);
1397 #endif /* CONFIG_IOSCHED */
1398 		goto out;
1399 	}
1400 
1401 	uplPos = f_offset;
1402 	off_t max_size = cachedSize - f_offset;
1403 
1404 	if (size < max_size) {
1405 		rounded_uplSize = uplSize = size;
1406 		file_tail_page_valid = true;
1407 	} else {
1408 		uplSize = (user_ssize_t)max_size;
1409 		if (fs_bsize) {
1410 			/* First round up to fs_bsize */
1411 			rounded_uplSize = (uplSize + (fs_bsize - 1)) & ~(fs_bsize - 1);
1412 			/* then to PAGE_SIZE */
1413 			rounded_uplSize = MIN(size, round_page((vm_offset_t)rounded_uplSize));
1414 		} else {
1415 			rounded_uplSize = round_page((vm_offset_t)uplSize);
1416 		}
1417 	}
1418 
1419 	/* do the fetch */
1420 	decmpfs_vector vec;
1421 
1422 decompress:
1423 	/* 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 */
1424 	vec = (decmpfs_vector) {
1425 		.buf = (char*)data + pl_offset,
1426 		.size = size,
1427 	};
1428 
1429 	uint64_t did_read = 0;
1430 	if (decmpfs_fast_get_state(cp) == FILE_IS_CONVERTING) {
1431 		ErrorLogWithPath("unexpected pagein during decompress\n");
1432 		/*
1433 		 *  if the file is converting, this must be a recursive call to pagein from underneath a call to decmpfs_decompress_file;
1434 		 *  pretend that it succeeded but don't do anything since we're just going to write over the pages anyway
1435 		 */
1436 		err = 0;
1437 	} else {
1438 		if (verify_block_size <= PAGE_SIZE) {
1439 			err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, uplPos, uplSize, 1, &vec, &did_read);
1440 			/* zero out whatever wasn't read */
1441 			if (did_read < rounded_uplSize) {
1442 				memset((char*)vec.buf + did_read, 0, (size_t)(rounded_uplSize - did_read));
1443 			}
1444 		} else {
1445 			off_t l_uplPos = uplPos;
1446 			off_t l_pl_offset = pl_offset;
1447 			user_ssize_t l_uplSize = uplSize;
1448 			upl_page_info_t *pl_info = ubc_upl_pageinfo(pl);
1449 
1450 			err = 0;
1451 			/*
1452 			 * When the system page size is less than the "verify block size",
1453 			 * the UPL passed may not consist solely of absent pages.
1454 			 * We have to detect the "absent" pages and only decompress
1455 			 * into those absent/invalid page ranges.
1456 			 *
1457 			 * Things that will change in each iteration of the loop :
1458 			 *
1459 			 * l_pl_offset = where we are inside the UPL [0, caller_upl_created_size)
1460 			 * l_uplPos = the file offset the l_pl_offset corresponds to.
1461 			 * l_uplSize = the size of the upl still unprocessed;
1462 			 *
1463 			 * In this picture, we have to do the transfer on 2 ranges
1464 			 * (One 2 page range and one 3 page range) and the loop
1465 			 * below will skip the first two pages and then identify
1466 			 * the next two as invalid and fill those in and
1467 			 * then skip the next one and then do the last pages.
1468 			 *
1469 			 *                          uplPos(file_offset)
1470 			 *                            |   uplSize
1471 			 * 0                          V<-------------->    file_size
1472 			 * |--------------------------------------------------->
1473 			 *                        | | |V|V|I|I|V|I|I|I|
1474 			 *                            ^
1475 			 *                            |    upl
1476 			 *                        <------------------->
1477 			 *                            |
1478 			 *                          pl_offset
1479 			 *
1480 			 * uplSize will be clipped in case the UPL range exceeds
1481 			 * the file size.
1482 			 *
1483 			 */
1484 			while (l_uplSize) {
1485 				uint64_t l_did_read = 0;
1486 				int pl_offset_pg = (int)(l_pl_offset / PAGE_SIZE);
1487 				int pages_left_in_upl;
1488 				int start_pg;
1489 				int last_pg;
1490 
1491 				/*
1492 				 * l_uplSize may start off less than the size of the upl,
1493 				 * we have to round it up to PAGE_SIZE to calculate
1494 				 * how many more pages are left.
1495 				 */
1496 				pages_left_in_upl = (int)(round_page((vm_offset_t)l_uplSize) / PAGE_SIZE);
1497 
1498 				/*
1499 				 * scan from the beginning of the upl looking for the first
1500 				 * non-valid page.... this will become the first page in
1501 				 * the request we're going to make to
1502 				 * 'decmpfs_fetch_uncompressed_data'... if all
1503 				 * of the pages are valid, we won't call through
1504 				 * to 'decmpfs_fetch_uncompressed_data'
1505 				 */
1506 				for (start_pg = 0; start_pg < pages_left_in_upl; start_pg++) {
1507 					if (!upl_valid_page(pl_info, pl_offset_pg + start_pg)) {
1508 						break;
1509 					}
1510 				}
1511 
1512 				num_valid_pages += start_pg;
1513 
1514 				/*
1515 				 * scan from the starting invalid page looking for
1516 				 * a valid page before the end of the upl is
1517 				 * reached, if we find one, then it will be the
1518 				 * last page of the request to 'decmpfs_fetch_uncompressed_data'
1519 				 */
1520 				for (last_pg = start_pg; last_pg < pages_left_in_upl; last_pg++) {
1521 					if (upl_valid_page(pl_info, pl_offset_pg + last_pg)) {
1522 						break;
1523 					}
1524 				}
1525 
1526 				if (start_pg < last_pg) {
1527 					off_t inval_offset = start_pg * PAGE_SIZE;
1528 					int inval_pages = last_pg - start_pg;
1529 					int inval_size = inval_pages * PAGE_SIZE;
1530 					decmpfs_vector l_vec;
1531 
1532 					num_invalid_pages += inval_pages;
1533 					if (inval_offset) {
1534 						did_read += inval_offset;
1535 						l_pl_offset += inval_offset;
1536 						l_uplPos += inval_offset;
1537 						l_uplSize -= inval_offset;
1538 					}
1539 
1540 					l_vec = (decmpfs_vector) {
1541 						.buf = (char*)data + l_pl_offset,
1542 						.size = inval_size,
1543 					};
1544 
1545 					err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, l_uplPos,
1546 					    MIN(l_uplSize, inval_size), 1, &l_vec, &l_did_read);
1547 
1548 					if (!err && (l_did_read != inval_size) && (l_uplSize > inval_size)) {
1549 						ErrorLogWithPath("Unexpected size fetch of decompressed data, l_uplSize = %d, l_did_read = %d, inval_size = %d\n",
1550 						    (int)l_uplSize, (int)l_did_read, (int)inval_size);
1551 						err = EINVAL;
1552 					}
1553 				} else {
1554 					/* no invalid pages left */
1555 					l_did_read = l_uplSize;
1556 					if (!file_tail_page_valid) {
1557 						file_tail_page_valid = true;
1558 					}
1559 				}
1560 
1561 				if (err) {
1562 					break;
1563 				}
1564 
1565 				did_read += l_did_read;
1566 				l_pl_offset += l_did_read;
1567 				l_uplPos += l_did_read;
1568 				l_uplSize -= l_did_read;
1569 			}
1570 
1571 			/* Zero out the region after EOF in the last page (if needed) */
1572 			if (!err && !file_tail_page_valid && (uplSize < rounded_uplSize)) {
1573 				memset((char*)vec.buf + uplSize, 0, (size_t)(rounded_uplSize - uplSize));
1574 			}
1575 		}
1576 	}
1577 	if (err) {
1578 		decmpfs_ktriage_record(KTRIAGE_DECMPFS_FETCH_UNCOMPRESSED_DATA_FAILED, err)
1579 		DebugLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err);
1580 		int cmp_state = decmpfs_fast_get_state(cp);
1581 		if (cmp_state == FILE_IS_CONVERTING) {
1582 			DebugLogWithPath("cmp_state == FILE_IS_CONVERTING\n");
1583 			cmp_state = wait_for_decompress(cp);
1584 			if (cmp_state == FILE_IS_COMPRESSED) {
1585 				DebugLogWithPath("cmp_state == FILE_IS_COMPRESSED\n");
1586 				/* a decompress was attempted but it failed, let's try calling fetch again */
1587 				goto decompress;
1588 			}
1589 		}
1590 		if (cmp_state == FILE_IS_NOT_COMPRESSED) {
1591 			DebugLogWithPath("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1592 			/* the file was decompressed after we started reading it */
1593 			*is_compressed = 0; /* instruct caller to fall back to its normal path */
1594 		}
1595 	}
1596 
1597 	if (!err && verify_block_size) {
1598 		size_t cur_verify_block_size = verify_block_size;
1599 
1600 		if ((err = VNOP_VERIFY(vp, uplPos, vec.buf, rounded_uplSize, &cur_verify_block_size, NULL, 0, NULL))) {
1601 			ErrorLogWithPath("Verification failed with error %d, uplPos = %lld, uplSize = %d, did_read = %d, valid_pages = %d, invalid_pages = %d, tail_page_valid = %d\n",
1602 			    err, (long long)uplPos, (int)rounded_uplSize, (int)did_read, num_valid_pages, num_invalid_pages, file_tail_page_valid);
1603 		}
1604 		/* XXX : If the verify block size changes, redo the read */
1605 	}
1606 
1607 #if CONFIG_IOSCHED
1608 	upl_unmark_decmp(pl);
1609 #endif /* CONFIG_IOSCHED */
1610 
1611 	kr = ubc_upl_unmap(pl); data = NULL; /* make sure to set data to NULL so we don't try to unmap again below */
1612 	if (kr != KERN_SUCCESS) {
1613 		ErrorLogWithPath("ubc_upl_unmap error %d\n", (int)kr);
1614 	} else {
1615 		if (!err) {
1616 			/* commit our pages */
1617 			kr = commit_upl(pl, pl_offset, (size_t)rounded_uplSize, UPL_COMMIT_FREE_ON_EMPTY, 0 /* commit */);
1618 			/* If there were any pages after the page containing EOF, abort them. */
1619 			if (rounded_uplSize < size) {
1620 				kr = commit_upl(pl, (upl_offset_t)(pl_offset + rounded_uplSize), (size_t)(size - rounded_uplSize),
1621 				    UPL_ABORT_FREE_ON_EMPTY | UPL_ABORT_ERROR, 1 /* abort */);
1622 			}
1623 		}
1624 	}
1625 
1626 out:
1627 	if (data) {
1628 		ubc_upl_unmap(pl);
1629 	}
1630 	if (hdr != NULL) {
1631 		kfree_data(hdr, alloc_size);
1632 	}
1633 	if (cmpdata_locked) {
1634 		decmpfs_unlock_compressed_data(cp, 0);
1635 	}
1636 	if (err) {
1637 #if 0
1638 		if (err != ENXIO && err != ENOSPC) {
1639 			char *path = zalloc(ZV_NAMEI);
1640 			panic("%s: decmpfs_pagein_compressed: err %d", vnpath(vp, path, PATH_MAX), err);
1641 			zfree(ZV_NAMEI, path);
1642 		}
1643 #endif /* 0 */
1644 		ErrorLogWithPath("err %d\n", err);
1645 	}
1646 	return err;
1647 }
1648 
1649 errno_t
1650 decmpfs_read_compressed(struct vnop_read_args *ap, int *is_compressed, decmpfs_cnode *cp)
1651 {
1652 	/* handles a read request from vfs for a compressed file */
1653 
1654 	uio_t uio                    = ap->a_uio;
1655 	vnode_t vp                   = ap->a_vp;
1656 	int err                      = 0;
1657 	int countInt                 = 0;
1658 	off_t uplPos                 = 0;
1659 	user_ssize_t uplSize         = 0;
1660 	user_ssize_t uplRemaining    = 0;
1661 	off_t curUplPos              = 0;
1662 	user_ssize_t curUplSize      = 0;
1663 	kern_return_t kr             = KERN_SUCCESS;
1664 	int abort_read               = 0;
1665 	void *data                   = NULL;
1666 	uint64_t did_read            = 0;
1667 	upl_t upl                    = NULL;
1668 	upl_page_info_t *pli         = NULL;
1669 	decmpfs_header *hdr          = NULL;
1670 	size_t alloc_size            = 0;
1671 	uint64_t cachedSize          = 0;
1672 	off_t uioPos                 = 0;
1673 	user_ssize_t uioRemaining    = 0;
1674 	size_t verify_block_size     = 0;
1675 	size_t alignment_size        = PAGE_SIZE;
1676 	int cmpdata_locked           = 0;
1677 
1678 	decmpfs_lock_compressed_data(cp, 0); cmpdata_locked = 1;
1679 
1680 	uplPos = uio_offset(uio);
1681 	uplSize = uio_resid(uio);
1682 	VerboseLogWithPath("uplPos %lld uplSize %lld\n", uplPos, uplSize);
1683 
1684 	cachedSize = decmpfs_cnode_get_vnode_cached_size(cp);
1685 
1686 	if ((uint64_t)uplPos + uplSize > cachedSize) {
1687 		/* truncate the read to the size of the file */
1688 		uplSize = (user_ssize_t)(cachedSize - uplPos);
1689 	}
1690 
1691 	/* give the cluster layer a chance to fill in whatever it already has */
1692 	countInt = (uplSize > INT_MAX) ? INT_MAX : (int)uplSize;
1693 	err = cluster_copy_ubc_data(vp, uio, &countInt, 0);
1694 	if (err != 0) {
1695 		goto out;
1696 	}
1697 
1698 	/* figure out what's left */
1699 	uioPos = uio_offset(uio);
1700 	uioRemaining = uio_resid(uio);
1701 	if ((uint64_t)uioPos + uioRemaining > cachedSize) {
1702 		/* truncate the read to the size of the file */
1703 		uioRemaining = (user_ssize_t)(cachedSize - uioPos);
1704 	}
1705 
1706 	if (uioRemaining <= 0) {
1707 		/* nothing left */
1708 		goto out;
1709 	}
1710 
1711 	err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0, &alloc_size);
1712 	if (err != 0) {
1713 		goto out;
1714 	}
1715 	if (!compression_type_valid(vp, hdr)) {
1716 		err = ENOTSUP;
1717 		goto out;
1718 	}
1719 
1720 	uplPos = uioPos;
1721 	uplSize = uioRemaining;
1722 #if COMPRESSION_DEBUG
1723 	DebugLogWithPath("uplPos %lld uplSize %lld\n", (uint64_t)uplPos, (uint64_t)uplSize);
1724 #endif
1725 
1726 	lck_rw_lock_shared(&decompressorsLock);
1727 	decmpfs_adjust_fetch_region_func adjust_fetch = decmp_get_func(vp, hdr->compression_type, adjust_fetch);
1728 	if (adjust_fetch) {
1729 		/* give the compressor a chance to adjust the portion of the file that we read */
1730 		adjust_fetch(vp, decmpfs_ctx, hdr, &uplPos, &uplSize);
1731 		VerboseLogWithPath("adjusted uplPos %lld uplSize %lld\n", (uint64_t)uplPos, (uint64_t)uplSize);
1732 	}
1733 	lck_rw_unlock_shared(&decompressorsLock);
1734 
1735 	/* clip the adjusted size to the size of the file */
1736 	if ((uint64_t)uplPos + uplSize > cachedSize) {
1737 		/* truncate the read to the size of the file */
1738 		uplSize = (user_ssize_t)(cachedSize - uplPos);
1739 	}
1740 
1741 	if (uplSize <= 0) {
1742 		/* nothing left */
1743 		goto out;
1744 	}
1745 
1746 	/*
1747 	 *  since we're going to create a upl for the given region of the file,
1748 	 *  make sure we're on page boundaries
1749 	 */
1750 
1751 	/* If the verify block size is larger than the page size, the UPL needs to aligned to it */
1752 	err = VNOP_VERIFY(vp, uplPos, NULL, 0, &verify_block_size, NULL, VNODE_VERIFY_DEFAULT, NULL);
1753 	if (err) {
1754 		goto out;
1755 	} else if (verify_block_size) {
1756 		if (verify_block_size & (verify_block_size - 1)) {
1757 			ErrorLogWithPath("verify block size is not power of 2, no verification will be done\n");
1758 			verify_block_size = 0;
1759 		} else if (verify_block_size > PAGE_SIZE) {
1760 			alignment_size = verify_block_size;
1761 		}
1762 	}
1763 
1764 	if (uplPos & (alignment_size - 1)) {
1765 		/* round position down to page boundary */
1766 		uplSize += (uplPos & (alignment_size - 1));
1767 		uplPos &= ~(alignment_size - 1);
1768 	}
1769 
1770 	/* round size up to alignement_size multiple */
1771 	uplSize = (uplSize + (alignment_size - 1)) & ~(alignment_size - 1);
1772 
1773 	VerboseLogWithPath("new uplPos %lld uplSize %lld\n", (uint64_t)uplPos, (uint64_t)uplSize);
1774 
1775 	uplRemaining = uplSize;
1776 	curUplPos = uplPos;
1777 	curUplSize = 0;
1778 
1779 	while (uplRemaining > 0) {
1780 		/* start after the last upl */
1781 		curUplPos += curUplSize;
1782 
1783 		/* clip to max upl size */
1784 		curUplSize = uplRemaining;
1785 		if (curUplSize > MAX_UPL_SIZE_BYTES) {
1786 			curUplSize = MAX_UPL_SIZE_BYTES;
1787 		}
1788 
1789 		/* create the upl */
1790 		kr = ubc_create_upl_kernel(vp, curUplPos, (int)curUplSize, &upl, &pli, UPL_SET_LITE, VM_KERN_MEMORY_FILE);
1791 		if (kr != KERN_SUCCESS) {
1792 			ErrorLogWithPath("ubc_create_upl error %d\n", (int)kr);
1793 			err = EINVAL;
1794 			goto out;
1795 		}
1796 		VerboseLogWithPath("curUplPos %lld curUplSize %lld\n", (uint64_t)curUplPos, (uint64_t)curUplSize);
1797 
1798 #if CONFIG_IOSCHED
1799 		/* Mark the UPL as the requesting UPL for decompression */
1800 		upl_mark_decmp(upl);
1801 #endif /* CONFIG_IOSCHED */
1802 
1803 		/* map the upl */
1804 		kr = ubc_upl_map(upl, (vm_offset_t*)&data);
1805 		if (kr != KERN_SUCCESS) {
1806 			commit_upl(upl, 0, curUplSize, UPL_ABORT_FREE_ON_EMPTY, 1);
1807 #if 0
1808 			char *path = zalloc(ZV_NAMEI);
1809 			panic("%s: decmpfs_read_compressed: ubc_upl_map error %d", vnpath(vp, path, PATH_MAX), (int)kr);
1810 			zfree(ZV_NAMEI, path);
1811 #else /* 0 */
1812 			ErrorLogWithPath("ubc_upl_map kr=0x%x\n", (int)kr);
1813 #endif /* 0 */
1814 			err = EINVAL;
1815 			goto out;
1816 		}
1817 
1818 		/* make sure the map succeeded */
1819 		if (!data) {
1820 			commit_upl(upl, 0, curUplSize, UPL_ABORT_FREE_ON_EMPTY, 1);
1821 
1822 			ErrorLogWithPath("ubc_upl_map mapped null\n");
1823 			err = EINVAL;
1824 			goto out;
1825 		}
1826 
1827 		/* fetch uncompressed data into the mapped upl */
1828 		decmpfs_vector vec;
1829 decompress:
1830 		vec = (decmpfs_vector){ .buf = data, .size = curUplSize };
1831 		err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, curUplPos, curUplSize, 1, &vec, &did_read);
1832 		if (err) {
1833 			ErrorLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err);
1834 
1835 			/* maybe the file is converting to decompressed */
1836 			int cmp_state = decmpfs_fast_get_state(cp);
1837 			if (cmp_state == FILE_IS_CONVERTING) {
1838 				ErrorLogWithPath("cmp_state == FILE_IS_CONVERTING\n");
1839 				cmp_state = wait_for_decompress(cp);
1840 				if (cmp_state == FILE_IS_COMPRESSED) {
1841 					ErrorLogWithPath("cmp_state == FILE_IS_COMPRESSED\n");
1842 					/* a decompress was attempted but it failed, let's try fetching again */
1843 					goto decompress;
1844 				}
1845 			}
1846 			if (cmp_state == FILE_IS_NOT_COMPRESSED) {
1847 				ErrorLogWithPath("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1848 				/* the file was decompressed after we started reading it */
1849 				*is_compressed = 0; /* instruct caller to fall back to its normal path */
1850 			}
1851 			kr = KERN_FAILURE;
1852 			abort_read = 1; /* we're not going to commit our data */
1853 			did_read = 0;
1854 		}
1855 
1856 		/* zero out the remainder of the last page */
1857 		memset((char*)data + did_read, 0, (size_t)(curUplSize - did_read));
1858 		if (!err && verify_block_size) {
1859 			size_t cur_verify_block_size = verify_block_size;
1860 
1861 			if ((err = VNOP_VERIFY(vp, curUplPos, data, curUplSize, &cur_verify_block_size, NULL, 0, NULL))) {
1862 				ErrorLogWithPath("Verification failed with error %d\n", err);
1863 				abort_read = 1;
1864 			}
1865 			/* XXX : If the verify block size changes, redo the read */
1866 		}
1867 
1868 		kr = ubc_upl_unmap(upl);
1869 		if (kr != KERN_SUCCESS) {
1870 			/* A failure to unmap here will eventually cause a panic anyway */
1871 			panic("ubc_upl_unmap returned error %d (kern_return_t)", (int)kr);
1872 		}
1873 
1874 		if (abort_read) {
1875 			kr = commit_upl(upl, 0, curUplSize, UPL_ABORT_FREE_ON_EMPTY, 1);
1876 		} else {
1877 			VerboseLogWithPath("uioPos %lld uioRemaining %lld\n", (uint64_t)uioPos, (uint64_t)uioRemaining);
1878 			if (uioRemaining) {
1879 				off_t uplOff = uioPos - curUplPos;
1880 				if (uplOff < 0) {
1881 					ErrorLogWithPath("uplOff %lld should never be negative\n", (int64_t)uplOff);
1882 					err = EINVAL;
1883 				} else if (uplOff > INT_MAX) {
1884 					ErrorLogWithPath("uplOff %lld too large\n", (int64_t)uplOff);
1885 					err = EINVAL;
1886 				} else {
1887 					off_t count = curUplPos + curUplSize - uioPos;
1888 					if (count < 0) {
1889 						/* this upl is entirely before the uio */
1890 					} else {
1891 						if (count > uioRemaining) {
1892 							count = uioRemaining;
1893 						}
1894 						int icount = (count > INT_MAX) ? INT_MAX : (int)count;
1895 						int io_resid = icount;
1896 						err = cluster_copy_upl_data(uio, upl, (int)uplOff, &io_resid);
1897 						int copied = icount - io_resid;
1898 						VerboseLogWithPath("uplOff %lld count %lld copied %lld\n", (uint64_t)uplOff, (uint64_t)count, (uint64_t)copied);
1899 						if (err) {
1900 							ErrorLogWithPath("cluster_copy_upl_data err %d\n", err);
1901 						}
1902 						uioPos += copied;
1903 						uioRemaining -= copied;
1904 					}
1905 				}
1906 			}
1907 			kr = commit_upl(upl, 0, curUplSize, UPL_COMMIT_FREE_ON_EMPTY | UPL_COMMIT_INACTIVATE, 0);
1908 		}
1909 
1910 		if (err) {
1911 			goto out;
1912 		}
1913 
1914 		uplRemaining -= curUplSize;
1915 	}
1916 
1917 out:
1918 
1919 	if (hdr != NULL) {
1920 		kfree_data(hdr, alloc_size);
1921 	}
1922 	if (cmpdata_locked) {
1923 		decmpfs_unlock_compressed_data(cp, 0);
1924 	}
1925 	if (err) {/* something went wrong */
1926 		ErrorLogWithPath("err %d\n", err);
1927 		return err;
1928 	}
1929 
1930 #if COMPRESSION_DEBUG
1931 	uplSize = uio_resid(uio);
1932 	if (uplSize) {
1933 		VerboseLogWithPath("still %lld bytes to copy\n", uplSize);
1934 	}
1935 #endif
1936 	return 0;
1937 }
1938 
1939 int
1940 decmpfs_free_compressed_data(vnode_t vp, decmpfs_cnode *cp)
1941 {
1942 	/*
1943 	 *  call out to the decompressor to free remove any data associated with this compressed file
1944 	 *  then delete the file's compression xattr
1945 	 */
1946 	decmpfs_header *hdr = NULL;
1947 	size_t alloc_size = 0;
1948 
1949 	/*
1950 	 * Trace the following parameters on entry with event-id 0x03120010.
1951 	 *
1952 	 * @vp->v_id:       vnode-id of the file for which to free compressed data.
1953 	 */
1954 	DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FREE_COMPRESSED_DATA, vp->v_id);
1955 
1956 	int err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0, &alloc_size);
1957 	if (err) {
1958 		ErrorLogWithPath("decmpfs_fetch_compressed_header err %d\n", err);
1959 	} else {
1960 		lck_rw_lock_shared(&decompressorsLock);
1961 		decmpfs_free_compressed_data_func free_data = decmp_get_func(vp, hdr->compression_type, free_data);
1962 		if (free_data) {
1963 			err = free_data(vp, decmpfs_ctx, hdr);
1964 		} else {
1965 			/* nothing to do, so no error */
1966 			err = 0;
1967 		}
1968 		lck_rw_unlock_shared(&decompressorsLock);
1969 
1970 		if (err != 0) {
1971 			ErrorLogWithPath("decompressor err %d\n", err);
1972 		}
1973 	}
1974 	/*
1975 	 * Trace the following parameters on return with event-id 0x03120010.
1976 	 *
1977 	 * @vp->v_id:       vnode-id of the file for which to free compressed data.
1978 	 * @err:            value returned from this function.
1979 	 */
1980 	DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FREE_COMPRESSED_DATA, vp->v_id, err);
1981 
1982 	/* delete the xattr */
1983 	err = vn_removexattr(vp, DECMPFS_XATTR_NAME, 0, decmpfs_ctx);
1984 
1985 	if (hdr != NULL) {
1986 		kfree_data(hdr, alloc_size);
1987 	}
1988 	return err;
1989 }
1990 
1991 #pragma mark --- file conversion routines ---
1992 
1993 static int
1994 unset_compressed_flag(vnode_t vp)
1995 {
1996 	int err = 0;
1997 	struct vnode_attr va;
1998 	struct fsioc_cas_bsdflags cas;
1999 	int i;
2000 
2001 # define MAX_CAS_BSDFLAGS_LOOPS 4
2002 	/* UF_COMPRESSED should be manipulated only with FSIOC_CAS_BSDFLAGS */
2003 	for (i = 0; i < MAX_CAS_BSDFLAGS_LOOPS; i++) {
2004 		VATTR_INIT(&va);
2005 		VATTR_WANTED(&va, va_flags);
2006 		err = vnode_getattr(vp, &va, decmpfs_ctx);
2007 		if (err != 0) {
2008 			ErrorLogWithPath("vnode_getattr err %d, num retries %d\n", err, i);
2009 			goto out;
2010 		}
2011 
2012 		cas.expected_flags = va.va_flags;
2013 		cas.new_flags = va.va_flags & ~UF_COMPRESSED;
2014 		err = VNOP_IOCTL(vp, FSIOC_CAS_BSDFLAGS, (caddr_t)&cas, FWRITE, decmpfs_ctx);
2015 
2016 		if ((err == 0) && (va.va_flags == cas.actual_flags)) {
2017 			goto out;
2018 		}
2019 
2020 		if ((err != 0) && (err != EAGAIN)) {
2021 			break;
2022 		}
2023 	}
2024 
2025 	/* fallback to regular chflags if FSIOC_CAS_BSDFLAGS is not supported */
2026 	if (err == ENOTTY) {
2027 		VATTR_INIT(&va);
2028 		VATTR_SET(&va, va_flags, cas.new_flags);
2029 		err = vnode_setattr(vp, &va, decmpfs_ctx);
2030 		if (err != 0) {
2031 			ErrorLogWithPath("vnode_setattr err %d\n", err);
2032 		}
2033 	} else if (va.va_flags != cas.actual_flags) {
2034 		ErrorLogWithPath("FSIOC_CAS_BSDFLAGS err: flags mismatc. actual (%x) expected (%x), num retries %d\n", cas.actual_flags, va.va_flags, i);
2035 	} else if (err != 0) {
2036 		ErrorLogWithPath("FSIOC_CAS_BSDFLAGS err %d, num retries %d\n", err, i);
2037 	}
2038 
2039 out:
2040 	return err;
2041 }
2042 
2043 int
2044 decmpfs_decompress_file(vnode_t vp, decmpfs_cnode *cp, off_t toSize, int truncate_okay, int skiplock)
2045 {
2046 	/* convert a compressed file to an uncompressed file */
2047 
2048 	int err                      = 0;
2049 	char *data                   = NULL;
2050 	uio_t uio_w                  = 0;
2051 	off_t offset                 = 0;
2052 	uint32_t old_state           = 0;
2053 	uint32_t new_state           = 0;
2054 	int update_file_state        = 0;
2055 	size_t allocSize             = 0;
2056 	decmpfs_header *hdr          = NULL;
2057 	size_t hdr_size              = 0;
2058 	int cmpdata_locked           = 0;
2059 	off_t remaining              = 0;
2060 	uint64_t uncompressed_size   = 0;
2061 
2062 	/*
2063 	 * Trace the following parameters on entry with event-id 0x03120000.
2064 	 *
2065 	 * @vp->v_id:		vnode-id of the file being decompressed.
2066 	 * @toSize:		uncompress given bytes of the file.
2067 	 * @truncate_okay:	on error it is OK to truncate.
2068 	 * @skiplock:		compressed data is locked, skip locking again.
2069 	 *
2070 	 * Please NOTE: @toSize can overflow in theory but here it is safe.
2071 	 */
2072 	DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_DECOMPRESS_FILE, vp->v_id,
2073 	    (int)toSize, truncate_okay, skiplock);
2074 
2075 	if (!skiplock) {
2076 		decmpfs_lock_compressed_data(cp, 1); cmpdata_locked = 1;
2077 	}
2078 
2079 decompress:
2080 	old_state = decmpfs_fast_get_state(cp);
2081 
2082 	switch (old_state) {
2083 	case FILE_IS_NOT_COMPRESSED:
2084 	{
2085 		/* someone else decompressed the file */
2086 		err = 0;
2087 		goto out;
2088 	}
2089 
2090 	case FILE_TYPE_UNKNOWN:
2091 	{
2092 		/* the file is in an unknown state, so update the state and retry */
2093 		(void)decmpfs_file_is_compressed(vp, cp);
2094 
2095 		/* try again */
2096 		goto decompress;
2097 	}
2098 
2099 	case FILE_IS_COMPRESSED:
2100 	{
2101 		/* the file is compressed, so decompress it */
2102 		break;
2103 	}
2104 
2105 	default:
2106 	{
2107 		/*
2108 		 *  this shouldn't happen since multiple calls to decmpfs_decompress_file lock each other out,
2109 		 *  and when decmpfs_decompress_file returns, the state should be always be set back to
2110 		 *  FILE_IS_NOT_COMPRESSED or FILE_IS_UNKNOWN
2111 		 */
2112 		err = EINVAL;
2113 		goto out;
2114 	}
2115 	}
2116 
2117 	err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0, &hdr_size);
2118 	if (err != 0) {
2119 		goto out;
2120 	}
2121 
2122 	uncompressed_size = hdr->uncompressed_size;
2123 	if (toSize == -1) {
2124 		toSize = hdr->uncompressed_size;
2125 	}
2126 
2127 	if (toSize == 0) {
2128 		/* special case truncating the file to zero bytes */
2129 		goto nodecmp;
2130 	} else if ((uint64_t)toSize > hdr->uncompressed_size) {
2131 		/* the caller is trying to grow the file, so we should decompress all the data */
2132 		toSize = hdr->uncompressed_size;
2133 	}
2134 
2135 	allocSize = MIN(64 * 1024, (size_t)toSize);
2136 	data = (char *)kalloc_data(allocSize, Z_WAITOK);
2137 	if (!data) {
2138 		err = ENOMEM;
2139 		goto out;
2140 	}
2141 
2142 	uio_w = uio_create(1, 0LL, UIO_SYSSPACE, UIO_WRITE);
2143 	if (!uio_w) {
2144 		err = ENOMEM;
2145 		goto out;
2146 	}
2147 	uio_w->uio_flags |= UIO_FLAGS_IS_COMPRESSED_FILE;
2148 
2149 	remaining = toSize;
2150 
2151 	/* tell the buffer cache that this is an empty file */
2152 	ubc_setsize(vp, 0);
2153 
2154 	/* if we got here, we need to decompress the file */
2155 	decmpfs_cnode_set_vnode_state(cp, FILE_IS_CONVERTING, 1);
2156 
2157 	while (remaining > 0) {
2158 		/* loop decompressing data from the file and writing it into the data fork */
2159 
2160 		uint64_t bytes_read = 0;
2161 		decmpfs_vector vec = { .buf = data, .size = (user_ssize_t)MIN(allocSize, remaining) };
2162 		err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, offset, vec.size, 1, &vec, &bytes_read);
2163 		if (err != 0) {
2164 			ErrorLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err);
2165 			goto out;
2166 		}
2167 
2168 		if (bytes_read == 0) {
2169 			/* we're done reading data */
2170 			break;
2171 		}
2172 
2173 		uio_reset(uio_w, offset, UIO_SYSSPACE, UIO_WRITE);
2174 		err = uio_addiov(uio_w, CAST_USER_ADDR_T(data), (user_size_t)bytes_read);
2175 		if (err != 0) {
2176 			ErrorLogWithPath("uio_addiov err %d\n", err);
2177 			err = ENOMEM;
2178 			goto out;
2179 		}
2180 
2181 		err = VNOP_WRITE(vp, uio_w, 0, decmpfs_ctx);
2182 		if (err != 0) {
2183 			/* if the write failed, truncate the file to zero bytes */
2184 			ErrorLogWithPath("VNOP_WRITE err %d\n", err);
2185 			break;
2186 		}
2187 		offset += bytes_read;
2188 		remaining -= bytes_read;
2189 	}
2190 
2191 	if (err == 0) {
2192 		if (offset != toSize) {
2193 			ErrorLogWithPath("file decompressed to %lld instead of %lld\n", offset, toSize);
2194 			err = EINVAL;
2195 			goto out;
2196 		}
2197 	}
2198 
2199 	if (err == 0) {
2200 		/* sync the data and metadata */
2201 		err = VNOP_FSYNC(vp, MNT_WAIT, decmpfs_ctx);
2202 		if (err != 0) {
2203 			ErrorLogWithPath("VNOP_FSYNC err %d\n", err);
2204 			goto out;
2205 		}
2206 	}
2207 
2208 	if (err != 0) {
2209 		/* write, setattr, or fsync failed */
2210 		ErrorLogWithPath("aborting decompress, err %d\n", err);
2211 		if (truncate_okay) {
2212 			/* truncate anything we might have written */
2213 			int error = vnode_setsize(vp, 0, 0, decmpfs_ctx);
2214 			ErrorLogWithPath("vnode_setsize err %d\n", error);
2215 		}
2216 		goto out;
2217 	}
2218 
2219 nodecmp:
2220 	/* if we're truncating the file to zero bytes, we'll skip ahead to here */
2221 
2222 	/* unset the compressed flag */
2223 	unset_compressed_flag(vp);
2224 
2225 	/* free the compressed data associated with this file */
2226 	err = decmpfs_free_compressed_data(vp, cp);
2227 	if (err != 0) {
2228 		ErrorLogWithPath("decmpfs_free_compressed_data err %d\n", err);
2229 	}
2230 
2231 	/*
2232 	 *  even if free_compressed_data or vnode_getattr/vnode_setattr failed, return success
2233 	 *  since we succeeded in writing all of the file data to the data fork
2234 	 */
2235 	err = 0;
2236 
2237 	/* if we got this far, the file was successfully decompressed */
2238 	update_file_state = 1;
2239 	new_state = FILE_IS_NOT_COMPRESSED;
2240 
2241 #if COMPRESSION_DEBUG
2242 	{
2243 		uint64_t filesize = 0;
2244 		vnsize(vp, &filesize);
2245 		DebugLogWithPath("new file size %lld\n", filesize);
2246 	}
2247 #endif
2248 
2249 out:
2250 	if (hdr != NULL) {
2251 		kfree_data(hdr, hdr_size);
2252 	}
2253 	kfree_data(data, allocSize);
2254 
2255 	if (uio_w) {
2256 		uio_free(uio_w);
2257 	}
2258 
2259 	if (err != 0) {
2260 		/* if there was a failure, reset compression flags to unknown and clear the buffer cache data */
2261 		update_file_state = 1;
2262 		new_state = FILE_TYPE_UNKNOWN;
2263 		if (uncompressed_size) {
2264 			ubc_setsize(vp, 0);
2265 			ubc_setsize(vp, uncompressed_size);
2266 		}
2267 	}
2268 
2269 	if (update_file_state) {
2270 		lck_mtx_lock(&decompress_channel_mtx);
2271 		decmpfs_cnode_set_vnode_state(cp, new_state, 1);
2272 		wakeup((caddr_t)&decompress_channel); /* wake up anyone who might have been waiting for decompression */
2273 		lck_mtx_unlock(&decompress_channel_mtx);
2274 	}
2275 
2276 	if (cmpdata_locked) {
2277 		decmpfs_unlock_compressed_data(cp, 1);
2278 	}
2279 	/*
2280 	 * Trace the following parameters on return with event-id 0x03120000.
2281 	 *
2282 	 * @vp->v_id:	vnode-id of the file being decompressed.
2283 	 * @err:	value returned from this function.
2284 	 */
2285 	DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_DECOMPRESS_FILE, vp->v_id, err);
2286 	return err;
2287 }
2288 
2289 #pragma mark --- Type1 compressor ---
2290 
2291 /*
2292  *  The "Type1" compressor stores the data fork directly in the compression xattr
2293  */
2294 
2295 static int
2296 decmpfs_validate_compressed_file_Type1(__unused vnode_t vp, __unused vfs_context_t ctx, decmpfs_header *hdr)
2297 {
2298 	int err          = 0;
2299 
2300 	if (hdr->uncompressed_size + sizeof(decmpfs_disk_header) != (uint64_t)hdr->attr_size) {
2301 		err = EINVAL;
2302 		goto out;
2303 	}
2304 out:
2305 	return err;
2306 }
2307 
2308 static int
2309 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)
2310 {
2311 	int err          = 0;
2312 	int i;
2313 	user_ssize_t remaining;
2314 
2315 	if (hdr->uncompressed_size + sizeof(decmpfs_disk_header) != (uint64_t)hdr->attr_size) {
2316 		err = EINVAL;
2317 		goto out;
2318 	}
2319 
2320 #if COMPRESSION_DEBUG
2321 	static int dummy = 0; // prevent syslog from coalescing printfs
2322 	DebugLogWithPath("%d memcpy %lld at %lld\n", dummy++, size, (uint64_t)offset);
2323 #endif
2324 
2325 	remaining = size;
2326 	for (i = 0; (i < nvec) && (remaining > 0); i++) {
2327 		user_ssize_t curCopy = vec[i].size;
2328 		if (curCopy > remaining) {
2329 			curCopy = remaining;
2330 		}
2331 		memcpy(vec[i].buf, hdr->attr_bytes + offset, curCopy);
2332 		offset += curCopy;
2333 		remaining -= curCopy;
2334 	}
2335 
2336 	if ((bytes_read) && (err == 0)) {
2337 		*bytes_read = (size - remaining);
2338 	}
2339 
2340 out:
2341 	return err;
2342 }
2343 
2344 SECURITY_READ_ONLY_EARLY(static decmpfs_registration) Type1Reg =
2345 {
2346 	.decmpfs_registration = DECMPFS_REGISTRATION_VERSION,
2347 	.validate          = decmpfs_validate_compressed_file_Type1,
2348 	.adjust_fetch      = NULL,/* no adjust necessary */
2349 	.fetch             = decmpfs_fetch_uncompressed_data_Type1,
2350 	.free_data         = NULL,/* no free necessary */
2351 	.get_flags         = NULL/* no flags */
2352 };
2353 
2354 #pragma mark --- decmpfs initialization ---
2355 
2356 void
2357 decmpfs_init(void)
2358 {
2359 	static int done = 0;
2360 	if (done) {
2361 		return;
2362 	}
2363 
2364 	decmpfs_ctx = vfs_context_create(vfs_context_kernel());
2365 
2366 	register_decmpfs_decompressor(CMP_Type1, &Type1Reg);
2367 
2368 	ktriage_register_subsystem_strings(KDBG_TRIAGE_SUBSYS_DECMPFS, &ktriage_decmpfs_subsystem_strings);
2369 
2370 	done = 1;
2371 }
2372 #endif /* FS_COMPRESSION */
2373