- }
- 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->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
-
- error = kern_ioctl_file_extents(ref, _DKIOCCSPINEXTENT, 0, ref->filelength);
- if (error && (ENOTTY != error)) goto out;
- ref->pinned = (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, 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 : ref->filelength;
- }
-
- physoffset = 0;
- while (physoffset < filechunk)
- {
- dk_physical_extent_t getphysreq;
- bzero(&getphysreq, sizeof(getphysreq));
-
- getphysreq.offset = fileblk + physoffset;
- getphysreq.length = (filechunk - physoffset);
- error = do_ioctl(p1, p2, DKIOCGETPHYSICALEXTENT, (caddr_t) &getphysreq);
- if (error) goto out;
- if (!target)
- {
- target = getphysreq.dev;
- }
- else if (target != getphysreq.dev)
- {
- error = ENOTSUP;
- 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;
+ }
+
+ 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;
+ }
+
+ physoffset = 0;
+ while (physoffset < filechunk) {
+ dk_physical_extent_t getphysreq;
+ bzero(&getphysreq, sizeof(getphysreq));
+
+ getphysreq.offset = fileblk + physoffset;
+ getphysreq.length = (filechunk - physoffset);
+ error = do_ioctl(p1, p2, DKIOCGETPHYSICALEXTENT, (caddr_t) &getphysreq);
+ if (error) {
+ goto out;
+ }
+ if (!target) {
+ target = getphysreq.dev;
+ } else if (target != getphysreq.dev) {
+ error = ENOTSUP;
+ goto out;
+ }
+
+ assert(getphysreq.offset >= minoffset);
+