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