- goto out;
- }
-
- bzero(ref, sizeof(*ref));
- p = kernproc;
- ref->ctx = vfs_context_create(vfs_context_kernel());
-
- fmode = (create_file) ? (O_CREAT | FWRITE) : FWRITE;
- cmode = S_IRUSR | S_IWUSR;
- ndflags = NOFOLLOW;
- NDINIT(&nd, LOOKUP, OP_OPEN, ndflags, UIO_SYSSPACE, CAST_USER_ADDR_T(name), ref->ctx);
- VATTR_INIT(&va);
- VATTR_SET(&va, va_mode, cmode);
- VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWENCRYPTED);
- VATTR_SET(&va, va_dataprotect_class, PROTECTION_CLASS_D);
- if ((error = vn_open_auth(&nd, &fmode, &va))) goto out;
-
- ref->vp = nd.ni_vp;
- if (ref->vp->v_type == VREG)
- {
- vnode_lock_spin(ref->vp);
- SET(ref->vp->v_flag, VSWAP);
- vnode_unlock(ref->vp);
- }
-
- if (write_file_addr && write_file_len)
- {
- if ((error = kern_write_file(ref, write_file_offset, write_file_addr, write_file_len, 0))) goto out;
- }
-
- VATTR_INIT(&va);
- VATTR_WANTED(&va, va_rdev);
- VATTR_WANTED(&va, va_fsid);
- VATTR_WANTED(&va, va_devid);
- VATTR_WANTED(&va, va_data_size);
- VATTR_WANTED(&va, va_data_alloc);
- VATTR_WANTED(&va, va_nlink);
- error = EFAULT;
- if (vnode_getattr(ref->vp, &va, ref->ctx)) goto out;
-
- mpFree = freespace_mb(ref->vp);
- mpFree <<= 20;
- kprintf("kern_direct_file(%s): vp size %qd, alloc %qd, mp free %qd, keep free %qd\n",
- name, va.va_data_size, va.va_data_alloc, mpFree, fs_free_size);
-
- if (ref->vp->v_type == VREG)
- {
- /* Don't dump files with links. */
- if (va.va_nlink != 1) goto out;
-
- device = (VATTR_IS_SUPPORTED(&va, va_devid)) ? va.va_devid : va.va_fsid;
- ref->filelength = va.va_data_size;
-
- p1 = &device;
- p2 = p;
- do_ioctl = &file_ioctl;
-
- if (set_file_size)
- {
- if (fs_free_size)
- {
- mpFree += va.va_data_alloc;
- if ((mpFree < set_file_size) || ((mpFree - set_file_size) < fs_free_size))
- {
- error = ENOSPC;
- goto out;
+ if (vnode_getattr(ref->vp, &va, ref->ctx)) {
+ goto out;
+ }
+
+ wbctotal = 0;
+ mpFree = freespace_mb(ref->vp);
+ mpFree <<= 20;
+ kprintf("kern_direct_file(%s): vp size %qd, alloc %qd, mp free %qd, keep free %qd\n",
+ name, va.va_data_size, va.va_data_alloc, mpFree, fs_free_size);
+
+ if (ref->vp->v_type == VREG) {
+ /* Don't dump files with links. */
+ if (va.va_nlink != 1) {
+ goto out;
+ }
+
+ device = (VATTR_IS_SUPPORTED(&va, va_devid)) ? va.va_devid : va.va_fsid;
+ ref->filelength = va.va_data_size;
+
+ p1 = &device;
+ p2 = p;
+ do_ioctl = &file_ioctl;
+
+ if (kIOPolledFileHibernate & iflags) {
+ error = do_ioctl(p1, p2, DKIOCAPFSGETWBCRANGE, (caddr_t) &wbc_range);
+ ref->wbcranged = (error == 0);
+ }
+ if (ref->wbcranged) {
+ uint32_t idx;
+ assert(wbc_range.count <= (sizeof(wbc_range.extents) / sizeof(wbc_range.extents[0])));
+ for (idx = 0; idx < wbc_range.count; idx++) {
+ wbctotal += wbc_range.extents[idx].length;
+ }
+ kprintf("kern_direct_file(%s): wbc %qd\n", name, wbctotal);
+ if (wbctotal) {
+ target = wbc_range.dev;
+ }
+ }
+
+ if (set_file_size) {
+ if (wbctotal) {
+ if (wbctotal >= set_file_size) {
+ set_file_size = HIBERNATE_MIN_FILE_SIZE;
+ } else {
+ set_file_size -= wbctotal;
+ if (set_file_size < HIBERNATE_MIN_FILE_SIZE) {
+ set_file_size = HIBERNATE_MIN_FILE_SIZE;
+ }
+ }
+ }
+ if (fs_free_size) {
+ mpFree += va.va_data_alloc;
+ if ((mpFree < set_file_size) || ((mpFree - set_file_size) < fs_free_size)) {
+ error = ENOSPC;
+ goto out;
+ }
+ }
+ error = vnode_setsize(ref->vp, set_file_size, IO_NOZEROFILL | IO_NOAUTH, ref->ctx);
+ if (error) {
+ goto out;
+ }
+ ref->filelength = set_file_size;
+ }
+ } else if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR)) {
+ /* Partition. */
+ device = va.va_rdev;
+
+ p1 = ref->vp;
+ p2 = ref->ctx;
+ do_ioctl = &device_ioctl;
+ } else {
+ /* Don't dump to non-regular files. */
+ error = EFAULT;
+ goto out;
+ }
+ ref->device = device;
+
+ // probe for CF
+ dk_corestorage_info_t cs_info;
+ memset(&cs_info, 0, sizeof(dk_corestorage_info_t));
+ error = do_ioctl(p1, p2, DKIOCCORESTORAGE, (caddr_t)&cs_info);
+ ref->cf = (error == 0) && (cs_info.flags & DK_CORESTORAGE_ENABLE_HOTFILES);
+
+ // get block size
+
+ error = do_ioctl(p1, p2, DKIOCGETBLOCKSIZE, (caddr_t) &ref->blksize);
+ if (error) {
+ goto out;
+ }
+
+ if (ref->blksize == 4096) {
+ minoffset = HIBERNATE_MIN_PHYSICAL_LBA_4096 * ref->blksize;
+ } else {
+ minoffset = HIBERNATE_MIN_PHYSICAL_LBA_512 * ref->blksize;
+ }
+
+ if (ref->vp->v_type != VREG) {
+ error = do_ioctl(p1, p2, DKIOCGETBLOCKCOUNT, (caddr_t) &fileblk);
+ if (error) {
+ goto out;
+ }
+ ref->filelength = fileblk * ref->blksize;
+ }
+
+ // pin logical extents, CS version
+
+ error = kern_ioctl_file_extents(ref, _DKIOCCSPINEXTENT, 0, ref->filelength);
+ if (error && (ENOTTY != error)) {
+ goto out;
+ }
+ ref->pinned = (error == 0);
+
+ // pin logical extents, apfs version
+
+ error = VNOP_IOCTL(ref->vp, FSCTL_FREEZE_EXTENTS, NULL, 0, ref->ctx);
+ if (error && (ENOTTY != error)) {
+ goto out;
+ }
+ ref->frozen = (error == 0);
+
+ // generate the block list
+
+ error = do_ioctl(p1, p2, DKIOCLOCKPHYSICALEXTENTS, NULL);
+ if (error) {
+ goto out;
+ }
+ locked = TRUE;
+
+ f_offset = 0;
+ for (; f_offset < ref->filelength; f_offset += filechunk) {
+ if (ref->vp->v_type == VREG) {
+ filechunk = 1 * 1024 * 1024 * 1024;
+ daddr64_t blkno;
+
+ error = VNOP_BLOCKMAP(ref->vp, f_offset, filechunk, &blkno,
+ &filechunk, NULL, VNODE_WRITE | VNODE_BLOCKMAP_NO_TRACK, NULL);
+ if (error) {
+ goto out;
+ }
+ if (-1LL == blkno) {
+ continue;
+ }
+ fileblk = blkno * ref->blksize;
+ } else if ((ref->vp->v_type == VBLK) || (ref->vp->v_type == VCHR)) {
+ fileblk = f_offset;
+ filechunk = f_offset ? 0 : (unsigned long)ref->filelength;