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