+void
+ubc_info_deallocate(struct ubc_info *uip)
+{
+ ubc_info_free(uip);
+}
+
+errno_t mach_to_bsd_errno(kern_return_t mach_err)
+{
+ switch (mach_err) {
+ case KERN_SUCCESS:
+ return 0;
+
+ case KERN_INVALID_ADDRESS:
+ case KERN_INVALID_ARGUMENT:
+ case KERN_NOT_IN_SET:
+ case KERN_INVALID_NAME:
+ case KERN_INVALID_TASK:
+ case KERN_INVALID_RIGHT:
+ case KERN_INVALID_VALUE:
+ case KERN_INVALID_CAPABILITY:
+ case KERN_INVALID_HOST:
+ case KERN_MEMORY_PRESENT:
+ case KERN_INVALID_PROCESSOR_SET:
+ case KERN_INVALID_POLICY:
+ case KERN_ALREADY_WAITING:
+ case KERN_DEFAULT_SET:
+ case KERN_EXCEPTION_PROTECTED:
+ case KERN_INVALID_LEDGER:
+ case KERN_INVALID_MEMORY_CONTROL:
+ case KERN_INVALID_SECURITY:
+ case KERN_NOT_DEPRESSED:
+ case KERN_LOCK_OWNED:
+ case KERN_LOCK_OWNED_SELF:
+ return EINVAL;
+
+ case KERN_PROTECTION_FAILURE:
+ case KERN_NOT_RECEIVER:
+ case KERN_NO_ACCESS:
+ case KERN_POLICY_STATIC:
+ return EACCES;
+
+ case KERN_NO_SPACE:
+ case KERN_RESOURCE_SHORTAGE:
+ case KERN_UREFS_OVERFLOW:
+ case KERN_INVALID_OBJECT:
+ return ENOMEM;
+
+ case KERN_FAILURE:
+ return EIO;
+
+ case KERN_MEMORY_FAILURE:
+ case KERN_POLICY_LIMIT:
+ case KERN_CODESIGN_ERROR:
+ return EPERM;
+
+ case KERN_MEMORY_ERROR:
+ return EBUSY;
+
+ case KERN_ALREADY_IN_SET:
+ case KERN_NAME_EXISTS:
+ case KERN_RIGHT_EXISTS:
+ return EEXIST;
+
+ case KERN_ABORTED:
+ return EINTR;
+
+ case KERN_TERMINATED:
+ case KERN_LOCK_SET_DESTROYED:
+ case KERN_LOCK_UNSTABLE:
+ case KERN_SEMAPHORE_DESTROYED:
+ return ENOENT;
+
+ case KERN_RPC_SERVER_TERMINATED:
+ return ECONNRESET;
+
+ case KERN_NOT_SUPPORTED:
+ return ENOTSUP;
+
+ case KERN_NODE_DOWN:
+ return ENETDOWN;
+
+ case KERN_NOT_WAITING:
+ return ENOENT;
+
+ case KERN_OPERATION_TIMED_OUT:
+ return ETIMEDOUT;
+
+ default:
+ return EIO;
+ }
+}
+
+/*
+ * ubc_setsize_ex
+ *
+ * Tell the VM that the the size of the file represented by the vnode has
+ * changed
+ *
+ * Parameters: vp The vp whose backing file size is
+ * being changed
+ * nsize The new size of the backing file
+ * opts Options
+ *
+ * Returns: EINVAL for new size < 0
+ * ENOENT if no UBC info exists
+ * EAGAIN if UBC_SETSIZE_NO_FS_REENTRY option is set and new_size < old size
+ * Other errors (mapped to errno_t) returned by VM functions
+ *
+ * Notes: This function will indicate success if the new size is the
+ * same or larger than the old size (in this case, the
+ * remainder of the file will require modification or use of
+ * an existing upl to access successfully).
+ *
+ * This function will fail if the new file size is smaller,
+ * and the memory region being invalidated was unable to
+ * actually be invalidated and/or the last page could not be
+ * flushed, if the new size is not aligned to a page
+ * boundary. This is usually indicative of an I/O error.
+ */
+errno_t ubc_setsize_ex(struct vnode *vp, off_t nsize, ubc_setsize_opts_t opts)
+{
+ off_t osize; /* ui_size before change */
+ off_t lastpg, olastpgend, lastoff;
+ struct ubc_info *uip;
+ memory_object_control_t control;
+ kern_return_t kret = KERN_SUCCESS;
+
+ if (nsize < (off_t)0)
+ return EINVAL;
+
+ if (!UBCINFOEXISTS(vp))
+ return ENOENT;
+
+ uip = vp->v_ubcinfo;
+ osize = uip->ui_size;
+
+ if (ISSET(opts, UBC_SETSIZE_NO_FS_REENTRY) && nsize < osize)
+ return EAGAIN;
+
+ /*
+ * Update the size before flushing the VM
+ */
+ uip->ui_size = nsize;
+
+ if (nsize >= osize) { /* Nothing more to do */
+ if (nsize > osize) {
+ lock_vnode_and_post(vp, NOTE_EXTEND);
+ }
+
+ return 0;
+ }
+
+ /*
+ * When the file shrinks, invalidate the pages beyond the
+ * new size. Also get rid of garbage beyond nsize on the
+ * last page. The ui_size already has the nsize, so any
+ * subsequent page-in will zero-fill the tail properly
+ */
+ lastpg = trunc_page_64(nsize);
+ olastpgend = round_page_64(osize);
+ control = uip->ui_control;
+ assert(control);
+ lastoff = (nsize & PAGE_MASK_64);
+
+ if (lastoff) {
+ upl_t upl;
+ upl_page_info_t *pl;
+
+ /*
+ * new EOF ends up in the middle of a page
+ * zero the tail of this page if it's currently
+ * present in the cache
+ */
+ kret = ubc_create_upl(vp, lastpg, PAGE_SIZE, &upl, &pl, UPL_SET_LITE);
+
+ if (kret != KERN_SUCCESS)
+ panic("ubc_setsize: ubc_create_upl (error = %d)\n", kret);
+
+ if (upl_valid_page(pl, 0))
+ cluster_zero(upl, (uint32_t)lastoff, PAGE_SIZE - (uint32_t)lastoff, NULL);
+
+ ubc_upl_abort_range(upl, 0, PAGE_SIZE, UPL_ABORT_FREE_ON_EMPTY);
+
+ lastpg += PAGE_SIZE_64;
+ }
+ if (olastpgend > lastpg) {
+ int flags;
+
+ if (lastpg == 0)
+ flags = MEMORY_OBJECT_DATA_FLUSH_ALL;
+ else
+ flags = MEMORY_OBJECT_DATA_FLUSH;
+ /*
+ * invalidate the pages beyond the new EOF page
+ *
+ */
+ kret = memory_object_lock_request(control,
+ (memory_object_offset_t)lastpg,
+ (memory_object_size_t)(olastpgend - lastpg), NULL, NULL,
+ MEMORY_OBJECT_RETURN_NONE, flags, VM_PROT_NO_CHANGE);
+ if (kret != KERN_SUCCESS)
+ printf("ubc_setsize: invalidate failed (error = %d)\n", kret);
+ }
+ return mach_to_bsd_errno(kret);
+}
+
+// Returns true for success
+int ubc_setsize(vnode_t vp, off_t nsize)
+{
+ return ubc_setsize_ex(vp, nsize, 0) == 0;
+}
+
+/*
+ * ubc_getsize
+ *
+ * Get the size of the file assocated with the specified vnode
+ *
+ * Parameters: vp The vnode whose size is of interest
+ *
+ * Returns: 0 There is no ubc_info associated with
+ * this vnode, or the size is zero
+ * !0 The size of the file
+ *
+ * Notes: Using this routine, it is not possible for a caller to
+ * successfully distinguish between a vnode associate with a zero
+ * length file, and a vnode with no associated ubc_info. The
+ * caller therefore needs to not care, or needs to ensure that
+ * they have previously successfully called ubc_info_init() or
+ * ubc_info_init_withsize().
+ */
+off_t
+ubc_getsize(struct vnode *vp)
+{
+ /* people depend on the side effect of this working this way
+ * as they call this for directory
+ */
+ if (!UBCINFOEXISTS(vp))
+ return ((off_t)0);
+ return (vp->v_ubcinfo->ui_size);
+}
+
+
+/*
+ * ubc_umount
+ *
+ * Call ubc_msync(vp, 0, EOF, NULL, UBC_PUSHALL) on all the vnodes for this
+ * mount point
+ *
+ * Parameters: mp The mount point
+ *
+ * Returns: 0 Success
+ *
+ * Notes: There is no failure indication for this function.
+ *
+ * This function is used in the unmount path; since it may block
+ * I/O indefinitely, it should not be used in the forced unmount
+ * path, since a device unavailability could also block that
+ * indefinitely.
+ *
+ * Because there is no device ejection interlock on USB, FireWire,
+ * or similar devices, it's possible that an ejection that begins
+ * subsequent to the vnode_iterate() completing, either on one of
+ * those devices, or a network mount for which the server quits
+ * responding, etc., may cause the caller to block indefinitely.
+ */
+__private_extern__ int
+ubc_umount(struct mount *mp)
+{
+ vnode_iterate(mp, 0, ubc_umcallback, 0);
+ return(0);
+}
+
+
+/*
+ * ubc_umcallback
+ *
+ * Used by ubc_umount() as an internal implementation detail; see ubc_umount()
+ * and vnode_iterate() for details of implementation.
+ */
+static int
+ubc_umcallback(vnode_t vp, __unused void * args)
+{
+
+ if (UBCINFOEXISTS(vp)) {
+
+ (void) ubc_msync(vp, (off_t)0, ubc_getsize(vp), NULL, UBC_PUSHALL);