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