+ if (started_tr) {
+ hfs_end_transaction(hfsmp);
+ }
+
+ hfs_unlockpair(from_cp, to_cp);
+ return (error);
+}
+
+int
+hfs_vnop_mmap(struct vnop_mmap_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ int error;
+
+ if (VNODE_IS_RSRC(vp)) {
+ /* allow pageins of the resource fork */
+ } else {
+ int compressed = hfs_file_is_compressed(VTOC(vp), 1); /* 1 == don't take the cnode lock */
+ time_t orig_ctime = VTOC(vp)->c_ctime;
+
+ if (!compressed && (VTOC(vp)->c_bsdflags & UF_COMPRESSED)) {
+ error = check_for_dataless_file(vp, NAMESPACE_HANDLER_READ_OP);
+ if (error != 0) {
+ return error;
+ }
+ }
+
+ if (ap->a_fflags & PROT_WRITE) {
+ check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_WRITE_OP, NULL);
+ }
+ }
+
+ //
+ // NOTE: we return ENOTSUP because we want the cluster layer
+ // to actually do all the real work.
+ //
+ return (ENOTSUP);
+}
+
+/*
+ * hfs_movedata
+ *
+ * This is a non-symmetric variant of exchangedata. In this function,
+ * the contents of the fork in from_vp are moved to the fork
+ * specified by to_vp.
+ *
+ * The cnodes pointed to by 'from_vp' and 'to_vp' must be locked.
+ *
+ * The vnode pointed to by 'to_vp' *must* be empty prior to invoking this function.
+ * We impose this restriction because we may not be able to fully delete the entire
+ * file's contents in a single transaction, particularly if it has a lot of extents.
+ * In the normal file deletion codepath, the file is screened for two conditions:
+ * 1) bigger than 400MB, and 2) more than 8 extents. If so, the file is relocated to
+ * the hidden directory and the deletion is broken up into multiple truncates. We can't
+ * do that here because both files need to exist in the namespace. The main reason this
+ * is imposed is that we may have to touch a whole lot of bitmap blocks if there are
+ * many extents.
+ *
+ * Any data written to 'from_vp' after this call completes is not guaranteed
+ * to be moved.
+ *
+ * Arguments:
+ * vnode from_vp: source file
+ * vnode to_vp: destination file; must be empty
+ *
+ * Returns:
+ * EFBIG - Destination file was not empty
+ * 0 - success
+ *
+ *
+ */
+int hfs_movedata (struct vnode *from_vp, struct vnode *to_vp) {
+
+ struct cnode *from_cp;
+ struct cnode *to_cp;
+ struct hfsmount *hfsmp = NULL;
+ int error = 0;
+ int started_tr = 0;
+ int lockflags = 0;
+ int overflow_blocks;
+ int rsrc = 0;
+
+
+ /* Get the HFS pointers */
+ from_cp = VTOC(from_vp);
+ to_cp = VTOC(to_vp);
+ hfsmp = VTOHFS(from_vp);
+
+ /* Verify that neither source/dest file is open-unlinked */
+ if (from_cp->c_flag & (C_DELETED | C_NOEXISTS)) {
+ error = EBUSY;
+ goto movedata_exit;
+ }
+
+ if (to_cp->c_flag & (C_DELETED | C_NOEXISTS)) {
+ error = EBUSY;
+ goto movedata_exit;
+ }
+
+ /*
+ * Verify the source file is not in use by anyone besides us.
+ *
+ * This function is typically invoked by a namespace handler
+ * process responding to a temporarily stalled system call.
+ * The FD that it is working off of is opened O_EVTONLY, so
+ * it really has no active usecounts (the kusecount from O_EVTONLY
+ * is subtracted from the total usecounts).
+ *
+ * As a result, we shouldn't have any active usecounts against
+ * this vnode when we go to check it below.
+ */
+ if (vnode_isinuse(from_vp, 0)) {
+ error = EBUSY;
+ goto movedata_exit;
+ }
+
+ if (from_cp->c_rsrc_vp == from_vp) {
+ rsrc = 1;
+ }
+
+ /*
+ * We assume that the destination file is already empty.
+ * Verify that it is.
+ */
+ if (rsrc) {
+ if (to_cp->c_rsrcfork->ff_size > 0) {
+ error = EFBIG;
+ goto movedata_exit;
+ }
+ }
+ else {
+ if (to_cp->c_datafork->ff_size > 0) {
+ error = EFBIG;
+ goto movedata_exit;
+ }
+ }
+
+ /* If the source has the rsrc open, make sure the destination is also the rsrc */
+ if (rsrc) {
+ if (to_vp != to_cp->c_rsrc_vp) {
+ error = EINVAL;
+ goto movedata_exit;
+ }
+ }
+ else {
+ /* Verify that both forks are data forks */
+ if (to_vp != to_cp->c_vp) {
+ error = EINVAL;
+ goto movedata_exit;
+ }
+ }
+
+ /*
+ * See if the source file has overflow extents. If it doesn't, we don't
+ * need to call into MoveData, and the catalog will be enough.
+ */
+ if (rsrc) {
+ overflow_blocks = overflow_extents(from_cp->c_rsrcfork);
+ }
+ else {
+ overflow_blocks = overflow_extents(from_cp->c_datafork);
+ }
+
+ if ((error = hfs_start_transaction (hfsmp)) != 0) {
+ goto movedata_exit;
+ }
+ started_tr = 1;
+
+ /* Lock the system files: catalog, extents, attributes */
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
+
+ /* Copy over any catalog allocation data into the new spot. */
+ if (rsrc) {
+ if ((error = hfs_move_fork (from_cp->c_rsrcfork, from_cp, to_cp->c_rsrcfork, to_cp))){
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ goto movedata_exit;
+ }
+ }
+ else {
+ if ((error = hfs_move_fork (from_cp->c_datafork, from_cp, to_cp->c_datafork, to_cp))) {
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ goto movedata_exit;
+ }
+ }
+
+ /*
+ * Note that because all we're doing is moving the extents around, we can
+ * probably do this in a single transaction: Each extent record (group of 8)
+ * is 64 bytes. A extent overflow B-Tree node is typically 4k. This means
+ * each node can hold roughly ~60 extent records == (480 extents).
+ *
+ * If a file was massively fragmented and had 20k extents, this means we'd
+ * roughly touch 20k/480 == 41 to 42 nodes, plus the index nodes, for half
+ * of the operation. (inserting or deleting). So if we're manipulating 80-100
+ * nodes, this is basically 320k of data to write to the journal in
+ * a bad case.
+ */
+ if (overflow_blocks != 0) {
+ if (rsrc) {
+ error = MoveData(hfsmp, from_cp->c_cnid, to_cp->c_cnid, 1);
+ }
+ else {
+ error = MoveData (hfsmp, from_cp->c_cnid, to_cp->c_cnid, 0);
+ }
+ }
+
+ if (error) {
+ /* Reverse the operation. Copy the fork data back into the source */
+ if (rsrc) {
+ hfs_move_fork (to_cp->c_rsrcfork, to_cp, from_cp->c_rsrcfork, from_cp);
+ }
+ else {
+ hfs_move_fork (to_cp->c_datafork, to_cp, from_cp->c_datafork, from_cp);
+ }
+ }
+ else {
+ struct cat_fork *src_data = NULL;
+ struct cat_fork *src_rsrc = NULL;
+ struct cat_fork *dst_data = NULL;
+ struct cat_fork *dst_rsrc = NULL;
+
+ /* Touch the times*/
+ to_cp->c_touch_acctime = TRUE;
+ to_cp->c_touch_chgtime = TRUE;
+ to_cp->c_touch_modtime = TRUE;
+
+ from_cp->c_touch_acctime = TRUE;
+ from_cp->c_touch_chgtime = TRUE;
+ from_cp->c_touch_modtime = TRUE;
+
+ hfs_touchtimes(hfsmp, to_cp);
+ hfs_touchtimes(hfsmp, from_cp);
+
+ if (from_cp->c_datafork) {
+ src_data = &from_cp->c_datafork->ff_data;
+ }
+ if (from_cp->c_rsrcfork) {
+ src_rsrc = &from_cp->c_rsrcfork->ff_data;
+ }
+
+ if (to_cp->c_datafork) {
+ dst_data = &to_cp->c_datafork->ff_data;
+ }
+ if (to_cp->c_rsrcfork) {
+ dst_rsrc = &to_cp->c_rsrcfork->ff_data;
+ }
+
+ /* Update the catalog nodes */
+ (void) cat_update(hfsmp, &from_cp->c_desc, &from_cp->c_attr,
+ src_data, src_rsrc);
+
+ (void) cat_update(hfsmp, &to_cp->c_desc, &to_cp->c_attr,
+ dst_data, dst_rsrc);
+
+ }
+ /* unlock the system files */
+ hfs_systemfile_unlock(hfsmp, lockflags);
+
+
+movedata_exit:
+ if (started_tr) {
+ hfs_end_transaction(hfsmp);
+ }
+
+ return error;
+
+}
+
+/*
+ * Copy all of the catalog and runtime data in srcfork to dstfork.
+ *
+ * This allows us to maintain the invalid ranges across the movedata operation so
+ * we don't need to force all of the pending IO right now. In addition, we move all
+ * non overflow-extent extents into the destination here.
+ */
+static int hfs_move_fork (struct filefork *srcfork, struct cnode *src_cp,
+ struct filefork *dstfork, struct cnode *dst_cp) {
+ struct rl_entry *invalid_range;
+ int size = sizeof(struct HFSPlusExtentDescriptor);
+ size = size * kHFSPlusExtentDensity;
+
+ /* If the dstfork has any invalid ranges, bail out */
+ invalid_range = TAILQ_FIRST(&dstfork->ff_invalidranges);
+ if (invalid_range != NULL) {
+ return EFBIG;
+ }
+
+ if (dstfork->ff_data.cf_size != 0 || dstfork->ff_data.cf_new_size != 0) {
+ return EFBIG;
+ }
+
+ /* First copy the invalid ranges */
+ while ((invalid_range = TAILQ_FIRST(&srcfork->ff_invalidranges))) {
+ off_t start = invalid_range->rl_start;
+ off_t end = invalid_range->rl_end;
+
+ /* Remove it from the srcfork and add it to dstfork */
+ rl_remove(start, end, &srcfork->ff_invalidranges);
+ rl_add(start, end, &dstfork->ff_invalidranges);