+ /*
+ * 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);
+ }
+
+ /*
+ * Ignore the ff_union. We don't move symlinks or system files.
+ * Now copy the in-catalog extent information
+ */
+ dstfork->ff_data.cf_size = srcfork->ff_data.cf_size;
+ dstfork->ff_data.cf_new_size = srcfork->ff_data.cf_new_size;
+ dstfork->ff_data.cf_vblocks = srcfork->ff_data.cf_vblocks;
+ dstfork->ff_data.cf_blocks = srcfork->ff_data.cf_blocks;
+
+ /* just memcpy the whole array of extents to the new location. */
+ memcpy (dstfork->ff_data.cf_extents, srcfork->ff_data.cf_extents, size);
+
+ /*
+ * Copy the cnode attribute data.
+ *
+ */
+ src_cp->c_blocks -= srcfork->ff_data.cf_vblocks;
+ src_cp->c_blocks -= srcfork->ff_data.cf_blocks;
+
+ dst_cp->c_blocks += srcfork->ff_data.cf_vblocks;
+ dst_cp->c_blocks += srcfork->ff_data.cf_blocks;
+
+ /* Now delete the entries in the source fork */
+ srcfork->ff_data.cf_size = 0;
+ srcfork->ff_data.cf_new_size = 0;
+ srcfork->ff_data.cf_union.cfu_bytesread = 0;
+ srcfork->ff_data.cf_vblocks = 0;
+ srcfork->ff_data.cf_blocks = 0;
+
+ /* Zero out the old extents */
+ bzero (srcfork->ff_data.cf_extents, size);
+ return 0;
+}
+
+
+/*
+ * cnode must be locked
+ */
+int
+hfs_fsync(struct vnode *vp, int waitfor, int fullsync, struct proc *p)