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