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 55#include <mach-o/loader.h> 56#include <mach-o/nlist.h> 57 58#include <kern/kalloc.h> 59#include <vm/vm_kern.h> 60#include <pexpert/pexpert.h> 61#include <IOKit/IOHibernatePrivate.h> 62 63/* This function is called from kern_sysctl in the current process context; 64 * it is exported with the System6.0.exports, but this appears to be a legacy 65 * export, as there are no internal consumers. 66 */ 67int 68get_kernel_symfile(__unused proc_t p, __unused char const **symfile); 69int 70get_kernel_symfile(__unused proc_t p, __unused char const **symfile) 71{ 72 return KERN_FAILURE; 73} 74 75struct kern_direct_file_io_ref_t 76{ 77 vfs_context_t ctx; 78 struct vnode * vp; 79 dev_t device; 80 uint32_t blksize; 81 off_t filelength; 82 char pinned; 83}; 84 85 86static int file_ioctl(void * p1, void * p2, u_long theIoctl, caddr_t result) 87{ 88 dev_t device = *(dev_t*) p1; 89 90 return ((*bdevsw[major(device)].d_ioctl) 91 (device, theIoctl, result, S_IFBLK, p2)); 92} 93 94static int device_ioctl(void * p1, __unused void * p2, u_long theIoctl, caddr_t result) 95{ 96 return (VNOP_IOCTL(p1, theIoctl, result, 0, p2)); 97} 98 99static int 100kern_ioctl_file_extents(struct kern_direct_file_io_ref_t * ref, u_long theIoctl, off_t offset, off_t end) 101{ 102 int error; 103 int (*do_ioctl)(void * p1, void * p2, u_long theIoctl, caddr_t result); 104 void * p1; 105 void * p2; 106 uint64_t fileblk; 107 size_t filechunk; 108 dk_extent_t extent; 109 dk_unmap_t unmap; 110 _dk_cs_pin_t pin; 111 112 bzero(&extent, sizeof(dk_extent_t)); 113 bzero(&unmap, sizeof(dk_unmap_t)); 114 bzero(&pin, sizeof(pin)); 115 if (ref->vp->v_type == VREG) 116 { 117 p1 = &ref->device; 118 p2 = kernproc; 119 do_ioctl = &file_ioctl; 120 } 121 else 122 { 123 /* Partition. */ 124 p1 = ref->vp; 125 p2 = ref->ctx; 126 do_ioctl = &device_ioctl; 127 } 128 while (offset < end) 129 { 130 if (ref->vp->v_type == VREG) 131 { 132 daddr64_t blkno; 133 filechunk = 1*1024*1024*1024; 134 if (filechunk > (size_t)(end - offset)) 135 filechunk = (size_t)(end - offset); 136 error = VNOP_BLOCKMAP(ref->vp, offset, filechunk, &blkno, &filechunk, NULL, 0, NULL); 137 if (error) break; 138 fileblk = blkno * ref->blksize; 139 } 140 else if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR)) 141 { 142 fileblk = offset; 143 filechunk = ref->filelength; 144 } 145 146 if (DKIOCUNMAP == theIoctl) 147 { 148 extent.offset = fileblk; 149 extent.length = filechunk; 150 unmap.extents = &extent; 151 unmap.extentsCount = 1; 152 error = do_ioctl(p1, p2, theIoctl, (caddr_t)&unmap); 153// printf("DKIOCUNMAP(%d) 0x%qx, 0x%qx\n", error, extent.offset, extent.length); 154 } 155 else if (_DKIOCCSPINEXTENT == theIoctl) 156 { 157 pin.cp_extent.offset = fileblk; 158 pin.cp_extent.length = filechunk; 159 pin.cp_flags = _DKIOCSPINDISCARDDATA; 160 error = do_ioctl(p1, p2, theIoctl, (caddr_t)&pin); 161 if (error && (ENOTTY != error)) 162 { 163 printf("_DKIOCCSPINEXTENT(%d) 0x%qx, 0x%qx\n", 164 error, pin.cp_extent.offset, pin.cp_extent.length); 165 } 166 } 167 else error = EINVAL; 168 169 if (error) break; 170 offset += filechunk; 171 } 172 return (error); 173} 174 175int 176kern_write_file(struct kern_direct_file_io_ref_t * ref, off_t offset, caddr_t addr, vm_size_t len); 177 178struct kern_direct_file_io_ref_t * 179kern_open_file_for_direct_io(const char * name, 180 kern_get_file_extents_callback_t callback, 181 void * callback_ref, 182 off_t set_file_size, 183 off_t write_file_offset, 184 caddr_t write_file_addr, 185 vm_size_t write_file_len, 186 dev_t * partition_device_result, 187 dev_t * image_device_result, 188 uint64_t * partitionbase_result, 189 uint64_t * maxiocount_result, 190 uint32_t * oflags) 191{ 192 struct kern_direct_file_io_ref_t * ref; 193 194 proc_t p; 195 struct vnode_attr va; 196 int error; 197 off_t f_offset; 198 uint64_t fileblk; 199 size_t filechunk; 200 uint64_t physoffset; 201 dev_t device; 202 dev_t target = 0; 203 int isssd = 0; 204 uint32_t flags = 0; 205 uint32_t blksize; 206 off_t maxiocount, count, segcount; 207 boolean_t locked = FALSE; 208 209 int (*do_ioctl)(void * p1, void * p2, u_long theIoctl, caddr_t result); 210 void * p1 = NULL; 211 void * p2 = NULL; 212 213 error = EFAULT; 214 215 ref = (struct kern_direct_file_io_ref_t *) kalloc(sizeof(struct kern_direct_file_io_ref_t)); 216 if (!ref) 217 { 218 error = EFAULT; 219 goto out; 220 } 221 222 bzero(ref, sizeof(*ref)); 223 p = kernproc; 224 ref->ctx = vfs_context_create(vfs_context_current()); 225 226 if ((error = vnode_open(name, (O_CREAT | FWRITE), (0), 0, &ref->vp, ref->ctx))) 227 goto out; 228 229 if (ref->vp->v_type == VREG) 230 { 231 vnode_lock_spin(ref->vp); 232 SET(ref->vp->v_flag, VSWAP); 233 vnode_unlock(ref->vp); 234 } 235 236 if (write_file_addr && write_file_len) 237 { 238 if ((error = kern_write_file(ref, write_file_offset, write_file_addr, write_file_len))) 239 goto out; 240 } 241 242 VATTR_INIT(&va); 243 VATTR_WANTED(&va, va_rdev); 244 VATTR_WANTED(&va, va_fsid); 245 VATTR_WANTED(&va, va_data_size); 246 VATTR_WANTED(&va, va_data_alloc); 247 VATTR_WANTED(&va, va_nlink); 248 error = EFAULT; 249 if (vnode_getattr(ref->vp, &va, ref->ctx)) 250 goto out; 251 252 kprintf("vp va_rdev major %d minor %d\n", major(va.va_rdev), minor(va.va_rdev)); 253 kprintf("vp va_fsid major %d minor %d\n", major(va.va_fsid), minor(va.va_fsid)); 254 kprintf("vp size %qd alloc %qd\n", va.va_data_size, va.va_data_alloc); 255 256 if (ref->vp->v_type == VREG) 257 { 258 /* Don't dump files with links. */ 259 if (va.va_nlink != 1) 260 goto out; 261 262 device = va.va_fsid; 263 ref->filelength = va.va_data_size; 264 265 p1 = &device; 266 p2 = p; 267 do_ioctl = &file_ioctl; 268 269 if (set_file_size && (set_file_size != (off_t) va.va_data_alloc)) 270 { 271 u_int32_t alloc_flags = PREALLOCATE | ALLOCATEFROMPEOF | ALLOCATEALL; 272 273 vnode_lock_spin(ref->vp); 274 CLR(ref->vp->v_flag, VSWAP); 275 vnode_unlock(ref->vp); 276 277 if (set_file_size < (off_t) va.va_data_alloc) 278 { 279 struct vnode_attr setva; 280 VATTR_INIT(&setva); 281 VATTR_SET(&setva, va_data_size, set_file_size); 282 error = vnode_setattr(ref->vp, &setva, ref->ctx); 283 } 284 else 285 { 286 off_t bytesallocated = set_file_size - va.va_data_alloc; 287 error = VNOP_ALLOCATE(ref->vp, bytesallocated, alloc_flags, 288 &bytesallocated, 0 /*fst_offset*/, 289 ref->ctx); 290 HIBLOG("VNOP_ALLOCATE(%d) %qd\n", error, bytesallocated); 291 } 292 // F_SETSIZE: 293 (void) vnode_setsize(ref->vp, set_file_size, IO_NOZEROFILL, ref->ctx); 294 ref->filelength = set_file_size; 295 296 vnode_lock_spin(ref->vp); 297 SET(ref->vp->v_flag, VSWAP); 298 vnode_unlock(ref->vp); 299 } 300 } 301 else if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR)) 302 { 303 /* Partition. */ 304 device = va.va_rdev; 305 306 p1 = ref->vp; 307 p2 = ref->ctx; 308 do_ioctl = &device_ioctl; 309 } 310 else 311 { 312 /* Don't dump to non-regular files. */ 313 error = EFAULT; 314 goto out; 315 } 316 ref->device = device; 317 318 // get block size 319 320 error = do_ioctl(p1, p2, DKIOCGETBLOCKSIZE, (caddr_t) &ref->blksize); 321 if (error) 322 goto out; 323 324 if (ref->vp->v_type != VREG) 325 { 326 error = do_ioctl(p1, p2, DKIOCGETBLOCKCOUNT, (caddr_t) &fileblk); 327 if (error) 328 goto out; 329 ref->filelength = fileblk * ref->blksize; 330 } 331 332 // pin logical extents 333 334 error = kern_ioctl_file_extents(ref, _DKIOCCSPINEXTENT, 0, ref->filelength); 335 if (error && (ENOTTY != error)) goto out; 336 ref->pinned = (error == 0); 337 338 // generate the block list 339 340 error = do_ioctl(p1, p2, DKIOCLOCKPHYSICALEXTENTS, NULL); 341 if (error) 342 goto out; 343 locked = TRUE; 344 345 f_offset = 0; 346 while (f_offset < ref->filelength) 347 { 348 if (ref->vp->v_type == VREG) 349 { 350 filechunk = 1*1024*1024*1024; 351 daddr64_t blkno; 352 353 error = VNOP_BLOCKMAP(ref->vp, f_offset, filechunk, &blkno, &filechunk, NULL, 0, NULL); 354 if (error) 355 goto out; 356 357 fileblk = blkno * ref->blksize; 358 } 359 else if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR)) 360 { 361 fileblk = f_offset; 362 filechunk = f_offset ? 0 : ref->filelength; 363 } 364 365 physoffset = 0; 366 while (physoffset < filechunk) 367 { 368 dk_physical_extent_t getphysreq; 369 bzero(&getphysreq, sizeof(getphysreq)); 370 371 getphysreq.offset = fileblk + physoffset; 372 getphysreq.length = (filechunk - physoffset); 373 error = do_ioctl(p1, p2, DKIOCGETPHYSICALEXTENT, (caddr_t) &getphysreq); 374 if (error) 375 goto out; 376 if (!target) 377 { 378 target = getphysreq.dev; 379 } 380 else if (target != getphysreq.dev) 381 { 382 error = ENOTSUP; 383 goto out; 384 } 385#if HIBFRAGMENT 386 uint64_t rev; 387 for (rev = 4096; rev <= getphysreq.length; rev += 4096) 388 { 389 callback(callback_ref, getphysreq.offset + getphysreq.length - rev, 4096); 390 } 391#else 392 callback(callback_ref, getphysreq.offset, getphysreq.length); 393#endif 394 physoffset += getphysreq.length; 395 } 396 f_offset += filechunk; 397 } 398 callback(callback_ref, 0ULL, 0ULL); 399 400 if (ref->vp->v_type == VREG) 401 p1 = ⌖ 402 403 // get partition base 404 405 error = do_ioctl(p1, p2, DKIOCGETBASE, (caddr_t) partitionbase_result); 406 if (error) 407 goto out; 408 409 // get block size & constraints 410 411 error = do_ioctl(p1, p2, DKIOCGETBLOCKSIZE, (caddr_t) &blksize); 412 if (error) 413 goto out; 414 415 maxiocount = 1*1024*1024*1024; 416 417 error = do_ioctl(p1, p2, DKIOCGETMAXBLOCKCOUNTREAD, (caddr_t) &count); 418 if (error) 419 count = 0; 420 count *= blksize; 421 if (count && (count < maxiocount)) 422 maxiocount = count; 423 424 error = do_ioctl(p1, p2, DKIOCGETMAXBLOCKCOUNTWRITE, (caddr_t) &count); 425 if (error) 426 count = 0; 427 count *= blksize; 428 if (count && (count < maxiocount)) 429 maxiocount = count; 430 431 error = do_ioctl(p1, p2, DKIOCGETMAXBYTECOUNTREAD, (caddr_t) &count); 432 if (error) 433 count = 0; 434 if (count && (count < maxiocount)) 435 maxiocount = count; 436 437 error = do_ioctl(p1, p2, DKIOCGETMAXBYTECOUNTWRITE, (caddr_t) &count); 438 if (error) 439 count = 0; 440 if (count && (count < maxiocount)) 441 maxiocount = count; 442 443 error = do_ioctl(p1, p2, DKIOCGETMAXSEGMENTBYTECOUNTREAD, (caddr_t) &count); 444 if (!error) 445 error = do_ioctl(p1, p2, DKIOCGETMAXSEGMENTCOUNTREAD, (caddr_t) &segcount); 446 if (error) 447 count = segcount = 0; 448 count *= segcount; 449 if (count && (count < maxiocount)) 450 maxiocount = count; 451 452 error = do_ioctl(p1, p2, DKIOCGETMAXSEGMENTBYTECOUNTWRITE, (caddr_t) &count); 453 if (!error) 454 error = do_ioctl(p1, p2, DKIOCGETMAXSEGMENTCOUNTWRITE, (caddr_t) &segcount); 455 if (error) 456 count = segcount = 0; 457 count *= segcount; 458 if (count && (count < maxiocount)) 459 maxiocount = count; 460 461 kprintf("max io 0x%qx bytes\n", maxiocount); 462 if (maxiocount_result) 463 *maxiocount_result = maxiocount; 464 465 error = do_ioctl(p1, p2, DKIOCISSOLIDSTATE, (caddr_t)&isssd); 466 if (!error && isssd) 467 flags |= kIOHibernateOptionSSD; 468 469 if (partition_device_result) 470 *partition_device_result = device; 471 if (image_device_result) 472 *image_device_result = target; 473 if (flags) 474 *oflags = flags; 475 476out: 477 kprintf("kern_open_file_for_direct_io(%d)\n", error); 478 479 if (error && locked) 480 { 481 p1 = &device; 482 (void) do_ioctl(p1, p2, DKIOCUNLOCKPHYSICALEXTENTS, NULL); 483 } 484 485 if (error && ref) 486 { 487 if (ref->vp) 488 { 489 vnode_close(ref->vp, FWRITE, ref->ctx); 490 ref->vp = NULLVP; 491 } 492 vfs_context_rele(ref->ctx); 493 kfree(ref, sizeof(struct kern_direct_file_io_ref_t)); 494 ref = NULL; 495 } 496 497 return(ref); 498} 499 500int 501kern_write_file(struct kern_direct_file_io_ref_t * ref, off_t offset, caddr_t addr, vm_size_t len) 502{ 503 return (vn_rdwr(UIO_WRITE, ref->vp, 504 addr, len, offset, 505 UIO_SYSSPACE, IO_SYNC|IO_NODELOCKED|IO_UNIT, 506 vfs_context_ucred(ref->ctx), (int *) 0, 507 vfs_context_proc(ref->ctx))); 508} 509 510 511void 512kern_close_file_for_direct_io(struct kern_direct_file_io_ref_t * ref, 513 off_t write_offset, caddr_t addr, vm_size_t write_length, 514 off_t discard_offset, off_t discard_end) 515{ 516 int error; 517 kprintf("kern_close_file_for_direct_io\n"); 518 519 if (!ref) return; 520 521 if (ref->vp) 522 { 523 int (*do_ioctl)(void * p1, void * p2, u_long theIoctl, caddr_t result); 524 void * p1; 525 void * p2; 526 527 if (ref->vp->v_type == VREG) 528 { 529 p1 = &ref->device; 530 p2 = kernproc; 531 do_ioctl = &file_ioctl; 532 } 533 else 534 { 535 /* Partition. */ 536 p1 = ref->vp; 537 p2 = ref->ctx; 538 do_ioctl = &device_ioctl; 539 } 540 (void) do_ioctl(p1, p2, DKIOCUNLOCKPHYSICALEXTENTS, NULL); 541 542 if (discard_offset && discard_end && !ref->pinned) 543 { 544 (void) kern_ioctl_file_extents(ref, DKIOCUNMAP, discard_offset, discard_end); 545 } 546 if (addr && write_length) 547 { 548 (void) kern_write_file(ref, write_offset, addr, write_length); 549 } 550 551 error = vnode_close(ref->vp, FWRITE, ref->ctx); 552 553 ref->vp = NULLVP; 554 kprintf("vnode_close(%d)\n", error); 555 } 556 vfs_context_rele(ref->ctx); 557 ref->ctx = NULL; 558 kfree(ref, sizeof(struct kern_direct_file_io_ref_t)); 559} 560 561