/*
- * Copyright (c) 1999-2014 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2015 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
}
}
}
+ cp->c_flag |= C_MODIFIED;
cp->c_touch_chgtime = TRUE;
- cp->c_flag |= C_FORCEUPDATE;
}
- dcp->c_flag |= C_FORCEUPDATE;
}
out:
hfs_systemfile_unlock(hfsmp, lockflags);
if (v_type == VLNK)
return (ENOTSUP);
+ cp = VTOC(vp);
+
if (v_type == VDIR) {
#if CONFIG_HFS_DIRLINK
/* Make sure our private directory exists. */
if (hfsmp->jnl == NULL) {
return (EPERM);
}
+
/* Directory hardlinks also need the parent of the original directory. */
- if ((error = hfs_vget(hfsmp, hfs_currentparent(VTOC(vp)), &fdvp, 1, 0))) {
+ if ((error = hfs_vget(hfsmp, hfs_currentparent(cp, /* have_lock: */ false),
+ &fdvp, 1, 0))) {
return (error);
}
#else
}
}
tdcp = VTOC(tdvp);
- cp = VTOC(vp);
/* grab the parent CNID from originlist after grabbing cnode locks */
- parentcnid = hfs_currentparent(cp);
+ parentcnid = hfs_currentparent(cp, /* have_lock: */ true);
/*
* Make sure we didn't race the src or dst parent directories with rmdir.
lockflags = 0;
cp->c_linkcount++;
+ cp->c_flag |= C_MODIFIED;
cp->c_touch_chgtime = TRUE;
error = hfs_makelink(hfsmp, vp, cp, tdcp, cnp);
if (error) {
}
}
tdcp->c_dirchangecnt++;
+ tdcp->c_flag |= C_MODIFIED;
hfs_incr_gencount(tdcp);
tdcp->c_touch_chgtime = TRUE;
tdcp->c_touch_modtime = TRUE;
- tdcp->c_flag |= C_FORCEUPDATE;
error = hfs_update(tdvp, 0);
if (error) {
((fdcp->c_attr.ca_recflags & kHFSHasChildLinkMask) == 0)) {
fdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
+ fdcp->c_flag |= C_MODIFIED;
fdcp->c_touch_chgtime = TRUE;
- fdcp->c_flag |= C_FORCEUPDATE;
error = hfs_update(fdvp, 0);
if (error) {
if (error != EIO && error != ENXIO) {
hfs_volupdate(hfsmp, VOL_MKFILE,
(tdcp->c_cnid == kHFSRootFolderID));
}
- /* Make sure update occurs inside transaction */
- cp->c_flag |= C_FORCEUPDATE;
- if (error == 0 && (ret = hfs_update(vp, TRUE)) != 0) {
+ if (error == 0 && (ret = hfs_update(vp, 0)) != 0) {
if (ret != EIO && ret != ENXIO)
printf("hfs_vnop_link: error %d updating vp @ %p\n", ret, vp);
hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
dcp->c_dirchangecnt++;
hfs_incr_gencount(dcp);
microtime(&tv);
- dcp->c_ctime = tv.tv_sec;
- dcp->c_mtime = tv.tv_sec;
- (void ) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
+ dcp->c_touch_chgtime = dcp->c_touch_modtime = true;
+ dcp->c_flag |= C_MODIFIED;
+ hfs_update(dcp->c_vp, 0);
/*
* If this is the last link then we need to process the inode.
firstlink == cndesc.cd_cnid) {
if (setfirstlink(hfsmp, cp->c_fileid, nextlinkid) == 0)
cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
- } else if (vnode_isreg(vp) && cp->c_attr.ca_firstlink == cndesc.cd_cnid) {
+ } else if (cp->c_attr.ca_firstlink == cndesc.cd_cnid) {
cp->c_attr.ca_firstlink = nextlinkid;
}
/* Update previous link. */
if (nextlinkid) {
(void) cat_update_siblinglinks(hfsmp, nextlinkid, prevlinkid, HFS_IGNORABLE_LINK);
}
-
- /*
- * The call to cat_releasedesc below will only release the name buffer;
- * it does not zero out the rest of the fields in the 'cat_desc' data structure.
- *
- * As a result, since there are still other links at this point, we need
- * to make the current cnode descriptor point to the raw inode. If a path-based
- * system call comes along first, it will replace the descriptor with a valid link
- * ID. If a userland process already has a file descriptor open, then they will
- * bypass that lookup, though. Replacing the descriptor CNID with the raw
- * inode will force it to generate a new full path.
- */
- cp->c_cnid = cp->c_fileid;
-
}
+ /*
+ * The call to cat_releasedesc below will only release the name
+ * buffer; it does not zero out the rest of the fields in the
+ * 'cat_desc' data structure.
+ *
+ * As a result, since there are still other links at this point,
+ * we need to make the current cnode descriptor point to the raw
+ * inode. If a path-based system call comes along first, it will
+ * replace the descriptor with a valid link ID. If a userland
+ * process already has a file descriptor open, then they will
+ * bypass that lookup, though. Replacing the descriptor CNID with
+ * the raw inode will force it to generate a new full path.
+ */
+ cp->c_cnid = cp->c_fileid;
+
/* Push new link count to disk. */
cp->c_ctime = tv.tv_sec;
(void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
thread_t thread = current_thread();
TAILQ_FOREACH_SAFE(origin, &cp->c_originlist, lo_link, prev) {
- if ((origin->lo_thread == thread) ||
- (origin->lo_parentcnid == parentcnid)) {
+ if (origin->lo_thread == thread) {
TAILQ_REMOVE(&cp->c_originlist, origin, lo_link);
FREE(origin, M_TEMP);
break;
+ } else if (origin->lo_parentcnid == parentcnid) {
+ /*
+ * If the threads don't match, then we don't want to
+ * delete the entry because that might cause other threads
+ * to fall back and use whatever happens to be in
+ * c_parentcnid or the wrong link ID. By setting the
+ * values to zero here, it should serve as an indication
+ * that the path is no longer valid and that's better than
+ * using a random parent ID or link ID.
+ */
+ origin->lo_parentcnid = 0;
+ origin->lo_cnid = 0;
}
}
}
TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
if (origin->lo_thread == thread) {
- return (1);
+ return origin->lo_cnid != 0;
}
}
}
*/
__private_extern__
cnid_t
-hfs_currentparent(cnode_t *cp)
+hfs_currentparent(cnode_t *cp, bool have_lock)
{
if (cp->c_flag & C_HARDLINK) {
+ if (!have_lock)
+ hfs_lock_always(cp, HFS_SHARED_LOCK);
+
linkorigin_t *origin;
thread_t thread = current_thread();
-
+
TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
if (origin->lo_thread == thread) {
+ if (!have_lock)
+ hfs_unlock(cp);
return (origin->lo_parentcnid);
}
}
+
+ if (!have_lock)
+ hfs_unlock(cp);
}
return (cp->c_parentcnid);
}
return MacToVFSError(result);
}
+errno_t hfs_first_link(hfsmount_t *hfsmp, cnode_t *cp, cnid_t *link_id)
+{
+ errno_t error = 0;
+
+ if (S_ISDIR(cp->c_mode)) {
+ int lockf = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
+
+ error = getfirstlink(hfsmp, cp->c_fileid, link_id);
+
+ hfs_systemfile_unlock(hfsmp, lockf);
+ } else {
+ if (cp->c_attr.ca_firstlink)
+ *link_id = cp->c_attr.ca_firstlink;
+ else {
+ // This can happen if the cnode has been deleted
+ error = ENOENT;
+ }
+ }
+
+ return error;
+}