1/*
2 * Copyright (c) 2000-2006 Apple Computer, 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/* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
29 *
30 * File: bsd/kern/kern_symfile.c
31 *
32 * HISTORY
33 */
34
35#include <mach/vm_param.h>
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/signalvar.h>
40#include <sys/resourcevar.h>
41#include <sys/namei.h>
42#include <sys/vnode_internal.h>
43#include <sys/proc_internal.h>
44#include <sys/kauth.h>
45#include <sys/timeb.h>
46#include <sys/times.h>
47#include <sys/acct.h>
48#include <sys/file_internal.h>
49#include <sys/uio.h>
50#include <sys/kernel.h>
51#include <sys/stat.h>
52#include <sys/disk.h>
53#include <sys/conf.h>
54#include <sys/content_protection.h>
55#include <sys/fsctl.h>
56
57#include <mach-o/loader.h>
58#include <mach-o/nlist.h>
59
60#include <kern/kalloc.h>
61#include <vm/vm_kern.h>
62#include <pexpert/pexpert.h>
63#include <IOKit/IOPolledInterface.h>
64
65#define HIBERNATE_MIN_PHYSICAL_LBA (34)
66#define HIBERNATE_MIN_FILE_SIZE (1024*1024)
67
68/* This function is called from kern_sysctl in the current process context;
69 * it is exported with the System6.0.exports, but this appears to be a legacy
70 * export, as there are no internal consumers.
71 */
72int
73get_kernel_symfile(__unused proc_t p, __unused char const **symfile);
74int
75get_kernel_symfile(__unused proc_t p, __unused char const **symfile)
76{
77 return KERN_FAILURE;
78}
79
80struct kern_direct_file_io_ref_t
81{
82 vfs_context_t ctx;
83 struct vnode * vp;
84 dev_t device;
85 uint32_t blksize;
86 off_t filelength;
87 char cf;
88 char pinned;
89 char frozen;
90 char wbcranged;
91};
92
93
94static int file_ioctl(void * p1, void * p2, u_long theIoctl, caddr_t result)
95{
96 dev_t device = *(dev_t*) p1;
97
98 return ((*bdevsw[major(device)].d_ioctl)
99 (device, theIoctl, result, S_IFBLK, p2));
100}
101
102static int device_ioctl(void * p1, __unused void * p2, u_long theIoctl, caddr_t result)
103{
104 return (VNOP_IOCTL(p1, theIoctl, result, 0, p2));
105}
106
107static int
108kern_ioctl_file_extents(struct kern_direct_file_io_ref_t * ref, u_long theIoctl, off_t offset, off_t end)
109{
110 int error = 0;
111 int (*do_ioctl)(void * p1, void * p2, u_long theIoctl, caddr_t result);
112 void * p1;
113 void * p2;
114 uint64_t fileblk;
115 size_t filechunk;
116 dk_extent_t extent;
117 dk_unmap_t unmap;
118 _dk_cs_pin_t pin;
119
120 bzero(&extent, sizeof(dk_extent_t));
121 bzero(&unmap, sizeof(dk_unmap_t));
122 bzero(&pin, sizeof(pin));
123 if (ref->vp->v_type == VREG)
124 {
125 p1 = &ref->device;
126 p2 = kernproc;
127 do_ioctl = &file_ioctl;
128 }
129 else
130 {
131 /* Partition. */
132 p1 = ref->vp;
133 p2 = ref->ctx;
134 do_ioctl = &device_ioctl;
135 }
136
137 if (_DKIOCCSPINEXTENT == theIoctl) {
138 /* Tell CS the image size, so it knows whether to place the subsequent pins SSD/HDD */
139 pin.cp_extent.length = end;
140 pin.cp_flags = _DKIOCCSHIBERNATEIMGSIZE;
141 (void) do_ioctl(p1, p2, _DKIOCCSPINEXTENT, (caddr_t)&pin);
142 } else if (_DKIOCCSUNPINEXTENT == theIoctl) {
143 /* Tell CS hibernation is done, so it can stop blocking overlapping writes */
144 pin.cp_flags = _DKIOCCSPINDISCARDBLACKLIST;
145 (void) do_ioctl(p1, p2, _DKIOCCSUNPINEXTENT, (caddr_t)&pin);
146 }
147
148 for (; offset < end; offset += filechunk)
149 {
150 if (ref->vp->v_type == VREG)
151 {
152 daddr64_t blkno;
153 filechunk = 1*1024*1024*1024;
154 if (filechunk > (size_t)(end - offset))
155 filechunk = (size_t)(end - offset);
156 error = VNOP_BLOCKMAP(ref->vp, offset, filechunk, &blkno,
157 &filechunk, NULL, VNODE_WRITE | VNODE_BLOCKMAP_NO_TRACK, NULL);
158 if (error) break;
159 if (-1LL == blkno) continue;
160 fileblk = blkno * ref->blksize;
161 }
162 else if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR))
163 {
164 fileblk = offset;
165 filechunk = ref->filelength;
166 }
167
168 if (DKIOCUNMAP == theIoctl)
169 {
170 extent.offset = fileblk;
171 extent.length = filechunk;
172 unmap.extents = &extent;
173 unmap.extentsCount = 1;
174 error = do_ioctl(p1, p2, theIoctl, (caddr_t)&unmap);
175// printf("DKIOCUNMAP(%d) 0x%qx, 0x%qx\n", error, extent.offset, extent.length);
176 }
177 else if (_DKIOCCSPINEXTENT == theIoctl)
178 {
179 pin.cp_extent.offset = fileblk;
180 pin.cp_extent.length = filechunk;
181 pin.cp_flags = _DKIOCCSPINFORHIBERNATION;
182 error = do_ioctl(p1, p2, theIoctl, (caddr_t)&pin);
183 if (error && (ENOTTY != error))
184 {
185 printf("_DKIOCCSPINEXTENT(%d) 0x%qx, 0x%qx\n", error, pin.cp_extent.offset, pin.cp_extent.length);
186 }
187 }
188 else if (_DKIOCCSUNPINEXTENT == theIoctl)
189 {
190 pin.cp_extent.offset = fileblk;
191 pin.cp_extent.length = filechunk;
192 pin.cp_flags = _DKIOCCSPINFORHIBERNATION;
193 error = do_ioctl(p1, p2, theIoctl, (caddr_t)&pin);
194 if (error && (ENOTTY != error))
195 {
196 printf("_DKIOCCSUNPINEXTENT(%d) 0x%qx, 0x%qx\n", error, pin.cp_extent.offset, pin.cp_extent.length);
197 }
198 }
199 else error = EINVAL;
200
201 if (error) break;
202 }
203 return (error);
204}
205
206extern uint32_t freespace_mb(vnode_t vp);
207
208struct kern_direct_file_io_ref_t *
209kern_open_file_for_direct_io(const char * name,
210 uint32_t iflags,
211 kern_get_file_extents_callback_t callback,
212 void * callback_ref,
213 off_t set_file_size,
214 off_t fs_free_size,
215 off_t write_file_offset,
216 void * write_file_addr,
217 size_t write_file_len,
218 dev_t * partition_device_result,
219 dev_t * image_device_result,
220 uint64_t * partitionbase_result,
221 uint64_t * maxiocount_result,
222 uint32_t * oflags)
223{
224 struct kern_direct_file_io_ref_t * ref;
225
226 proc_t p;
227 struct vnode_attr va;
228 dk_apfs_wbc_range_t wbc_range;
229 int error;
230 off_t f_offset;
231 uint64_t fileblk;
232 size_t filechunk;
233 uint64_t physoffset, minoffset;
234 dev_t device;
235 dev_t target = 0;
236 int isssd = 0;
237 uint32_t flags = 0;
238 uint32_t blksize;
239 off_t maxiocount, count, segcount, wbctotal;
240 boolean_t locked = FALSE;
241 int fmode, cmode;
242 struct nameidata nd;
243 u_int32_t ndflags;
244 off_t mpFree;
245
246 int (*do_ioctl)(void * p1, void * p2, u_long theIoctl, caddr_t result);
247 void * p1 = NULL;
248 void * p2 = NULL;
249
250 error = EFAULT;
251
252 ref = (struct kern_direct_file_io_ref_t *) kalloc(sizeof(struct kern_direct_file_io_ref_t));
253 if (!ref)
254 {
255 error = EFAULT;
256 goto out;
257 }
258
259 bzero(ref, sizeof(*ref));
260 p = kernproc;
261 ref->ctx = vfs_context_kernel();
262
263 fmode = (kIOPolledFileCreate & iflags) ? (O_CREAT | FWRITE) : FWRITE;
264 cmode = S_IRUSR | S_IWUSR;
265 ndflags = NOFOLLOW;
266 NDINIT(&nd, LOOKUP, OP_OPEN, ndflags, UIO_SYSSPACE, CAST_USER_ADDR_T(name), ref->ctx);
267 VATTR_INIT(&va);
268 VATTR_SET(&va, va_mode, cmode);
269 VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWENCRYPTED);
270 VATTR_SET(&va, va_dataprotect_class, PROTECTION_CLASS_D);
271 if ((error = vn_open_auth(&nd, &fmode, &va))) {
272 kprintf("vn_open_auth(fmode: %d, cmode: %d) failed with error: %d\n", fmode, cmode, error);
273 goto out;
274 }
275
276 ref->vp = nd.ni_vp;
277 if (ref->vp->v_type == VREG)
278 {
279 vnode_lock_spin(ref->vp);
280 SET(ref->vp->v_flag, VSWAP);
281 vnode_unlock(ref->vp);
282 }
283
284 if (write_file_addr && write_file_len)
285 {
286 if ((error = kern_write_file(ref, write_file_offset, write_file_addr, write_file_len, IO_SKIP_ENCRYPTION))) {
287 kprintf("kern_write_file() failed with error: %d\n", error);
288 goto out;
289 }
290 }
291
292 VATTR_INIT(&va);
293 VATTR_WANTED(&va, va_rdev);
294 VATTR_WANTED(&va, va_fsid);
295 VATTR_WANTED(&va, va_devid);
296 VATTR_WANTED(&va, va_data_size);
297 VATTR_WANTED(&va, va_data_alloc);
298 VATTR_WANTED(&va, va_nlink);
299 error = EFAULT;
300 if (vnode_getattr(ref->vp, &va, ref->ctx)) goto out;
301
302 wbctotal = 0;
303 mpFree = freespace_mb(ref->vp);
304 mpFree <<= 20;
305 kprintf("kern_direct_file(%s): vp size %qd, alloc %qd, mp free %qd, keep free %qd\n",
306 name, va.va_data_size, va.va_data_alloc, mpFree, fs_free_size);
307
308 if (ref->vp->v_type == VREG)
309 {
310 /* Don't dump files with links. */
311 if (va.va_nlink != 1) goto out;
312
313 device = (VATTR_IS_SUPPORTED(&va, va_devid)) ? va.va_devid : va.va_fsid;
314 ref->filelength = va.va_data_size;
315
316 p1 = &device;
317 p2 = p;
318 do_ioctl = &file_ioctl;
319
320 if (kIOPolledFileHibernate & iflags)
321 {
322 error = do_ioctl(p1, p2, DKIOCAPFSGETWBCRANGE, (caddr_t) &wbc_range);
323 ref->wbcranged = (error == 0);
324 }
325 if (ref->wbcranged)
326 {
327 uint32_t idx;
328 assert(wbc_range.count <= (sizeof(wbc_range.extents) / sizeof(wbc_range.extents[0])));
329 for (idx = 0; idx < wbc_range.count; idx++) wbctotal += wbc_range.extents[idx].length;
330 kprintf("kern_direct_file(%s): wbc %qd\n", name, wbctotal);
331 if (wbctotal) target = wbc_range.dev;
332 }
333
334 if (set_file_size)
335 {
336 if (wbctotal)
337 {
338 if (wbctotal >= set_file_size) set_file_size = HIBERNATE_MIN_FILE_SIZE;
339 else
340 {
341 set_file_size -= wbctotal;
342 if (set_file_size < HIBERNATE_MIN_FILE_SIZE) set_file_size = HIBERNATE_MIN_FILE_SIZE;
343 }
344 }
345 if (fs_free_size)
346 {
347 mpFree += va.va_data_alloc;
348 if ((mpFree < set_file_size) || ((mpFree - set_file_size) < fs_free_size))
349 {
350 error = ENOSPC;
351 goto out;
352 }
353 }
354 error = vnode_setsize(ref->vp, set_file_size, IO_NOZEROFILL | IO_NOAUTH, ref->ctx);
355 if (error) goto out;
356 ref->filelength = set_file_size;
357 }
358 }
359 else if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR))
360 {
361 /* Partition. */
362 device = va.va_rdev;
363
364 p1 = ref->vp;
365 p2 = ref->ctx;
366 do_ioctl = &device_ioctl;
367 }
368 else
369 {
370 /* Don't dump to non-regular files. */
371 error = EFAULT;
372 goto out;
373 }
374 ref->device = device;
375
376 // probe for CF
377 dk_corestorage_info_t cs_info;
378 memset(&cs_info, 0, sizeof(dk_corestorage_info_t));
379 error = do_ioctl(p1, p2, DKIOCCORESTORAGE, (caddr_t)&cs_info);
380 ref->cf = (error == 0) && (cs_info.flags & DK_CORESTORAGE_ENABLE_HOTFILES);
381
382 // get block size
383
384 error = do_ioctl(p1, p2, DKIOCGETBLOCKSIZE, (caddr_t) &ref->blksize);
385 if (error)
386 goto out;
387
388 minoffset = HIBERNATE_MIN_PHYSICAL_LBA * ref->blksize;
389
390 if (ref->vp->v_type != VREG)
391 {
392 error = do_ioctl(p1, p2, DKIOCGETBLOCKCOUNT, (caddr_t) &fileblk);
393 if (error) goto out;
394 ref->filelength = fileblk * ref->blksize;
395 }
396
397 // pin logical extents, CS version
398
399 error = kern_ioctl_file_extents(ref, _DKIOCCSPINEXTENT, 0, ref->filelength);
400 if (error && (ENOTTY != error)) goto out;
401 ref->pinned = (error == 0);
402
403 // pin logical extents, apfs version
404
405 error = VNOP_IOCTL(ref->vp, FSCTL_FREEZE_EXTENTS, NULL, 0, ref->ctx);
406 if (error && (ENOTTY != error)) goto out;
407 ref->frozen = (error == 0);
408
409 // generate the block list
410
411 error = do_ioctl(p1, p2, DKIOCLOCKPHYSICALEXTENTS, NULL);
412 if (error) goto out;
413 locked = TRUE;
414
415 f_offset = 0;
416 for (; f_offset < ref->filelength; f_offset += filechunk)
417 {
418 if (ref->vp->v_type == VREG)
419 {
420 filechunk = 1*1024*1024*1024;
421 daddr64_t blkno;
422
423 error = VNOP_BLOCKMAP(ref->vp, f_offset, filechunk, &blkno,
424 &filechunk, NULL, VNODE_WRITE | VNODE_BLOCKMAP_NO_TRACK, NULL);
425 if (error) goto out;
426 if (-1LL == blkno) continue;
427 fileblk = blkno * ref->blksize;
428 }
429 else if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR))
430 {
431 fileblk = f_offset;
432 filechunk = f_offset ? 0 : ref->filelength;
433 }
434
435 physoffset = 0;
436 while (physoffset < filechunk)
437 {
438 dk_physical_extent_t getphysreq;
439 bzero(&getphysreq, sizeof(getphysreq));
440
441 getphysreq.offset = fileblk + physoffset;
442 getphysreq.length = (filechunk - physoffset);
443 error = do_ioctl(p1, p2, DKIOCGETPHYSICALEXTENT, (caddr_t) &getphysreq);
444 if (error) goto out;
445 if (!target)
446 {
447 target = getphysreq.dev;
448 }
449 else if (target != getphysreq.dev)
450 {
451 error = ENOTSUP;
452 goto out;
453 }
454
455 assert(getphysreq.offset >= minoffset);
456
457#if HIBFRAGMENT
458 uint64_t rev;
459 for (rev = 4096; rev <= getphysreq.length; rev += 4096)
460 {
461 callback(callback_ref, getphysreq.offset + getphysreq.length - rev, 4096);
462 }
463#else
464 callback(callback_ref, getphysreq.offset, getphysreq.length);
465#endif
466 physoffset += getphysreq.length;
467 }
468 }
469 if (ref->wbcranged)
470 {
471 uint32_t idx;
472 for (idx = 0; idx < wbc_range.count; idx++)
473 {
474 assert(wbc_range.extents[idx].offset >= minoffset);
475 callback(callback_ref, wbc_range.extents[idx].offset, wbc_range.extents[idx].length);
476 }
477 }
478 callback(callback_ref, 0ULL, 0ULL);
479
480 if (ref->vp->v_type == VREG) p1 = &target;
481 else
482 {
483 p1 = &target;
484 p2 = p;
485 do_ioctl = &file_ioctl;
486 }
487
488 // get partition base
489
490 if (partitionbase_result)
491 {
492 error = do_ioctl(p1, p2, DKIOCGETBASE, (caddr_t) partitionbase_result);
493 if (error)
494 goto out;
495 }
496
497 // get block size & constraints
498
499 error = do_ioctl(p1, p2, DKIOCGETBLOCKSIZE, (caddr_t) &blksize);
500 if (error)
501 goto out;
502
503 maxiocount = 1*1024*1024*1024;
504
505 error = do_ioctl(p1, p2, DKIOCGETMAXBLOCKCOUNTREAD, (caddr_t) &count);
506 if (error)
507 count = 0;
508 count *= blksize;
509 if (count && (count < maxiocount))
510 maxiocount = count;
511
512 error = do_ioctl(p1, p2, DKIOCGETMAXBLOCKCOUNTWRITE, (caddr_t) &count);
513 if (error)
514 count = 0;
515 count *= blksize;
516 if (count && (count < maxiocount))
517 maxiocount = count;
518
519 error = do_ioctl(p1, p2, DKIOCGETMAXBYTECOUNTREAD, (caddr_t) &count);
520 if (error)
521 count = 0;
522 if (count && (count < maxiocount))
523 maxiocount = count;
524
525 error = do_ioctl(p1, p2, DKIOCGETMAXBYTECOUNTWRITE, (caddr_t) &count);
526 if (error)
527 count = 0;
528 if (count && (count < maxiocount))
529 maxiocount = count;
530
531 error = do_ioctl(p1, p2, DKIOCGETMAXSEGMENTBYTECOUNTREAD, (caddr_t) &count);
532 if (!error)
533 error = do_ioctl(p1, p2, DKIOCGETMAXSEGMENTCOUNTREAD, (caddr_t) &segcount);
534 if (error)
535 count = segcount = 0;
536 count *= segcount;
537 if (count && (count < maxiocount))
538 maxiocount = count;
539
540 error = do_ioctl(p1, p2, DKIOCGETMAXSEGMENTBYTECOUNTWRITE, (caddr_t) &count);
541 if (!error)
542 error = do_ioctl(p1, p2, DKIOCGETMAXSEGMENTCOUNTWRITE, (caddr_t) &segcount);
543 if (error)
544 count = segcount = 0;
545 count *= segcount;
546 if (count && (count < maxiocount))
547 maxiocount = count;
548
549 kprintf("max io 0x%qx bytes\n", maxiocount);
550 if (maxiocount_result)
551 *maxiocount_result = maxiocount;
552
553 error = do_ioctl(p1, p2, DKIOCISSOLIDSTATE, (caddr_t)&isssd);
554 if (!error && isssd)
555 flags |= kIOPolledFileSSD;
556
557 if (partition_device_result)
558 *partition_device_result = device;
559 if (image_device_result)
560 *image_device_result = target;
561 if (oflags)
562 *oflags = flags;
563
564 if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR))
565 {
566 vnode_close(ref->vp, FWRITE, ref->ctx);
567 ref->vp = NULLVP;
568 ref->ctx = NULL;
569 }
570
571out:
572 printf("kern_open_file_for_direct_io(%p, %d)\n", ref, error);
573
574
575 if (error && locked)
576 {
577 p1 = &device;
578 (void) do_ioctl(p1, p2, DKIOCUNLOCKPHYSICALEXTENTS, NULL);
579 }
580
581 if (error && ref)
582 {
583 if (ref->vp)
584 {
585 (void) kern_ioctl_file_extents(ref, _DKIOCCSUNPINEXTENT, 0, (ref->pinned && ref->cf) ? ref->filelength : 0);
586
587 if (ref->frozen)
588 {
589 (void) VNOP_IOCTL(ref->vp, FSCTL_THAW_EXTENTS, NULL, 0, ref->ctx);
590 }
591 if (ref->wbcranged)
592 {
593 (void) do_ioctl(p1, p2, DKIOCAPFSRELEASEWBCRANGE, (caddr_t) NULL);
594 }
595 vnode_close(ref->vp, FWRITE, ref->ctx);
596 ref->vp = NULLVP;
597 }
598 ref->ctx = NULL;
599 kfree(ref, sizeof(struct kern_direct_file_io_ref_t));
600 ref = NULL;
601 }
602
603 return(ref);
604}
605
606int
607kern_write_file(struct kern_direct_file_io_ref_t * ref, off_t offset, void * addr, size_t len, int ioflag)
608{
609 return (vn_rdwr(UIO_WRITE, ref->vp,
610 addr, len, offset,
611 UIO_SYSSPACE, ioflag|IO_SYNC|IO_NODELOCKED|IO_UNIT,
612 vfs_context_ucred(ref->ctx), (int *) 0,
613 vfs_context_proc(ref->ctx)));
614}
615
616int
617kern_read_file(struct kern_direct_file_io_ref_t * ref, off_t offset, void * addr, size_t len, int ioflag)
618{
619 return (vn_rdwr(UIO_READ, ref->vp,
620 addr, len, offset,
621 UIO_SYSSPACE, ioflag|IO_SYNC|IO_NODELOCKED|IO_UNIT,
622 vfs_context_ucred(ref->ctx), (int *) 0,
623 vfs_context_proc(ref->ctx)));
624}
625
626
627struct mount *
628kern_file_mount(struct kern_direct_file_io_ref_t * ref)
629{
630 return (ref->vp->v_mount);
631}
632
633void
634kern_close_file_for_direct_io(struct kern_direct_file_io_ref_t * ref,
635 off_t write_offset, void * addr, size_t write_length,
636 off_t discard_offset, off_t discard_end)
637{
638 int error;
639 printf("kern_close_file_for_direct_io(%p)\n", ref);
640
641 if (!ref) return;
642
643 if (ref->vp)
644 {
645 int (*do_ioctl)(void * p1, void * p2, u_long theIoctl, caddr_t result);
646 void * p1;
647 void * p2;
648
649 discard_offset = ((discard_offset + ref->blksize - 1) & ~(((off_t) ref->blksize) - 1));
650 discard_end = ((discard_end) & ~(((off_t) ref->blksize) - 1));
651
652 if (ref->vp->v_type == VREG)
653 {
654 p1 = &ref->device;
655 p2 = kernproc;
656 do_ioctl = &file_ioctl;
657 }
658 else
659 {
660 /* Partition. */
661 p1 = ref->vp;
662 p2 = ref->ctx;
663 do_ioctl = &device_ioctl;
664 }
665 (void) do_ioctl(p1, p2, DKIOCUNLOCKPHYSICALEXTENTS, NULL);
666
667 //XXX If unmapping extents then don't also need to unpin; except ...
668 //XXX if file unaligned (HFS 4k / Fusion 128k) then pin is superset and
669 //XXX unmap is subset, so save extra walk over file extents (and the risk
670 //XXX that CF drain starts) vs leaving partial units pinned to SSD
671 //XXX (until whatever was sharing also unmaps). Err on cleaning up fully.
672 boolean_t will_unmap = (!ref->pinned || ref->cf) && (discard_end > discard_offset);
673 boolean_t will_unpin = (ref->pinned && ref->cf /* && !will_unmap */);
674
675 (void) kern_ioctl_file_extents(ref, _DKIOCCSUNPINEXTENT, 0, (will_unpin) ? ref->filelength : 0);
676
677 if (will_unmap)
678 {
679 (void) kern_ioctl_file_extents(ref, DKIOCUNMAP, discard_offset, (ref->cf) ? ref->filelength : discard_end);
680 }
681
682 if (ref->frozen)
683 {
684 (void) VNOP_IOCTL(ref->vp, FSCTL_THAW_EXTENTS, NULL, 0, ref->ctx);
685 }
686 if (ref->wbcranged)
687 {
688 (void) do_ioctl(p1, p2, DKIOCAPFSRELEASEWBCRANGE, (caddr_t) NULL);
689 }
690
691 if (addr && write_length)
692 {
693 (void) kern_write_file(ref, write_offset, addr, write_length, IO_SKIP_ENCRYPTION);
694 }
695
696 error = vnode_close(ref->vp, FWRITE, ref->ctx);
697
698 ref->vp = NULLVP;
699 kprintf("vnode_close(%d)\n", error);
700
701 }
702
703 ref->ctx = NULL;
704
705 kfree(ref, sizeof(struct kern_direct_file_io_ref_t));
706}
707