]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/decmpfs.c
84866b9e2f8d59f027b53b12c41b0f03b37194b3
[apple/xnu.git] / bsd / kern / decmpfs.c
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_lock_compressed_data)
42 UNUSED_SYMBOL(decmpfs_cnode_free)
43 UNUSED_SYMBOL(decmpfs_cnode_alloc)
44 UNUSED_SYMBOL(decmpfs_cnode_destroy)
45 UNUSED_SYMBOL(decmpfs_decompress_file)
46 UNUSED_SYMBOL(decmpfs_unlock_compressed_data)
47 UNUSED_SYMBOL(decmpfs_cnode_init)
48 UNUSED_SYMBOL(decmpfs_cnode_set_vnode_state)
49 UNUSED_SYMBOL(decmpfs_hides_xattr)
50 UNUSED_SYMBOL(decmpfs_ctx)
51 UNUSED_SYMBOL(decmpfs_file_is_compressed)
52 UNUSED_SYMBOL(decmpfs_update_attributes)
53 UNUSED_SYMBOL(decmpfs_hides_rsrc)
54 UNUSED_SYMBOL(decmpfs_pagein_compressed)
55 UNUSED_SYMBOL(decmpfs_validate_compressed_file)
56
57 #else /* FS_COMPRESSION */
58 #include <sys/kernel.h>
59 #include <sys/vnode_internal.h>
60 #include <sys/file_internal.h>
61 #include <sys/stat.h>
62 #include <sys/fcntl.h>
63 #include <sys/xattr.h>
64 #include <sys/namei.h>
65 #include <sys/user.h>
66 #include <sys/mount_internal.h>
67 #include <sys/ubc.h>
68 #include <sys/decmpfs.h>
69 #include <sys/uio_internal.h>
70 #include <libkern/OSByteOrder.h>
71 #include <libkern/section_keywords.h>
72
73 #pragma mark --- debugging ---
74
75 #define COMPRESSION_DEBUG 0
76 #define COMPRESSION_DEBUG_VERBOSE 0
77 #define MALLOC_DEBUG 0
78
79 static const char *
80 baseName(const char *path)
81 {
82 if (!path) {
83 return NULL;
84 }
85 const char *ret = path;
86 int i;
87 for (i = 0; path[i] != 0; i++) {
88 if (path[i] == '/') {
89 ret = &path[i + 1];
90 }
91 }
92 return ret;
93 }
94
95 static char*
96 vnpath(vnode_t vp, char *path, int len)
97 {
98 int origlen = len;
99 path[0] = 0;
100 vn_getpath(vp, path, &len);
101 path[origlen - 1] = 0;
102 return path;
103 }
104
105 #define ErrorLog(x, args...) printf("%s:%d:%s: " x, baseName(__FILE__), __LINE__, __FUNCTION__, ## args)
106 #define ErrorLogWithPath(x, args...) do { char *path; MALLOC(path, char *, PATH_MAX, M_TEMP, M_WAITOK); printf("%s:%d:%s: %s: " x, baseName(__FILE__), __LINE__, __FUNCTION__, vnpath(vp, path, PATH_MAX), ## args); FREE(path, M_TEMP); } while(0)
107
108 #if COMPRESSION_DEBUG
109 #define DebugLog ErrorLog
110 #define DebugLogWithPath ErrorLogWithPath
111 #else
112 #define DebugLog(x...) do { } while(0)
113 #define DebugLogWithPath(x...) do { } while(0)
114 #endif
115
116 #if COMPRESSION_DEBUG_VERBOSE
117 #define VerboseLog ErrorLog
118 #define VerboseLogWithPath ErrorLogWithPath
119 #else
120 #define VerboseLog(x...) do { } while(0)
121 #define VerboseLogWithPath(x...) do { } while(0)
122 #endif
123
124 #if MALLOC_DEBUG
125
126 static SInt32 totalAlloc;
127
128 typedef struct {
129 uint32_t allocSz;
130 uint32_t magic;
131 const char *file;
132 int line;
133 } allocated;
134
135 static void *
136 _malloc(uint32_t sz, __unused int type, __unused int flags, const char *file, int line)
137 {
138 uint32_t allocSz = sz + 2 * sizeof(allocated);
139
140 allocated *alloc = NULL;
141 MALLOC(alloc, allocated *, allocSz, type, flags);
142 if (!alloc) {
143 ErrorLog("malloc failed\n");
144 return NULL;
145 }
146
147 char *ret = (char*)&alloc[1];
148 allocated *alloc2 = (allocated*)(ret + sz);
149
150 alloc->allocSz = allocSz;
151 alloc->magic = 0xdadadada;
152 alloc->file = file;
153 alloc->line = line;
154
155 *alloc2 = *alloc;
156
157 int s = OSAddAtomic(sz, &totalAlloc);
158 ErrorLog("malloc(%d) -> %p, total allocations %d\n", sz, ret, s + sz);
159
160 return ret;
161 }
162
163 static void
164 _free(char *ret, __unused int type, const char *file, int line)
165 {
166 if (!ret) {
167 ErrorLog("freeing null\n");
168 return;
169 }
170 allocated *alloc = (allocated*)ret;
171 alloc--;
172 uint32_t sz = alloc->allocSz - 2 * sizeof(allocated);
173 allocated *alloc2 = (allocated*)(ret + sz);
174
175 if (alloc->magic != 0xdadadada) {
176 panic("freeing bad pointer");
177 }
178
179 if (memcmp(alloc, alloc2, sizeof(*alloc)) != 0) {
180 panic("clobbered data");
181 }
182
183 memset(ret, 0xce, sz);
184 alloc2->file = file;
185 alloc2->line = line;
186 FREE(alloc, type);
187 int s = OSAddAtomic(-sz, &totalAlloc);
188 ErrorLog("free(%p,%d) -> total allocations %d\n", ret, sz, s - sz);
189 }
190
191 #undef MALLOC
192 #undef FREE
193 #define MALLOC(space, cast, size, type, flags) (space) = (cast)_malloc(size, type, flags, __FILE__, __LINE__)
194 #define FREE(addr, type) _free((void *)addr, type, __FILE__, __LINE__)
195
196 #endif /* MALLOC_DEBUG */
197
198 #pragma mark --- globals ---
199
200 static lck_grp_t *decmpfs_lockgrp;
201
202 SECURITY_READ_ONLY_EARLY(static decmpfs_registration *) decompressors[CMP_MAX]; /* the registered compressors */
203 static lck_rw_t * decompressorsLock;
204 static int decompress_channel; /* channel used by decompress_file to wake up waiters */
205 static lck_mtx_t *decompress_channel_mtx;
206
207 vfs_context_t decmpfs_ctx;
208
209 #pragma mark --- decmp_get_func ---
210
211 #define offsetof_func(func) ((uintptr_t)(&(((decmpfs_registration*)NULL)->func)))
212
213 static void *
214 _func_from_offset(uint32_t type, uintptr_t offset)
215 {
216 /* get the function at the given offset in the registration for the given type */
217 const decmpfs_registration *reg = decompressors[type];
218 const char *regChar = (const char*)reg;
219 const char *func = &regChar[offset];
220 void * const * funcPtr = (void * const *) func;
221
222 switch (reg->decmpfs_registration) {
223 case DECMPFS_REGISTRATION_VERSION_V1:
224 if (offset > offsetof_func(free_data)) {
225 return NULL;
226 }
227 break;
228 case DECMPFS_REGISTRATION_VERSION_V3:
229 if (offset > offsetof_func(get_flags)) {
230 return NULL;
231 }
232 break;
233 default:
234 return NULL;
235 }
236
237 return funcPtr[0];
238 }
239
240 extern void IOServicePublishResource( const char * property, boolean_t value );
241 extern boolean_t IOServiceWaitForMatchingResource( const char * property, uint64_t timeout );
242 extern boolean_t IOCatalogueMatchingDriversPresent( const char * property );
243
244 static void *
245 _decmp_get_func(vnode_t vp, uint32_t type, uintptr_t offset)
246 {
247 /*
248 * this function should be called while holding a shared lock to decompressorsLock,
249 * and will return with the lock held
250 */
251
252 if (type >= CMP_MAX) {
253 return NULL;
254 }
255
256 if (decompressors[type] != NULL) {
257 // the compressor has already registered but the function might be null
258 return _func_from_offset(type, offset);
259 }
260
261 // does IOKit know about a kext that is supposed to provide this type?
262 char providesName[80];
263 snprintf(providesName, sizeof(providesName), "com.apple.AppleFSCompression.providesType%u", type);
264 if (IOCatalogueMatchingDriversPresent(providesName)) {
265 // there is a kext that says it will register for this type, so let's wait for it
266 char resourceName[80];
267 uint64_t delay = 10000000ULL; // 10 milliseconds.
268 snprintf(resourceName, sizeof(resourceName), "com.apple.AppleFSCompression.Type%u", type);
269 ErrorLogWithPath("waiting for %s\n", resourceName);
270 while (decompressors[type] == NULL) {
271 lck_rw_unlock_shared(decompressorsLock); // we have to unlock to allow the kext to register
272 if (IOServiceWaitForMatchingResource(resourceName, delay)) {
273 lck_rw_lock_shared(decompressorsLock);
274 break;
275 }
276 if (!IOCatalogueMatchingDriversPresent(providesName)) {
277 //
278 ErrorLogWithPath("the kext with %s is no longer present\n", providesName);
279 lck_rw_lock_shared(decompressorsLock);
280 break;
281 }
282 ErrorLogWithPath("still waiting for %s\n", resourceName);
283 delay *= 2;
284 lck_rw_lock_shared(decompressorsLock);
285 }
286 // IOKit says the kext is loaded, so it should be registered too!
287 if (decompressors[type] == NULL) {
288 ErrorLogWithPath("we found %s, but the type still isn't registered\n", providesName);
289 return NULL;
290 }
291 // it's now registered, so let's return the function
292 return _func_from_offset(type, offset);
293 }
294
295 // the compressor hasn't registered, so it never will unless someone manually kextloads it
296 ErrorLogWithPath("tried to access a compressed file of unregistered type %d\n", type);
297 return NULL;
298 }
299
300 #define decmp_get_func(vp, type, func) ((typeof(((decmpfs_registration*)NULL)->func))_decmp_get_func(vp, type, offsetof_func(func)))
301
302 #pragma mark --- utilities ---
303
304 #if COMPRESSION_DEBUG
305 static int
306 vnsize(vnode_t vp, uint64_t *size)
307 {
308 struct vnode_attr va;
309 VATTR_INIT(&va);
310 VATTR_WANTED(&va, va_data_size);
311 int error = vnode_getattr(vp, &va, decmpfs_ctx);
312 if (error != 0) {
313 ErrorLogWithPath("vnode_getattr err %d\n", error);
314 return error;
315 }
316 *size = va.va_data_size;
317 return 0;
318 }
319 #endif /* COMPRESSION_DEBUG */
320
321 #pragma mark --- cnode routines ---
322
323 decmpfs_cnode *
324 decmpfs_cnode_alloc(void)
325 {
326 decmpfs_cnode *dp;
327 MALLOC_ZONE(dp, decmpfs_cnode *, sizeof(decmpfs_cnode), M_DECMPFS_CNODE, M_WAITOK);
328 return dp;
329 }
330
331 void
332 decmpfs_cnode_free(decmpfs_cnode *dp)
333 {
334 FREE_ZONE(dp, sizeof(*dp), M_DECMPFS_CNODE);
335 }
336
337 void
338 decmpfs_cnode_init(decmpfs_cnode *cp)
339 {
340 memset(cp, 0, sizeof(*cp));
341 lck_rw_init(&cp->compressed_data_lock, decmpfs_lockgrp, NULL);
342 }
343
344 void
345 decmpfs_cnode_destroy(decmpfs_cnode *cp)
346 {
347 lck_rw_destroy(&cp->compressed_data_lock, decmpfs_lockgrp);
348 }
349
350 bool
351 decmpfs_trylock_compressed_data(decmpfs_cnode *cp, int exclusive)
352 {
353 void *thread = current_thread();
354 bool retval = false;
355
356 if (cp->lockowner == thread) {
357 /* this thread is already holding an exclusive lock, so bump the count */
358 cp->lockcount++;
359 retval = true;
360 } else if (exclusive) {
361 if ((retval = lck_rw_try_lock_exclusive(&cp->compressed_data_lock))) {
362 cp->lockowner = thread;
363 cp->lockcount = 1;
364 }
365 } else {
366 if ((retval = lck_rw_try_lock_shared(&cp->compressed_data_lock))) {
367 cp->lockowner = (void *)-1;
368 }
369 }
370 return retval;
371 }
372
373 void
374 decmpfs_lock_compressed_data(decmpfs_cnode *cp, int exclusive)
375 {
376 void *thread = current_thread();
377
378 if (cp->lockowner == thread) {
379 /* this thread is already holding an exclusive lock, so bump the count */
380 cp->lockcount++;
381 } else if (exclusive) {
382 lck_rw_lock_exclusive(&cp->compressed_data_lock);
383 cp->lockowner = thread;
384 cp->lockcount = 1;
385 } else {
386 lck_rw_lock_shared(&cp->compressed_data_lock);
387 cp->lockowner = (void *)-1;
388 }
389 }
390
391 void
392 decmpfs_unlock_compressed_data(decmpfs_cnode *cp, __unused int exclusive)
393 {
394 void *thread = current_thread();
395
396 if (cp->lockowner == thread) {
397 /* this thread is holding an exclusive lock, so decrement the count */
398 if ((--cp->lockcount) > 0) {
399 /* the caller still has outstanding locks, so we're done */
400 return;
401 }
402 cp->lockowner = NULL;
403 }
404
405 lck_rw_done(&cp->compressed_data_lock);
406 }
407
408 uint32_t
409 decmpfs_cnode_get_vnode_state(decmpfs_cnode *cp)
410 {
411 return cp->cmp_state;
412 }
413
414 void
415 decmpfs_cnode_set_vnode_state(decmpfs_cnode *cp, uint32_t state, int skiplock)
416 {
417 if (!skiplock) {
418 decmpfs_lock_compressed_data(cp, 1);
419 }
420 cp->cmp_state = state;
421 if (state == FILE_TYPE_UNKNOWN) {
422 /* clear out the compression type too */
423 cp->cmp_type = 0;
424 }
425 if (!skiplock) {
426 decmpfs_unlock_compressed_data(cp, 1);
427 }
428 }
429
430 static void
431 decmpfs_cnode_set_vnode_cmp_type(decmpfs_cnode *cp, uint32_t cmp_type, int skiplock)
432 {
433 if (!skiplock) {
434 decmpfs_lock_compressed_data(cp, 1);
435 }
436 cp->cmp_type = cmp_type;
437 if (!skiplock) {
438 decmpfs_unlock_compressed_data(cp, 1);
439 }
440 }
441
442 static void
443 decmpfs_cnode_set_vnode_minimal_xattr(decmpfs_cnode *cp, int minimal_xattr, int skiplock)
444 {
445 if (!skiplock) {
446 decmpfs_lock_compressed_data(cp, 1);
447 }
448 cp->cmp_minimal_xattr = minimal_xattr;
449 if (!skiplock) {
450 decmpfs_unlock_compressed_data(cp, 1);
451 }
452 }
453
454 uint64_t
455 decmpfs_cnode_get_vnode_cached_size(decmpfs_cnode *cp)
456 {
457 return cp->uncompressed_size;
458 }
459
460 static void
461 decmpfs_cnode_set_vnode_cached_size(decmpfs_cnode *cp, uint64_t size)
462 {
463 while (1) {
464 uint64_t old = cp->uncompressed_size;
465 if (OSCompareAndSwap64(old, size, (UInt64*)&cp->uncompressed_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)
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 uio_t attr_uio = NULL;
512 int err = 0;
513 char *data = NULL;
514 const bool no_additional_data = ((cp != NULL)
515 && (cp->cmp_type != 0)
516 && (cp->cmp_minimal_xattr != 0));
517 char uio_buf[UIO_SIZEOF(1)];
518 decmpfs_header *hdr = NULL;
519
520 /*
521 * Trace the following parameters on entry with event-id 0x03120004
522 *
523 * @vp->v_id: vnode-id for which to fetch compressed header.
524 * @no_additional_data: If set true then xattr didn't have any extra data.
525 * @returnInvalid: return the header even though the type is out of range.
526 */
527 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FETCH_COMPRESSED_HEADER, vp->v_id,
528 no_additional_data, returnInvalid);
529
530 if (no_additional_data) {
531 /* 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 */
532
533 MALLOC(data, char *, sizeof(decmpfs_header), M_TEMP, M_WAITOK);
534 if (!data) {
535 err = ENOMEM;
536 goto out;
537 }
538 hdr = (decmpfs_header*)data;
539 hdr->attr_size = sizeof(decmpfs_disk_header);
540 hdr->compression_magic = DECMPFS_MAGIC;
541 hdr->compression_type = cp->cmp_type;
542 hdr->uncompressed_size = decmpfs_cnode_get_vnode_cached_size(cp);
543 } else {
544 /* figure out how big the xattr is on disk */
545 err = vn_getxattr(vp, DECMPFS_XATTR_NAME, NULL, &attr_size, XATTR_NOSECURITY, decmpfs_ctx);
546 if (err != 0) {
547 goto out;
548 }
549
550 if (attr_size < sizeof(decmpfs_disk_header) || attr_size > MAX_DECMPFS_XATTR_SIZE) {
551 err = EINVAL;
552 goto out;
553 }
554
555 /* allocation includes space for the extra attr_size field of a compressed_header */
556 MALLOC(data, char *, attr_size + sizeof(hdr->attr_size), M_TEMP, M_WAITOK);
557 if (!data) {
558 err = ENOMEM;
559 goto out;
560 }
561
562 /* read the xattr into our buffer, skipping over the attr_size field at the beginning */
563 attr_uio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
564 uio_addiov(attr_uio, CAST_USER_ADDR_T(data + sizeof(hdr->attr_size)), attr_size);
565
566 err = vn_getxattr(vp, DECMPFS_XATTR_NAME, attr_uio, &read_size, XATTR_NOSECURITY, decmpfs_ctx);
567 if (err != 0) {
568 goto out;
569 }
570 if (read_size != attr_size) {
571 err = EINVAL;
572 goto out;
573 }
574 hdr = (decmpfs_header*)data;
575 hdr->attr_size = attr_size;
576 /* swap the fields to native endian */
577 hdr->compression_magic = OSSwapLittleToHostInt32(hdr->compression_magic);
578 hdr->compression_type = OSSwapLittleToHostInt32(hdr->compression_type);
579 hdr->uncompressed_size = OSSwapLittleToHostInt64(hdr->uncompressed_size);
580 }
581
582 if (hdr->compression_magic != DECMPFS_MAGIC) {
583 ErrorLogWithPath("invalid compression_magic 0x%08x, should be 0x%08x\n", hdr->compression_magic, DECMPFS_MAGIC);
584 err = EINVAL;
585 goto out;
586 }
587
588 if (hdr->compression_type >= CMP_MAX) {
589 if (returnInvalid) {
590 /* return the header even though the type is out of range */
591 err = ERANGE;
592 } else {
593 ErrorLogWithPath("compression_type %d out of range\n", hdr->compression_type);
594 err = EINVAL;
595 }
596 goto out;
597 }
598
599 out:
600 if (err && (err != ERANGE)) {
601 DebugLogWithPath("err %d\n", err);
602 if (data) {
603 FREE(data, M_TEMP);
604 }
605 *hdrOut = NULL;
606 } else {
607 *hdrOut = hdr;
608 }
609 /*
610 * Trace the following parameters on return with event-id 0x03120004.
611 *
612 * @vp->v_id: vnode-id for which to fetch compressed header.
613 * @err: value returned from this function.
614 */
615 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FETCH_COMPRESSED_HEADER, vp->v_id, err);
616 return err;
617 }
618
619 static int
620 decmpfs_fast_get_state(decmpfs_cnode *cp)
621 {
622 /*
623 * return the cached state
624 * this should *only* be called when we know that decmpfs_file_is_compressed has already been called,
625 * because this implies that the cached state is valid
626 */
627 int cmp_state = decmpfs_cnode_get_vnode_state(cp);
628
629 switch (cmp_state) {
630 case FILE_IS_NOT_COMPRESSED:
631 case FILE_IS_COMPRESSED:
632 case FILE_IS_CONVERTING:
633 return cmp_state;
634 case FILE_TYPE_UNKNOWN:
635 /*
636 * we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
637 * which should not be possible
638 */
639 ErrorLog("decmpfs_fast_get_state called on unknown file\n");
640 return FILE_IS_NOT_COMPRESSED;
641 default:
642 /* */
643 ErrorLog("unknown cmp_state %d\n", cmp_state);
644 return FILE_IS_NOT_COMPRESSED;
645 }
646 }
647
648 static int
649 decmpfs_fast_file_is_compressed(decmpfs_cnode *cp)
650 {
651 int cmp_state = decmpfs_cnode_get_vnode_state(cp);
652
653 switch (cmp_state) {
654 case FILE_IS_NOT_COMPRESSED:
655 return 0;
656 case FILE_IS_COMPRESSED:
657 case FILE_IS_CONVERTING:
658 return 1;
659 case FILE_TYPE_UNKNOWN:
660 /*
661 * we should only get here if decmpfs_file_is_compressed was not called earlier on this vnode,
662 * which should not be possible
663 */
664 ErrorLog("decmpfs_fast_get_state called on unknown file\n");
665 return 0;
666 default:
667 /* */
668 ErrorLog("unknown cmp_state %d\n", cmp_state);
669 return 0;
670 }
671 }
672
673 errno_t
674 decmpfs_validate_compressed_file(vnode_t vp, decmpfs_cnode *cp)
675 {
676 /* give a compressor a chance to indicate that a compressed file is invalid */
677
678 decmpfs_header *hdr = NULL;
679 errno_t err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0);
680 if (err) {
681 /* we couldn't get the header */
682 if (decmpfs_fast_get_state(cp) == FILE_IS_NOT_COMPRESSED) {
683 /* the file is no longer compressed, so return success */
684 err = 0;
685 }
686 goto out;
687 }
688
689 lck_rw_lock_shared(decompressorsLock);
690 decmpfs_validate_compressed_file_func validate = decmp_get_func(vp, hdr->compression_type, validate);
691 if (validate) { /* make sure this validation function is valid */
692 /* is the data okay? */
693 err = validate(vp, decmpfs_ctx, hdr);
694 } else if (decmp_get_func(vp, hdr->compression_type, fetch) == NULL) {
695 /* the type isn't registered */
696 err = EIO;
697 } else {
698 /* no validate registered, so nothing to do */
699 err = 0;
700 }
701 lck_rw_unlock_shared(decompressorsLock);
702 out:
703 if (hdr) {
704 FREE(hdr, M_TEMP);
705 }
706 #if COMPRESSION_DEBUG
707 if (err) {
708 DebugLogWithPath("decmpfs_validate_compressed_file ret %d, vp->v_flag %d\n", err, vp->v_flag);
709 }
710 #endif
711 return err;
712 }
713
714 int
715 decmpfs_file_is_compressed(vnode_t vp, decmpfs_cnode *cp)
716 {
717 /*
718 * determines whether vp points to a compressed file
719 *
720 * to speed up this operation, we cache the result in the cnode, and do as little as possible
721 * in the case where the cnode already has a valid cached state
722 *
723 */
724
725 int ret = 0;
726 int error = 0;
727 uint32_t cmp_state;
728 struct vnode_attr va_fetch;
729 decmpfs_header *hdr = NULL;
730 mount_t mp = NULL;
731 int cnode_locked = 0;
732 int saveInvalid = 0; // save the header data even though the type was out of range
733 uint64_t decompression_flags = 0;
734 bool is_mounted, is_local_fs;
735
736 if (vnode_isnamedstream(vp)) {
737 /*
738 * named streams can't be compressed
739 * since named streams of the same file share the same cnode,
740 * we don't want to get/set the state in the cnode, just return 0
741 */
742 return 0;
743 }
744
745 /* examine the cached a state in this cnode */
746 cmp_state = decmpfs_cnode_get_vnode_state(cp);
747 switch (cmp_state) {
748 case FILE_IS_NOT_COMPRESSED:
749 return 0;
750 case FILE_IS_COMPRESSED:
751 return 1;
752 case FILE_IS_CONVERTING:
753 /* treat the file as compressed, because this gives us a way to block future reads until decompression is done */
754 return 1;
755 case FILE_TYPE_UNKNOWN:
756 /* the first time we encountered this vnode, so we need to check it out */
757 break;
758 default:
759 /* unknown state, assume file is not compressed */
760 ErrorLogWithPath("unknown cmp_state %d\n", cmp_state);
761 return 0;
762 }
763
764 if (!vnode_isreg(vp)) {
765 /* only regular files can be compressed */
766 ret = FILE_IS_NOT_COMPRESSED;
767 goto done;
768 }
769
770 is_mounted = false;
771 is_local_fs = false;
772 mp = vnode_mount(vp);
773 if (mp) {
774 is_mounted = true;
775 }
776 if (is_mounted) {
777 is_local_fs = ((mp->mnt_flag & MNT_LOCAL));
778 }
779 /*
780 * Trace the following parameters on entry with event-id 0x03120014.
781 *
782 * @vp->v_id: vnode-id of the file being queried.
783 * @is_mounted: set to true if @vp belongs to a mounted fs.
784 * @is_local_fs: set to true if @vp belongs to local fs.
785 */
786 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id,
787 is_mounted, is_local_fs);
788
789 if (!is_mounted) {
790 /*
791 * this should only be true before we mount the root filesystem
792 * we short-cut this return to avoid the call to getattr below, which
793 * will fail before root is mounted
794 */
795 ret = FILE_IS_NOT_COMPRESSED;
796 goto done;
797 }
798
799 if (!is_local_fs) {
800 /* compression only supported on local filesystems */
801 ret = FILE_IS_NOT_COMPRESSED;
802 goto done;
803 }
804
805 /* lock our cnode data so that another caller doesn't change the state under us */
806 decmpfs_lock_compressed_data(cp, 1);
807 cnode_locked = 1;
808
809 VATTR_INIT(&va_fetch);
810 VATTR_WANTED(&va_fetch, va_flags);
811 error = vnode_getattr(vp, &va_fetch, decmpfs_ctx);
812 if (error) {
813 /* failed to get the bsd flags so the file is not compressed */
814 ret = FILE_IS_NOT_COMPRESSED;
815 goto done;
816 }
817 if (va_fetch.va_flags & UF_COMPRESSED) {
818 /* UF_COMPRESSED is on, make sure the file has the DECMPFS_XATTR_NAME xattr */
819 error = decmpfs_fetch_compressed_header(vp, cp, &hdr, 1);
820 if ((hdr != NULL) && (error == ERANGE)) {
821 saveInvalid = 1;
822 }
823 if (error) {
824 /* failed to get the xattr so the file is not compressed */
825 ret = FILE_IS_NOT_COMPRESSED;
826 goto done;
827 }
828 /* we got the xattr, so the file is compressed */
829 ret = FILE_IS_COMPRESSED;
830 goto done;
831 }
832 /* UF_COMPRESSED isn't on, so the file isn't compressed */
833 ret = FILE_IS_NOT_COMPRESSED;
834
835 done:
836 if (((ret == FILE_IS_COMPRESSED) || saveInvalid) && hdr) {
837 /*
838 * cache the uncompressed size away in the cnode
839 */
840
841 if (!cnode_locked) {
842 /*
843 * we should never get here since the only place ret is set to FILE_IS_COMPRESSED
844 * is after the call to decmpfs_lock_compressed_data above
845 */
846 decmpfs_lock_compressed_data(cp, 1);
847 cnode_locked = 1;
848 }
849
850 decmpfs_cnode_set_vnode_cached_size(cp, hdr->uncompressed_size);
851 decmpfs_cnode_set_vnode_state(cp, ret, 1);
852 decmpfs_cnode_set_vnode_cmp_type(cp, hdr->compression_type, 1);
853 /* remember if the xattr's size was equal to the minimal xattr */
854 if (hdr->attr_size == sizeof(decmpfs_disk_header)) {
855 decmpfs_cnode_set_vnode_minimal_xattr(cp, 1, 1);
856 }
857 if (ret == FILE_IS_COMPRESSED) {
858 /* update the ubc's size for this file */
859 ubc_setsize(vp, hdr->uncompressed_size);
860
861 /* update the decompression flags in the decmpfs cnode */
862 lck_rw_lock_shared(decompressorsLock);
863 decmpfs_get_decompression_flags_func get_flags = decmp_get_func(vp, hdr->compression_type, get_flags);
864 if (get_flags) {
865 decompression_flags = get_flags(vp, decmpfs_ctx, hdr);
866 }
867 lck_rw_unlock_shared(decompressorsLock);
868 decmpfs_cnode_set_decompression_flags(cp, decompression_flags);
869 }
870 } else {
871 /* we might have already taken the lock above; if so, skip taking it again by passing cnode_locked as the skiplock parameter */
872 decmpfs_cnode_set_vnode_state(cp, ret, cnode_locked);
873 }
874
875 if (cnode_locked) {
876 decmpfs_unlock_compressed_data(cp, 1);
877 }
878
879 if (hdr) {
880 FREE(hdr, M_TEMP);
881 }
882 /*
883 * Trace the following parameters on return with event-id 0x03120014.
884 *
885 * @vp->v_id: vnode-id of the file being queried.
886 * @return: set to 1 is file is compressed.
887 */
888 switch (ret) {
889 case FILE_IS_NOT_COMPRESSED:
890 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id, 0);
891 return 0;
892 case FILE_IS_COMPRESSED:
893 case FILE_IS_CONVERTING:
894 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id, 1);
895 return 1;
896 default:
897 /* unknown state, assume file is not compressed */
898 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FILE_IS_COMPRESSED, vp->v_id, 0);
899 ErrorLogWithPath("unknown ret %d\n", ret);
900 return 0;
901 }
902 }
903
904 int
905 decmpfs_update_attributes(vnode_t vp, struct vnode_attr *vap)
906 {
907 int error = 0;
908
909 if (VATTR_IS_ACTIVE(vap, va_flags)) {
910 /* the BSD flags are being updated */
911 if (vap->va_flags & UF_COMPRESSED) {
912 /* the compressed bit is being set, did it change? */
913 struct vnode_attr va_fetch;
914 int old_flags = 0;
915 VATTR_INIT(&va_fetch);
916 VATTR_WANTED(&va_fetch, va_flags);
917 error = vnode_getattr(vp, &va_fetch, decmpfs_ctx);
918 if (error) {
919 return error;
920 }
921
922 old_flags = va_fetch.va_flags;
923
924 if (!(old_flags & UF_COMPRESSED)) {
925 /*
926 * Compression bit was turned on, make sure the file has the DECMPFS_XATTR_NAME attribute.
927 * This precludes anyone from using the UF_COMPRESSED bit for anything else, and it enforces
928 * an order of operation -- you must first do the setxattr and then the chflags.
929 */
930
931 if (VATTR_IS_ACTIVE(vap, va_data_size)) {
932 /*
933 * don't allow the caller to set the BSD flag and the size in the same call
934 * since this doesn't really make sense
935 */
936 vap->va_flags &= ~UF_COMPRESSED;
937 return 0;
938 }
939
940 decmpfs_header *hdr = NULL;
941 error = decmpfs_fetch_compressed_header(vp, NULL, &hdr, 1);
942 if (error == 0) {
943 /*
944 * allow the flag to be set since the decmpfs attribute is present
945 * in that case, we also want to truncate the data fork of the file
946 */
947 VATTR_SET_ACTIVE(vap, va_data_size);
948 vap->va_data_size = 0;
949 } else if (error == ERANGE) {
950 /* the file had a decmpfs attribute but the type was out of range, so don't muck with the file's data size */
951 } else {
952 /* no DECMPFS_XATTR_NAME attribute, so deny the update */
953 vap->va_flags &= ~UF_COMPRESSED;
954 }
955 if (hdr) {
956 FREE(hdr, M_TEMP);
957 }
958 }
959 }
960 }
961
962 return 0;
963 }
964
965 static int
966 wait_for_decompress(decmpfs_cnode *cp)
967 {
968 int state;
969 lck_mtx_lock(decompress_channel_mtx);
970 do {
971 state = decmpfs_fast_get_state(cp);
972 if (state != FILE_IS_CONVERTING) {
973 /* file is not decompressing */
974 lck_mtx_unlock(decompress_channel_mtx);
975 return state;
976 }
977 msleep((caddr_t)&decompress_channel, decompress_channel_mtx, PINOD, "wait_for_decompress", NULL);
978 } while (1);
979 }
980
981 #pragma mark --- decmpfs hide query routines ---
982
983 int
984 decmpfs_hides_rsrc(vfs_context_t ctx, decmpfs_cnode *cp)
985 {
986 /*
987 * WARNING!!!
988 * callers may (and do) pass NULL for ctx, so we should only use it
989 * for this equality comparison
990 *
991 * This routine should only be called after a file has already been through decmpfs_file_is_compressed
992 */
993
994 if (ctx == decmpfs_ctx) {
995 return 0;
996 }
997
998 if (!decmpfs_fast_file_is_compressed(cp)) {
999 return 0;
1000 }
1001
1002 /* all compressed files hide their resource fork */
1003 return 1;
1004 }
1005
1006 int
1007 decmpfs_hides_xattr(vfs_context_t ctx, decmpfs_cnode *cp, const char *xattr)
1008 {
1009 /*
1010 * WARNING!!!
1011 * callers may (and do) pass NULL for ctx, so we should only use it
1012 * for this equality comparison
1013 *
1014 * This routine should only be called after a file has already been through decmpfs_file_is_compressed
1015 */
1016
1017 if (ctx == decmpfs_ctx) {
1018 return 0;
1019 }
1020 if (strncmp(xattr, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME) - 1) == 0) {
1021 return decmpfs_hides_rsrc(ctx, cp);
1022 }
1023 if (!decmpfs_fast_file_is_compressed(cp)) {
1024 /* file is not compressed, so don't hide this xattr */
1025 return 0;
1026 }
1027 if (strncmp(xattr, DECMPFS_XATTR_NAME, sizeof(DECMPFS_XATTR_NAME) - 1) == 0) {
1028 /* it's our xattr, so hide it */
1029 return 1;
1030 }
1031 /* don't hide this xattr */
1032 return 0;
1033 }
1034
1035 #pragma mark --- registration/validation routines ---
1036
1037 static inline int
1038 registration_valid(const decmpfs_registration *registration)
1039 {
1040 return registration && ((registration->decmpfs_registration == DECMPFS_REGISTRATION_VERSION_V1) || (registration->decmpfs_registration == DECMPFS_REGISTRATION_VERSION_V3));
1041 }
1042
1043 errno_t
1044 register_decmpfs_decompressor(uint32_t compression_type, const decmpfs_registration *registration)
1045 {
1046 /* called by kexts to register decompressors */
1047
1048 errno_t ret = 0;
1049 int locked = 0;
1050 char resourceName[80];
1051
1052 if ((compression_type >= CMP_MAX) || !registration_valid(registration)) {
1053 ret = EINVAL;
1054 goto out;
1055 }
1056
1057 lck_rw_lock_exclusive(decompressorsLock); locked = 1;
1058
1059 /* make sure the registration for this type is zero */
1060 if (decompressors[compression_type] != NULL) {
1061 ret = EEXIST;
1062 goto out;
1063 }
1064 decompressors[compression_type] = registration;
1065 snprintf(resourceName, sizeof(resourceName), "com.apple.AppleFSCompression.Type%u", compression_type);
1066 IOServicePublishResource(resourceName, TRUE);
1067
1068 out:
1069 if (locked) {
1070 lck_rw_unlock_exclusive(decompressorsLock);
1071 }
1072 return ret;
1073 }
1074
1075 errno_t
1076 unregister_decmpfs_decompressor(uint32_t compression_type, decmpfs_registration *registration)
1077 {
1078 /* called by kexts to unregister decompressors */
1079
1080 errno_t ret = 0;
1081 int locked = 0;
1082 char resourceName[80];
1083
1084 if ((compression_type >= CMP_MAX) || !registration_valid(registration)) {
1085 ret = EINVAL;
1086 goto out;
1087 }
1088
1089 lck_rw_lock_exclusive(decompressorsLock); locked = 1;
1090 if (decompressors[compression_type] != registration) {
1091 ret = EEXIST;
1092 goto out;
1093 }
1094 decompressors[compression_type] = NULL;
1095 snprintf(resourceName, sizeof(resourceName), "com.apple.AppleFSCompression.Type%u", compression_type);
1096 IOServicePublishResource(resourceName, FALSE);
1097
1098 out:
1099 if (locked) {
1100 lck_rw_unlock_exclusive(decompressorsLock);
1101 }
1102 return ret;
1103 }
1104
1105 static int
1106 compression_type_valid(vnode_t vp, decmpfs_header *hdr)
1107 {
1108 /* fast pre-check to determine if the given compressor has checked in */
1109 int ret = 0;
1110
1111 /* every compressor must have at least a fetch function */
1112 lck_rw_lock_shared(decompressorsLock);
1113 if (decmp_get_func(vp, hdr->compression_type, fetch) != NULL) {
1114 ret = 1;
1115 }
1116 lck_rw_unlock_shared(decompressorsLock);
1117
1118 return ret;
1119 }
1120
1121 #pragma mark --- compression/decompression routines ---
1122
1123 static int
1124 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)
1125 {
1126 /* get the uncompressed bytes for the specified region of vp by calling out to the registered compressor */
1127
1128 int err = 0;
1129
1130 *bytes_read = 0;
1131
1132 if ((uint64_t)offset >= hdr->uncompressed_size) {
1133 /* reading past end of file; nothing to do */
1134 err = 0;
1135 goto out;
1136 }
1137 if (offset < 0) {
1138 /* tried to read from before start of file */
1139 err = EINVAL;
1140 goto out;
1141 }
1142 if ((uint64_t)(offset + size) > hdr->uncompressed_size) {
1143 /* adjust size so we don't read past the end of the file */
1144 size = hdr->uncompressed_size - offset;
1145 }
1146 if (size == 0) {
1147 /* nothing to read */
1148 err = 0;
1149 goto out;
1150 }
1151
1152 /*
1153 * Trace the following parameters on entry with event-id 0x03120008.
1154 *
1155 * @vp->v_id: vnode-id of the file being decompressed.
1156 * @hdr->compression_type: compression type.
1157 * @offset: offset from where to fetch uncompressed data.
1158 * @size: amount of uncompressed data to fetch.
1159 *
1160 * Please NOTE: @offset and @size can overflow in theory but
1161 * here it is safe.
1162 */
1163 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FETCH_UNCOMPRESSED_DATA, vp->v_id,
1164 hdr->compression_type, (int)offset, (int)size);
1165 lck_rw_lock_shared(decompressorsLock);
1166 decmpfs_fetch_uncompressed_data_func fetch = decmp_get_func(vp, hdr->compression_type, fetch);
1167 if (fetch) {
1168 err = fetch(vp, decmpfs_ctx, hdr, offset, size, nvec, vec, bytes_read);
1169 lck_rw_unlock_shared(decompressorsLock);
1170 if (err == 0) {
1171 uint64_t decompression_flags = decmpfs_cnode_get_decompression_flags(cp);
1172 if (decompression_flags & DECMPFS_FLAGS_FORCE_FLUSH_ON_DECOMPRESS) {
1173 #if !defined(__i386__) && !defined(__x86_64__)
1174 int i;
1175 for (i = 0; i < nvec; i++) {
1176 flush_dcache64((addr64_t)(uintptr_t)vec[i].buf, vec[i].size, FALSE);
1177 }
1178 #endif
1179 }
1180 }
1181 } else {
1182 err = ENOTSUP;
1183 lck_rw_unlock_shared(decompressorsLock);
1184 }
1185 /*
1186 * Trace the following parameters on return with event-id 0x03120008.
1187 *
1188 * @vp->v_id: vnode-id of the file being decompressed.
1189 * @bytes_read: amount of uncompressed bytes fetched in bytes.
1190 * @err: value returned from this function.
1191 *
1192 * Please NOTE: @bytes_read can overflow in theory but here it is safe.
1193 */
1194 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FETCH_UNCOMPRESSED_DATA, vp->v_id,
1195 (int)*bytes_read, err);
1196 out:
1197 return err;
1198 }
1199
1200 static kern_return_t
1201 commit_upl(upl_t upl, upl_offset_t pl_offset, size_t uplSize, int flags, int abort)
1202 {
1203 kern_return_t kr = 0;
1204
1205 #if CONFIG_IOSCHED
1206 upl_unmark_decmp(upl);
1207 #endif /* CONFIG_IOSCHED */
1208
1209 /* commit the upl pages */
1210 if (abort) {
1211 VerboseLog("aborting upl, flags 0x%08x\n", flags);
1212 kr = ubc_upl_abort_range(upl, pl_offset, uplSize, flags);
1213 if (kr != KERN_SUCCESS) {
1214 ErrorLog("ubc_upl_abort_range error %d\n", (int)kr);
1215 }
1216 } else {
1217 VerboseLog("committing upl, flags 0x%08x\n", flags | UPL_COMMIT_CLEAR_DIRTY);
1218 kr = ubc_upl_commit_range(upl, pl_offset, uplSize, flags | UPL_COMMIT_CLEAR_DIRTY | UPL_COMMIT_WRITTEN_BY_KERNEL);
1219 if (kr != KERN_SUCCESS) {
1220 ErrorLog("ubc_upl_commit_range error %d\n", (int)kr);
1221 }
1222 }
1223 return kr;
1224 }
1225
1226
1227 errno_t
1228 decmpfs_pagein_compressed(struct vnop_pagein_args *ap, int *is_compressed, decmpfs_cnode *cp)
1229 {
1230 /* handles a page-in request from vfs for a compressed file */
1231
1232 int err = 0;
1233 vnode_t vp = ap->a_vp;
1234 upl_t pl = ap->a_pl;
1235 upl_offset_t pl_offset = ap->a_pl_offset;
1236 off_t f_offset = ap->a_f_offset;
1237 size_t size = ap->a_size;
1238 int flags = ap->a_flags;
1239 off_t uplPos = 0;
1240 user_ssize_t uplSize = 0;
1241 void *data = NULL;
1242 decmpfs_header *hdr = NULL;
1243 uint64_t cachedSize = 0;
1244 int cmpdata_locked = 0;
1245
1246 if (!decmpfs_trylock_compressed_data(cp, 0)) {
1247 return EAGAIN;
1248 }
1249 cmpdata_locked = 1;
1250
1251
1252 if (flags & ~(UPL_IOSYNC | UPL_NOCOMMIT | UPL_NORDAHEAD)) {
1253 DebugLogWithPath("pagein: unknown flags 0x%08x\n", (flags & ~(UPL_IOSYNC | UPL_NOCOMMIT | UPL_NORDAHEAD)));
1254 }
1255
1256 err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0);
1257 if (err != 0) {
1258 goto out;
1259 }
1260
1261 cachedSize = hdr->uncompressed_size;
1262
1263 if (!compression_type_valid(vp, hdr)) {
1264 /* compressor not registered */
1265 err = ENOTSUP;
1266 goto out;
1267 }
1268
1269 #if CONFIG_IOSCHED
1270 /* Mark the UPL as the requesting UPL for decompression */
1271 upl_mark_decmp(pl);
1272 #endif /* CONFIG_IOSCHED */
1273
1274 /* map the upl so we can fetch into it */
1275 kern_return_t kr = ubc_upl_map(pl, (vm_offset_t*)&data);
1276 if ((kr != KERN_SUCCESS) || (data == NULL)) {
1277 err = ENOSPC;
1278 data = NULL;
1279 #if CONFIG_IOSCHED
1280 upl_unmark_decmp(pl);
1281 #endif /* CONFIG_IOSCHED */
1282 goto out;
1283 }
1284
1285 uplPos = f_offset;
1286 uplSize = size;
1287
1288 /* clip the size to the size of the file */
1289 if ((uint64_t)uplPos + uplSize > cachedSize) {
1290 /* truncate the read to the size of the file */
1291 uplSize = cachedSize - uplPos;
1292 }
1293
1294 /* do the fetch */
1295 decmpfs_vector vec;
1296
1297 decompress:
1298 /* 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 */
1299 vec.buf = (char*)data + pl_offset;
1300 vec.size = size;
1301
1302 uint64_t did_read = 0;
1303 if (decmpfs_fast_get_state(cp) == FILE_IS_CONVERTING) {
1304 ErrorLogWithPath("unexpected pagein during decompress\n");
1305 /*
1306 * if the file is converting, this must be a recursive call to pagein from underneath a call to decmpfs_decompress_file;
1307 * pretend that it succeeded but don't do anything since we're just going to write over the pages anyway
1308 */
1309 err = 0;
1310 did_read = 0;
1311 } else {
1312 err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, uplPos, uplSize, 1, &vec, &did_read);
1313 }
1314 if (err) {
1315 DebugLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err);
1316 int cmp_state = decmpfs_fast_get_state(cp);
1317 if (cmp_state == FILE_IS_CONVERTING) {
1318 DebugLogWithPath("cmp_state == FILE_IS_CONVERTING\n");
1319 cmp_state = wait_for_decompress(cp);
1320 if (cmp_state == FILE_IS_COMPRESSED) {
1321 DebugLogWithPath("cmp_state == FILE_IS_COMPRESSED\n");
1322 /* a decompress was attempted but it failed, let's try calling fetch again */
1323 goto decompress;
1324 }
1325 }
1326 if (cmp_state == FILE_IS_NOT_COMPRESSED) {
1327 DebugLogWithPath("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1328 /* the file was decompressed after we started reading it */
1329 *is_compressed = 0; /* instruct caller to fall back to its normal path */
1330 }
1331 }
1332
1333 /* zero out whatever we didn't read, and zero out the end of the last page(s) */
1334 uint64_t total_size = (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
1335 if (did_read < total_size) {
1336 memset((char*)vec.buf + did_read, 0, total_size - did_read);
1337 }
1338
1339 #if CONFIG_IOSCHED
1340 upl_unmark_decmp(pl);
1341 #endif /* CONFIG_IOSCHED */
1342
1343 kr = ubc_upl_unmap(pl); data = NULL; /* make sure to set data to NULL so we don't try to unmap again below */
1344 if (kr != KERN_SUCCESS) {
1345 ErrorLogWithPath("ubc_upl_unmap error %d\n", (int)kr);
1346 } else {
1347 if (!err) {
1348 /* commit our pages */
1349 kr = commit_upl(pl, pl_offset, total_size, UPL_COMMIT_FREE_ON_EMPTY, 0);
1350 }
1351 }
1352
1353 out:
1354 if (data) {
1355 ubc_upl_unmap(pl);
1356 }
1357 if (hdr) {
1358 FREE(hdr, M_TEMP);
1359 }
1360 if (cmpdata_locked) {
1361 decmpfs_unlock_compressed_data(cp, 0);
1362 }
1363 if (err) {
1364 #if 0
1365 if (err != ENXIO && err != ENOSPC) {
1366 char *path;
1367 MALLOC(path, char *, PATH_MAX, M_TEMP, M_WAITOK);
1368 panic("%s: decmpfs_pagein_compressed: err %d", vnpath(vp, path, PATH_MAX), err);
1369 FREE(path, M_TEMP);
1370 }
1371 #endif /* 0 */
1372 ErrorLogWithPath("err %d\n", err);
1373 }
1374 return err;
1375 }
1376
1377 errno_t
1378 decmpfs_read_compressed(struct vnop_read_args *ap, int *is_compressed, decmpfs_cnode *cp)
1379 {
1380 /* handles a read request from vfs for a compressed file */
1381
1382 uio_t uio = ap->a_uio;
1383 vnode_t vp = ap->a_vp;
1384 int err = 0;
1385 int countInt = 0;
1386 off_t uplPos = 0;
1387 user_ssize_t uplSize = 0;
1388 user_ssize_t uplRemaining = 0;
1389 off_t curUplPos = 0;
1390 user_ssize_t curUplSize = 0;
1391 kern_return_t kr = KERN_SUCCESS;
1392 int abort_read = 0;
1393 void *data = NULL;
1394 uint64_t did_read = 0;
1395 upl_t upl = NULL;
1396 upl_page_info_t *pli = NULL;
1397 decmpfs_header *hdr = NULL;
1398 uint64_t cachedSize = 0;
1399 off_t uioPos = 0;
1400 user_ssize_t uioRemaining = 0;
1401 int cmpdata_locked = 0;
1402
1403 decmpfs_lock_compressed_data(cp, 0); cmpdata_locked = 1;
1404
1405 uplPos = uio_offset(uio);
1406 uplSize = uio_resid(uio);
1407 VerboseLogWithPath("uplPos %lld uplSize %lld\n", uplPos, uplSize);
1408
1409 cachedSize = decmpfs_cnode_get_vnode_cached_size(cp);
1410
1411 if ((uint64_t)uplPos + uplSize > cachedSize) {
1412 /* truncate the read to the size of the file */
1413 uplSize = cachedSize - uplPos;
1414 }
1415
1416 /* give the cluster layer a chance to fill in whatever it already has */
1417 countInt = (uplSize > INT_MAX) ? INT_MAX : uplSize;
1418 err = cluster_copy_ubc_data(vp, uio, &countInt, 0);
1419 if (err != 0) {
1420 goto out;
1421 }
1422
1423 /* figure out what's left */
1424 uioPos = uio_offset(uio);
1425 uioRemaining = uio_resid(uio);
1426 if ((uint64_t)uioPos + uioRemaining > cachedSize) {
1427 /* truncate the read to the size of the file */
1428 uioRemaining = cachedSize - uioPos;
1429 }
1430
1431 if (uioRemaining <= 0) {
1432 /* nothing left */
1433 goto out;
1434 }
1435
1436 err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0);
1437 if (err != 0) {
1438 goto out;
1439 }
1440 if (!compression_type_valid(vp, hdr)) {
1441 err = ENOTSUP;
1442 goto out;
1443 }
1444
1445 uplPos = uioPos;
1446 uplSize = uioRemaining;
1447 #if COMPRESSION_DEBUG
1448 DebugLogWithPath("uplPos %lld uplSize %lld\n", (uint64_t)uplPos, (uint64_t)uplSize);
1449 #endif
1450
1451 lck_rw_lock_shared(decompressorsLock);
1452 decmpfs_adjust_fetch_region_func adjust_fetch = decmp_get_func(vp, hdr->compression_type, adjust_fetch);
1453 if (adjust_fetch) {
1454 /* give the compressor a chance to adjust the portion of the file that we read */
1455 adjust_fetch(vp, decmpfs_ctx, hdr, &uplPos, &uplSize);
1456 VerboseLogWithPath("adjusted uplPos %lld uplSize %lld\n", (uint64_t)uplPos, (uint64_t)uplSize);
1457 }
1458 lck_rw_unlock_shared(decompressorsLock);
1459
1460 /* clip the adjusted size to the size of the file */
1461 if ((uint64_t)uplPos + uplSize > cachedSize) {
1462 /* truncate the read to the size of the file */
1463 uplSize = cachedSize - uplPos;
1464 }
1465
1466 if (uplSize <= 0) {
1467 /* nothing left */
1468 goto out;
1469 }
1470
1471 /*
1472 * since we're going to create a upl for the given region of the file,
1473 * make sure we're on page boundaries
1474 */
1475
1476 if (uplPos & (PAGE_SIZE - 1)) {
1477 /* round position down to page boundary */
1478 uplSize += (uplPos & (PAGE_SIZE - 1));
1479 uplPos &= ~(PAGE_SIZE - 1);
1480 }
1481 /* round size up to page multiple */
1482 uplSize = (uplSize + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
1483
1484 VerboseLogWithPath("new uplPos %lld uplSize %lld\n", (uint64_t)uplPos, (uint64_t)uplSize);
1485
1486 uplRemaining = uplSize;
1487 curUplPos = uplPos;
1488 curUplSize = 0;
1489
1490 while (uplRemaining > 0) {
1491 /* start after the last upl */
1492 curUplPos += curUplSize;
1493
1494 /* clip to max upl size */
1495 curUplSize = uplRemaining;
1496 if (curUplSize > MAX_UPL_SIZE_BYTES) {
1497 curUplSize = MAX_UPL_SIZE_BYTES;
1498 }
1499
1500 /* create the upl */
1501 kr = ubc_create_upl_kernel(vp, curUplPos, curUplSize, &upl, &pli, UPL_SET_LITE, VM_KERN_MEMORY_FILE);
1502 if (kr != KERN_SUCCESS) {
1503 ErrorLogWithPath("ubc_create_upl error %d\n", (int)kr);
1504 err = EINVAL;
1505 goto out;
1506 }
1507 VerboseLogWithPath("curUplPos %lld curUplSize %lld\n", (uint64_t)curUplPos, (uint64_t)curUplSize);
1508
1509 #if CONFIG_IOSCHED
1510 /* Mark the UPL as the requesting UPL for decompression */
1511 upl_mark_decmp(upl);
1512 #endif /* CONFIG_IOSCHED */
1513
1514 /* map the upl */
1515 kr = ubc_upl_map(upl, (vm_offset_t*)&data);
1516 if (kr != KERN_SUCCESS) {
1517 commit_upl(upl, 0, curUplSize, UPL_ABORT_FREE_ON_EMPTY, 1);
1518 #if 0
1519 char *path;
1520 MALLOC(path, char *, PATH_MAX, M_TEMP, M_WAITOK);
1521 panic("%s: decmpfs_read_compressed: ubc_upl_map error %d", vnpath(vp, path, PATH_MAX), (int)kr);
1522 FREE(path, M_TEMP);
1523 #else /* 0 */
1524 ErrorLogWithPath("ubc_upl_map kr=0x%x\n", (int)kr);
1525 #endif /* 0 */
1526 err = EINVAL;
1527 goto out;
1528 }
1529
1530 /* make sure the map succeeded */
1531 if (!data) {
1532 commit_upl(upl, 0, curUplSize, UPL_ABORT_FREE_ON_EMPTY, 1);
1533
1534 ErrorLogWithPath("ubc_upl_map mapped null\n");
1535 err = EINVAL;
1536 goto out;
1537 }
1538
1539 /* fetch uncompressed data into the mapped upl */
1540 decmpfs_vector vec;
1541 decompress:
1542 vec = (decmpfs_vector){ .buf = data, .size = curUplSize };
1543 err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, curUplPos, curUplSize, 1, &vec, &did_read);
1544 if (err) {
1545 ErrorLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err);
1546
1547 /* maybe the file is converting to decompressed */
1548 int cmp_state = decmpfs_fast_get_state(cp);
1549 if (cmp_state == FILE_IS_CONVERTING) {
1550 ErrorLogWithPath("cmp_state == FILE_IS_CONVERTING\n");
1551 cmp_state = wait_for_decompress(cp);
1552 if (cmp_state == FILE_IS_COMPRESSED) {
1553 ErrorLogWithPath("cmp_state == FILE_IS_COMPRESSED\n");
1554 /* a decompress was attempted but it failed, let's try fetching again */
1555 goto decompress;
1556 }
1557 }
1558 if (cmp_state == FILE_IS_NOT_COMPRESSED) {
1559 ErrorLogWithPath("cmp_state == FILE_IS_NOT_COMPRESSED\n");
1560 /* the file was decompressed after we started reading it */
1561 abort_read = 1; /* we're not going to commit our data */
1562 *is_compressed = 0; /* instruct caller to fall back to its normal path */
1563 }
1564 kr = KERN_FAILURE;
1565 did_read = 0;
1566 }
1567 /* zero out the remainder of the last page */
1568 memset((char*)data + did_read, 0, curUplSize - did_read);
1569 kr = ubc_upl_unmap(upl);
1570 if (kr == KERN_SUCCESS) {
1571 if (abort_read) {
1572 kr = commit_upl(upl, 0, curUplSize, UPL_ABORT_FREE_ON_EMPTY, 1);
1573 } else {
1574 VerboseLogWithPath("uioPos %lld uioRemaining %lld\n", (uint64_t)uioPos, (uint64_t)uioRemaining);
1575 if (uioRemaining) {
1576 off_t uplOff = uioPos - curUplPos;
1577 if (uplOff < 0) {
1578 ErrorLogWithPath("uplOff %lld should never be negative\n", (int64_t)uplOff);
1579 err = EINVAL;
1580 } else {
1581 off_t count = curUplPos + curUplSize - uioPos;
1582 if (count < 0) {
1583 /* this upl is entirely before the uio */
1584 } else {
1585 if (count > uioRemaining) {
1586 count = uioRemaining;
1587 }
1588 int io_resid = count;
1589 err = cluster_copy_upl_data(uio, upl, uplOff, &io_resid);
1590 int copied = count - io_resid;
1591 VerboseLogWithPath("uplOff %lld count %lld copied %lld\n", (uint64_t)uplOff, (uint64_t)count, (uint64_t)copied);
1592 if (err) {
1593 ErrorLogWithPath("cluster_copy_upl_data err %d\n", err);
1594 }
1595 uioPos += copied;
1596 uioRemaining -= copied;
1597 }
1598 }
1599 }
1600 kr = commit_upl(upl, 0, curUplSize, UPL_COMMIT_FREE_ON_EMPTY | UPL_COMMIT_INACTIVATE, 0);
1601 if (err) {
1602 goto out;
1603 }
1604 }
1605 } else {
1606 ErrorLogWithPath("ubc_upl_unmap error %d\n", (int)kr);
1607 }
1608
1609 uplRemaining -= curUplSize;
1610 }
1611
1612 out:
1613
1614 if (hdr) {
1615 FREE(hdr, M_TEMP);
1616 }
1617 if (cmpdata_locked) {
1618 decmpfs_unlock_compressed_data(cp, 0);
1619 }
1620 if (err) {/* something went wrong */
1621 ErrorLogWithPath("err %d\n", err);
1622 return err;
1623 }
1624
1625 #if COMPRESSION_DEBUG
1626 uplSize = uio_resid(uio);
1627 if (uplSize) {
1628 VerboseLogWithPath("still %lld bytes to copy\n", uplSize);
1629 }
1630 #endif
1631 return 0;
1632 }
1633
1634 int
1635 decmpfs_free_compressed_data(vnode_t vp, decmpfs_cnode *cp)
1636 {
1637 /*
1638 * call out to the decompressor to free remove any data associated with this compressed file
1639 * then delete the file's compression xattr
1640 */
1641 decmpfs_header *hdr = NULL;
1642
1643 /*
1644 * Trace the following parameters on entry with event-id 0x03120010.
1645 *
1646 * @vp->v_id: vnode-id of the file for which to free compressed data.
1647 */
1648 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_FREE_COMPRESSED_DATA, vp->v_id);
1649
1650 int err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0);
1651 if (err) {
1652 ErrorLogWithPath("decmpfs_fetch_compressed_header err %d\n", err);
1653 } else {
1654 lck_rw_lock_shared(decompressorsLock);
1655 decmpfs_free_compressed_data_func free_data = decmp_get_func(vp, hdr->compression_type, free_data);
1656 if (free_data) {
1657 err = free_data(vp, decmpfs_ctx, hdr);
1658 } else {
1659 /* nothing to do, so no error */
1660 err = 0;
1661 }
1662 lck_rw_unlock_shared(decompressorsLock);
1663
1664 if (err != 0) {
1665 ErrorLogWithPath("decompressor err %d\n", err);
1666 }
1667 }
1668 /*
1669 * Trace the following parameters on return with event-id 0x03120010.
1670 *
1671 * @vp->v_id: vnode-id of the file for which to free compressed data.
1672 * @err: value returned from this function.
1673 */
1674 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_FREE_COMPRESSED_DATA, vp->v_id, err);
1675
1676 /* delete the xattr */
1677 err = vn_removexattr(vp, DECMPFS_XATTR_NAME, 0, decmpfs_ctx);
1678 if (err != 0) {
1679 goto out;
1680 }
1681
1682 out:
1683 if (hdr) {
1684 FREE(hdr, M_TEMP);
1685 }
1686 return err;
1687 }
1688
1689 #pragma mark --- file conversion routines ---
1690
1691 static int
1692 unset_compressed_flag(vnode_t vp)
1693 {
1694 int err = 0;
1695 struct vnode_attr va;
1696 int new_bsdflags = 0;
1697
1698 VATTR_INIT(&va);
1699 VATTR_WANTED(&va, va_flags);
1700 err = vnode_getattr(vp, &va, decmpfs_ctx);
1701
1702 if (err != 0) {
1703 ErrorLogWithPath("vnode_getattr err %d\n", err);
1704 } else {
1705 new_bsdflags = va.va_flags & ~UF_COMPRESSED;
1706
1707 VATTR_INIT(&va);
1708 VATTR_SET(&va, va_flags, new_bsdflags);
1709 err = vnode_setattr(vp, &va, decmpfs_ctx);
1710 if (err != 0) {
1711 ErrorLogWithPath("vnode_setattr err %d\n", err);
1712 }
1713 }
1714 return err;
1715 }
1716
1717 int
1718 decmpfs_decompress_file(vnode_t vp, decmpfs_cnode *cp, off_t toSize, int truncate_okay, int skiplock)
1719 {
1720 /* convert a compressed file to an uncompressed file */
1721
1722 int err = 0;
1723 char *data = NULL;
1724 uio_t uio_w = 0;
1725 off_t offset = 0;
1726 uint32_t old_state = 0;
1727 uint32_t new_state = 0;
1728 int update_file_state = 0;
1729 int allocSize = 0;
1730 decmpfs_header *hdr = NULL;
1731 int cmpdata_locked = 0;
1732 off_t remaining = 0;
1733 uint64_t uncompressed_size = 0;
1734
1735 /*
1736 * Trace the following parameters on entry with event-id 0x03120000.
1737 *
1738 * @vp->v_id: vnode-id of the file being decompressed.
1739 * @toSize: uncompress given bytes of the file.
1740 * @truncate_okay: on error it is OK to truncate.
1741 * @skiplock: compressed data is locked, skip locking again.
1742 *
1743 * Please NOTE: @toSize can overflow in theory but here it is safe.
1744 */
1745 DECMPFS_EMIT_TRACE_ENTRY(DECMPDBG_DECOMPRESS_FILE, vp->v_id,
1746 (int)toSize, truncate_okay, skiplock);
1747
1748 if (!skiplock) {
1749 decmpfs_lock_compressed_data(cp, 1); cmpdata_locked = 1;
1750 }
1751
1752 decompress:
1753 old_state = decmpfs_fast_get_state(cp);
1754
1755 switch (old_state) {
1756 case FILE_IS_NOT_COMPRESSED:
1757 {
1758 /* someone else decompressed the file */
1759 err = 0;
1760 goto out;
1761 }
1762
1763 case FILE_TYPE_UNKNOWN:
1764 {
1765 /* the file is in an unknown state, so update the state and retry */
1766 (void)decmpfs_file_is_compressed(vp, cp);
1767
1768 /* try again */
1769 goto decompress;
1770 }
1771
1772 case FILE_IS_COMPRESSED:
1773 {
1774 /* the file is compressed, so decompress it */
1775 break;
1776 }
1777
1778 default:
1779 {
1780 /*
1781 * this shouldn't happen since multiple calls to decmpfs_decompress_file lock each other out,
1782 * and when decmpfs_decompress_file returns, the state should be always be set back to
1783 * FILE_IS_NOT_COMPRESSED or FILE_IS_UNKNOWN
1784 */
1785 err = EINVAL;
1786 goto out;
1787 }
1788 }
1789
1790 err = decmpfs_fetch_compressed_header(vp, cp, &hdr, 0);
1791 if (err != 0) {
1792 goto out;
1793 }
1794
1795 uncompressed_size = hdr->uncompressed_size;
1796 if (toSize == -1) {
1797 toSize = hdr->uncompressed_size;
1798 }
1799
1800 if (toSize == 0) {
1801 /* special case truncating the file to zero bytes */
1802 goto nodecmp;
1803 } else if ((uint64_t)toSize > hdr->uncompressed_size) {
1804 /* the caller is trying to grow the file, so we should decompress all the data */
1805 toSize = hdr->uncompressed_size;
1806 }
1807
1808 allocSize = MIN(64 * 1024, toSize);
1809 MALLOC(data, char *, allocSize, M_TEMP, M_WAITOK);
1810 if (!data) {
1811 err = ENOMEM;
1812 goto out;
1813 }
1814
1815 uio_w = uio_create(1, 0LL, UIO_SYSSPACE, UIO_WRITE);
1816 if (!uio_w) {
1817 err = ENOMEM;
1818 goto out;
1819 }
1820 uio_w->uio_flags |= UIO_FLAGS_IS_COMPRESSED_FILE;
1821
1822 remaining = toSize;
1823
1824 /* tell the buffer cache that this is an empty file */
1825 ubc_setsize(vp, 0);
1826
1827 /* if we got here, we need to decompress the file */
1828 decmpfs_cnode_set_vnode_state(cp, FILE_IS_CONVERTING, 1);
1829
1830 while (remaining > 0) {
1831 /* loop decompressing data from the file and writing it into the data fork */
1832
1833 uint64_t bytes_read = 0;
1834 decmpfs_vector vec = { .buf = data, .size = MIN(allocSize, remaining) };
1835 err = decmpfs_fetch_uncompressed_data(vp, cp, hdr, offset, vec.size, 1, &vec, &bytes_read);
1836 if (err != 0) {
1837 ErrorLogWithPath("decmpfs_fetch_uncompressed_data err %d\n", err);
1838 goto out;
1839 }
1840
1841 if (bytes_read == 0) {
1842 /* we're done reading data */
1843 break;
1844 }
1845
1846 uio_reset(uio_w, offset, UIO_SYSSPACE, UIO_WRITE);
1847 err = uio_addiov(uio_w, CAST_USER_ADDR_T(data), bytes_read);
1848 if (err != 0) {
1849 ErrorLogWithPath("uio_addiov err %d\n", err);
1850 err = ENOMEM;
1851 goto out;
1852 }
1853
1854 err = VNOP_WRITE(vp, uio_w, 0, decmpfs_ctx);
1855 if (err != 0) {
1856 /* if the write failed, truncate the file to zero bytes */
1857 ErrorLogWithPath("VNOP_WRITE err %d\n", err);
1858 break;
1859 }
1860 offset += bytes_read;
1861 remaining -= bytes_read;
1862 }
1863
1864 if (err == 0) {
1865 if (offset != toSize) {
1866 ErrorLogWithPath("file decompressed to %lld instead of %lld\n", offset, toSize);
1867 err = EINVAL;
1868 goto out;
1869 }
1870 }
1871
1872 if (err == 0) {
1873 /* sync the data and metadata */
1874 err = VNOP_FSYNC(vp, MNT_WAIT, decmpfs_ctx);
1875 if (err != 0) {
1876 ErrorLogWithPath("VNOP_FSYNC err %d\n", err);
1877 goto out;
1878 }
1879 }
1880
1881 if (err != 0) {
1882 /* write, setattr, or fsync failed */
1883 ErrorLogWithPath("aborting decompress, err %d\n", err);
1884 if (truncate_okay) {
1885 /* truncate anything we might have written */
1886 int error = vnode_setsize(vp, 0, 0, decmpfs_ctx);
1887 ErrorLogWithPath("vnode_setsize err %d\n", error);
1888 }
1889 goto out;
1890 }
1891
1892 nodecmp:
1893 /* if we're truncating the file to zero bytes, we'll skip ahead to here */
1894
1895 /* unset the compressed flag */
1896 unset_compressed_flag(vp);
1897
1898 /* free the compressed data associated with this file */
1899 err = decmpfs_free_compressed_data(vp, cp);
1900 if (err != 0) {
1901 ErrorLogWithPath("decmpfs_free_compressed_data err %d\n", err);
1902 }
1903
1904 /*
1905 * even if free_compressed_data or vnode_getattr/vnode_setattr failed, return success
1906 * since we succeeded in writing all of the file data to the data fork
1907 */
1908 err = 0;
1909
1910 /* if we got this far, the file was successfully decompressed */
1911 update_file_state = 1;
1912 new_state = FILE_IS_NOT_COMPRESSED;
1913
1914 #if COMPRESSION_DEBUG
1915 {
1916 uint64_t filesize = 0;
1917 vnsize(vp, &filesize);
1918 DebugLogWithPath("new file size %lld\n", filesize);
1919 }
1920 #endif
1921
1922 out:
1923 if (hdr) {
1924 FREE(hdr, M_TEMP);
1925 }
1926 if (data) {
1927 FREE(data, M_TEMP);
1928 }
1929 if (uio_w) {
1930 uio_free(uio_w);
1931 }
1932
1933 if (err != 0) {
1934 /* if there was a failure, reset compression flags to unknown and clear the buffer cache data */
1935 update_file_state = 1;
1936 new_state = FILE_TYPE_UNKNOWN;
1937 if (uncompressed_size) {
1938 ubc_setsize(vp, 0);
1939 ubc_setsize(vp, uncompressed_size);
1940 }
1941 }
1942
1943 if (update_file_state) {
1944 lck_mtx_lock(decompress_channel_mtx);
1945 decmpfs_cnode_set_vnode_state(cp, new_state, 1);
1946 wakeup((caddr_t)&decompress_channel); /* wake up anyone who might have been waiting for decompression */
1947 lck_mtx_unlock(decompress_channel_mtx);
1948 }
1949
1950 if (cmpdata_locked) {
1951 decmpfs_unlock_compressed_data(cp, 1);
1952 }
1953 /*
1954 * Trace the following parameters on return with event-id 0x03120000.
1955 *
1956 * @vp->v_id: vnode-id of the file being decompressed.
1957 * @err: value returned from this function.
1958 */
1959 DECMPFS_EMIT_TRACE_RETURN(DECMPDBG_DECOMPRESS_FILE, vp->v_id, err);
1960 return err;
1961 }
1962
1963 #pragma mark --- Type1 compressor ---
1964
1965 /*
1966 * The "Type1" compressor stores the data fork directly in the compression xattr
1967 */
1968
1969 static int
1970 decmpfs_validate_compressed_file_Type1(__unused vnode_t vp, __unused vfs_context_t ctx, decmpfs_header *hdr)
1971 {
1972 int err = 0;
1973
1974 if (hdr->uncompressed_size + sizeof(decmpfs_disk_header) != (uint64_t)hdr->attr_size) {
1975 err = EINVAL;
1976 goto out;
1977 }
1978 out:
1979 return err;
1980 }
1981
1982 static int
1983 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)
1984 {
1985 int err = 0;
1986 int i;
1987 user_ssize_t remaining;
1988
1989 if (hdr->uncompressed_size + sizeof(decmpfs_disk_header) != (uint64_t)hdr->attr_size) {
1990 err = EINVAL;
1991 goto out;
1992 }
1993
1994 #if COMPRESSION_DEBUG
1995 static int dummy = 0; // prevent syslog from coalescing printfs
1996 DebugLogWithPath("%d memcpy %lld at %lld\n", dummy++, size, (uint64_t)offset);
1997 #endif
1998
1999 remaining = size;
2000 for (i = 0; (i < nvec) && (remaining > 0); i++) {
2001 user_ssize_t curCopy = vec[i].size;
2002 if (curCopy > remaining) {
2003 curCopy = remaining;
2004 }
2005 memcpy(vec[i].buf, hdr->attr_bytes + offset, curCopy);
2006 offset += curCopy;
2007 remaining -= curCopy;
2008 }
2009
2010 if ((bytes_read) && (err == 0)) {
2011 *bytes_read = (size - remaining);
2012 }
2013
2014 out:
2015 return err;
2016 }
2017
2018 SECURITY_READ_ONLY_EARLY(static decmpfs_registration) Type1Reg =
2019 {
2020 .decmpfs_registration = DECMPFS_REGISTRATION_VERSION,
2021 .validate = decmpfs_validate_compressed_file_Type1,
2022 .adjust_fetch = NULL,/* no adjust necessary */
2023 .fetch = decmpfs_fetch_uncompressed_data_Type1,
2024 .free_data = NULL,/* no free necessary */
2025 .get_flags = NULL/* no flags */
2026 };
2027
2028 #pragma mark --- decmpfs initialization ---
2029
2030 void
2031 decmpfs_init()
2032 {
2033 static int done = 0;
2034 if (done) {
2035 return;
2036 }
2037
2038 decmpfs_ctx = vfs_context_create(vfs_context_kernel());
2039
2040 lck_grp_attr_t *attr = lck_grp_attr_alloc_init();
2041 decmpfs_lockgrp = lck_grp_alloc_init("VFSCOMP", attr);
2042 lck_grp_attr_free(attr);
2043 decompressorsLock = lck_rw_alloc_init(decmpfs_lockgrp, NULL);
2044 decompress_channel_mtx = lck_mtx_alloc_init(decmpfs_lockgrp, NULL);
2045
2046 register_decmpfs_decompressor(CMP_Type1, &Type1Reg);
2047
2048 done = 1;
2049 }
2050 #endif /* FS_COMPRESSION */