#if HFS_HARDLINKS
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <vfs/vfs_support.h>
#include <libkern/libkern.h>
#include "hfs.h"
#include "hfscommon/headers/FileMgrInternal.h"
static int
createindirectlink(struct hfsnode *dnhp, UInt32 linkPID, char *linkName)
{
struct hfsCatalogInfo catInfo;
struct FInfo *fip;
ExtendedVCB *vcb;
int result;
vcb = HTOVCB(dnhp);
result = hfsCreate(vcb, linkPID, linkName, IFREG);
if (result) return (result);
catInfo.hint = 0;
INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
result = hfs_getcatalog(vcb, linkPID, linkName, -1, &catInfo);
if (result) goto errExit;
fip = (struct FInfo *)&catInfo.nodeData.cnd_finderInfo;
fip->fdType = kHardLinkFileType;
fip->fdCreator = kHFSPlusCreator;
fip->fdFlags |= kHasBeenInited;
catInfo.nodeData.cnd_iNodeNum = dnhp->h_meta->h_indnodeno;
catInfo.nodeData.cnd_createDate = vcb->vcbCrDate;
result = UpdateCatalogNode(vcb, linkPID, linkName, catInfo.hint, &catInfo.nodeData);
if (result) goto errExit;
CLEAN_CATALOGDATA(&catInfo.nodeData);
return (0);
errExit:
CLEAN_CATALOGDATA(&catInfo.nodeData);
(void) hfsDelete(vcb, linkPID, linkName, TRUE, 0);
return (result);
}
static int
hfs_makelink(hp, dvp, cnp)
struct hfsnode *hp;
struct vnode *dvp;
register struct componentname *cnp;
{
struct proc *p = cnp->cn_proc;
struct hfsnode *dhp = VTOH(dvp);
u_int32_t ldirID;
ExtendedVCB *vcb = VTOVCB(dvp);
u_int32_t hint;
u_int32_t indnodeno = 0;
char inodename[32];
int retval;
ldirID = VTOHFS(dvp)->hfs_private_metadata_dir;
if ( H_FILEID(dhp) == ldirID)
return (EPERM);
if (vcb->freeBlocks == 0)
return (ENOSPC);
retval = hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (retval != E_NONE)
return retval;
if (hp->h_meta->h_nlink == 1) {
do {
indnodeno = ((random() & 0x3fffffff) + 100);
MAKE_INODE_NAME(inodename, indnodeno);
hint = 0;
retval = hfsMoveRename(vcb, H_DIRID(hp), H_NAME(hp), ldirID, inodename, &hint);
} while (retval == cmExists);
if (retval) goto out;
hp->h_meta->h_indnodeno = indnodeno;
retval = createindirectlink(hp, H_DIRID(hp), H_NAME(hp));
if (retval) {
hint = 0;
(void) hfsMoveRename(vcb, ldirID, inodename, H_DIRID(hp), H_NAME(hp), &hint);
goto out;
}
}
retval = createindirectlink(hp, H_FILEID(dhp), cnp->cn_nameptr);
if (retval && hp->h_meta->h_nlink == 1) {
(void) hfsDelete(vcb, H_DIRID(hp), H_NAME(hp), TRUE, 0);
hint = 0;
(void) hfsMoveRename(vcb, ldirID, inodename, H_DIRID(hp), H_NAME(hp), &hint);
goto out;
}
if (hp->h_meta->h_nlink == 1) {
hp->h_meta->h_nlink++;
hp->h_nodeflags |= IN_CHANGE;
hp->h_meta->h_metaflags |= IN_DATANODE;
}
out:
(void) hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_RELEASE, p);
return (retval);
}
int
hfs_link(ap)
struct vop_link_args *ap;
{
struct vnode *vp = ap->a_vp;
struct vnode *tdvp = ap->a_tdvp;
struct componentname *cnp = ap->a_cnp;
struct proc *p = cnp->cn_proc;
struct hfsnode *hp;
struct timeval tv;
int error;
#if HFS_DIAGNOSTIC
if ((cnp->cn_flags & HASBUF) == 0)
panic("hfs_link: no name");
#endif
if (tdvp->v_mount != vp->v_mount) {
VOP_ABORTOP(tdvp, cnp);
error = EXDEV;
goto out2;
}
if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord)
return err_link(ap);
if (VTOHFS(vp)->hfs_private_metadata_dir == 0)
return err_link(ap);
if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
VOP_ABORTOP(tdvp, cnp);
goto out2;
}
hp = VTOH(vp);
if (hp->h_meta->h_nlink >= HFS_LINK_MAX) {
VOP_ABORTOP(tdvp, cnp);
error = EMLINK;
goto out1;
}
if (hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) {
VOP_ABORTOP(tdvp, cnp);
error = EPERM;
goto out1;
}
if (vp->v_type == VBLK || vp->v_type == VCHR) {
VOP_ABORTOP(tdvp, cnp);
error = EINVAL;
goto out1;
}
hp->h_meta->h_nlink++;
hp->h_nodeflags |= IN_CHANGE;
tv = time;
error = VOP_UPDATE(vp, &tv, &tv, 1);
if (!error)
error = hfs_makelink(hp, tdvp, cnp);
if (error) {
hp->h_meta->h_nlink--;
hp->h_nodeflags |= IN_CHANGE;
}
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
out1:
if (tdvp != vp)
VOP_UNLOCK(vp, 0, p);
out2:
vput(tdvp);
return (error);
}
#endif