/*
- * Copyright (c) 1999-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2015 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
/* Put the original file back. */
err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
- if (err && err != EIO && err != ENXIO)
- panic("hfs_makelink: error %d from cat_rename backout 1", err);
+ if (err) {
+ if (err != EIO && err != ENXIO)
+ printf("hfs_makelink: error %d from cat_rename backout 1", err);
+ hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
+ }
+ if (retval != EIO && retval != ENXIO) {
+ printf("hfs_makelink: createindirectlink (1) failed: %d\n", retval);
+ retval = EIO;
+ }
goto out;
}
cp->c_attr.ca_linkref = indnodeno;
/* Put the original file back. */
err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
- if (err && err != EIO && err != ENXIO)
- panic("hfs_makelink: error %d from cat_rename backout 2", err);
+ if (err) {
+ if (err != EIO && err != ENXIO)
+ printf("hfs_makelink: error %d from cat_rename backout 2", err);
+ hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
+ }
cp->c_attr.ca_linkref = 0;
+
+ if (retval != EIO && retval != ENXIO) {
+ printf("hfs_makelink: createindirectlink (2) failed: %d\n", retval);
+ retval = EIO;
+ }
goto out;
} else if (retval == 0) {
if (newlink) {
vnode_t vp;
- if (retval != 0) {
- panic("hfs_makelink: retval %d but newlink = 1!\n", retval);
- }
-
hfsmp->hfs_private_attr[type].ca_entries++;
/* From application perspective, directory hard link is a
* normal directory. Therefore count the new directory
}
retval = cat_update(hfsmp, &hfsmp->hfs_private_desc[type],
&hfsmp->hfs_private_attr[type], NULL, NULL);
- if (retval != 0 && retval != EIO && retval != ENXIO) {
- panic("hfs_makelink: cat_update of privdir failed! (%d)\n", retval);
+ if (retval) {
+ if (retval != EIO && retval != ENXIO) {
+ printf("hfs_makelink: cat_update of privdir failed! (%d)\n", retval);
+ retval = EIO;
+ }
+ hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
}
cp->c_flag |= C_HARDLINK;
}
}
}
+ 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 == VBLK || v_type == VCHR) {
return (EPERM);
}
+
+ /*
+ * For now, return ENOTSUP for a symlink target. This can happen
+ * for linkat(2) when called without AT_SYMLINK_FOLLOW.
+ */
+ 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 && error != EIO && error != ENXIO) {
- panic("hfs_vnop_link: error %d updating tdvp %p\n", error, tdvp);
+ if (error) {
+ if (error != EIO && error != ENXIO) {
+ printf("hfs_vnop_link: error %d updating tdvp %p\n", error, tdvp);
+ error = EIO;
+ }
+ hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
}
-
+
if ((v_type == VDIR) &&
(fdcp != NULL) &&
((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 && error != EIO && error != ENXIO) {
- panic("hfs_vnop_link: error %d updating fdvp %p\n", error, fdvp);
+ if (error) {
+ if (error != EIO && error != ENXIO) {
+ printf("hfs_vnop_link: error %d updating fdvp %p\n", error, fdvp);
+ // No point changing error as it's set immediate below
+ }
+ hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
}
/* Set kHFSHasChildLinkBit in the source hierarchy */
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 && ret != EIO && ret != ENXIO) {
- panic("hfs_vnop_link: error %d updating vp @ %p\n", ret, vp);
+ 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);
}
out:
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);
hfs_savelinkorigin(cnode_t *cp, cnid_t parentcnid)
{
linkorigin_t *origin = NULL;
- void * thread = current_thread();
+ thread_t thread = current_thread();
int count = 0;
int maxorigins = (S_ISDIR(cp->c_mode)) ? MAX_CACHED_ORIGINS : MAX_CACHED_FILE_ORIGINS;
/*
hfs_relorigin(struct cnode *cp, cnid_t parentcnid)
{
linkorigin_t *origin, *prev;
- void * thread = current_thread();
+ 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;
}
}
}
{
if (cp->c_flag & C_HARDLINK) {
linkorigin_t *origin;
- void * thread = current_thread();
+ thread_t thread = current_thread();
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;
- void * thread = current_thread();
-
+ 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);
}
{
if (cp->c_flag & C_HARDLINK) {
linkorigin_t *origin;
- void * thread = current_thread();
+ thread_t thread = current_thread();
TAILQ_FOREACH(origin, &cp->c_originlist, lo_link) {
if (origin->lo_thread == thread) {
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;
+}