/*
- * Copyright (c) 1999-2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* @APPLE_LICENSE_HEADER_END@
*/
-#if HFS_HARDLINKS
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
+#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <libkern/libkern.h>
#include "hfs.h"
-#include "hfscommon/headers/FileMgrInternal.h"
+#include "hfs_catalog.h"
+#include "hfs_format.h"
+#include "hfs_endian.h"
/*
* Create a new indirect link
*
- * An indirect link is a reference to a data node. The only useable fields in the
- * link are the parentID, name and text encoding. All other catalog fields
- * are ignored.
+ * An indirect link is a reference to a data node. The only useable
+ * fields in the link are the link number, parentID, name and text
+ * encoding. All other catalog fields are ignored.
*/
static int
-createindirectlink(struct hfsnode *dnhp, UInt32 linkPID, char *linkName)
+createindirectlink(struct hfsmount *hfsmp, u_int32_t linknum,
+ u_int32_t linkparid, char *linkName, cnid_t *linkcnid)
{
- struct hfsCatalogInfo catInfo;
- struct FInfo *fip;
- ExtendedVCB *vcb;
+ struct FndrFileInfo *fip;
+ struct cat_desc desc;
+ struct cat_attr attr;
int result;
- vcb = HTOVCB(dnhp);
+ /* Setup the descriptor */
+ bzero(&desc, sizeof(desc));
+ desc.cd_nameptr = linkName;
+ desc.cd_namelen = strlen(linkName);
+ desc.cd_parentcnid = linkparid;
- /* Create the indirect link directly in the catalog */
- result = hfsCreate(vcb, linkPID, linkName, IFREG);
- if (result) return (result);
-
- /*
- * XXX SER Here is a good example where hfsCreate should pass in a catinfo and return
- * things like the hint and file ID there should be no reason to call lookup here
- */
- 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; /* 'hlnk' */
- fip->fdCreator = kHFSPlusCreator; /* 'hfs+' */
- fip->fdFlags |= kHasBeenInited;
-
- /* links are matched to data nodes by nodeID and to volumes by create date */
- 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;
+ /* Setup the default attributes */
+ bzero(&attr, sizeof(attr));
+
+ /* links are matched to data nodes by link ID and to volumes by create date */
+ attr.ca_rdev = linknum; /* note: cat backend overloads ca_rdev to be the linknum when nlink = 0 */
+ attr.ca_itime = HFSTOVCB(hfsmp)->vcbCrDate;
+ attr.ca_mode = S_IFREG;
+
+ fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
+ fip->fdType = SWAP_BE32 (kHardLinkFileType); /* 'hlnk' */
+ fip->fdCreator = SWAP_BE32 (kHFSPlusCreator); /* 'hfs+' */
+ fip->fdFlags = SWAP_BE16 (kHasBeenInited);
+
+ hfs_global_shared_lock_acquire(hfsmp);
+ if (hfsmp->jnl) {
+ if (journal_start_transaction(hfsmp->jnl) != 0) {
+ hfs_global_shared_lock_release(hfsmp);
+ return EINVAL;
+ }
+ }
- CLEAN_CATALOGDATA(&catInfo.nodeData);
- return (0);
+ /* Create the indirect link directly in the catalog */
+ result = cat_create(hfsmp, &desc, &attr, NULL);
-errExit:
- CLEAN_CATALOGDATA(&catInfo.nodeData);
+ if (result == 0 && linkcnid != NULL)
+ *linkcnid = attr.ca_fileid;
- /* get rid of link node */
- (void) hfsDelete(vcb, linkPID, linkName, TRUE, 0);
+ if (hfsmp->jnl) {
+ journal_end_transaction(hfsmp->jnl);
+ }
+ hfs_global_shared_lock_release(hfsmp);
return (result);
}
/*
- * 2 locks are needed (dvp and hp)
+ * 2 locks are needed (dvp and vp)
* also need catalog lock
*
* caller's responsibility:
* componentname cleanup
- * unlocking dvp and hp
+ * unlocking dvp and vp
*/
static int
-hfs_makelink(hp, dvp, cnp)
- struct hfsnode *hp;
- struct vnode *dvp;
- register struct componentname *cnp;
+hfs_makelink(struct hfsmount *hfsmp, struct cnode *cp, struct cnode *dcp,
+ struct componentname *cnp)
{
struct proc *p = cnp->cn_proc;
- struct hfsnode *dhp = VTOH(dvp);
- u_int32_t ldirID; /* directory ID of linked nodes directory */
- ExtendedVCB *vcb = VTOVCB(dvp);
- u_int32_t hint;
u_int32_t indnodeno = 0;
char inodename[32];
+ struct cat_desc to_desc;
+ int newlink = 0;
int retval;
- ldirID = VTOHFS(dvp)->hfs_private_metadata_dir;
/* We don't allow link nodes in our Private Meta Data folder! */
- if ( H_FILEID(dhp) == ldirID)
+ if (dcp->c_fileid == hfsmp->hfs_privdir_desc.cd_cnid)
return (EPERM);
- if (vcb->freeBlocks == 0)
+ if (hfs_freeblks(hfsmp, 0) == 0)
return (ENOSPC);
- /* lock catalog b-tree */
- retval = hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
- if (retval != E_NONE)
- return retval;
+ /* Lock catalog b-tree */
+ retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
+ if (retval) {
+ return retval;
+ }
/*
* If this is a new hardlink then we need to create the data
* node (inode) and replace the original file with a link node.
*/
- if (hp->h_meta->h_nlink == 1) {
+ if (cp->c_nlink == 2 && (cp->c_flag & C_HARDLINK) == 0) {
+ newlink = 1;
+ bzero(&to_desc, sizeof(to_desc));
+ to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
+ to_desc.cd_cnid = cp->c_fileid;
+
do {
/* get a unique indirect node number */
indnodeno = ((random() & 0x3fffffff) + 100);
MAKE_INODE_NAME(inodename, indnodeno);
/* move source file to data node directory */
- hint = 0;
- retval = hfsMoveRename(vcb, H_DIRID(hp), H_NAME(hp), ldirID, inodename, &hint);
- } while (retval == cmExists);
+ to_desc.cd_nameptr = inodename;
+ to_desc.cd_namelen = strlen(inodename);
+
+ retval = cat_rename(hfsmp, &cp->c_desc, &hfsmp->hfs_privdir_desc,
+ &to_desc, NULL);
- if (retval) goto out;
+ } while (retval == EEXIST);
+ if (retval)
+ goto out;
- hp->h_meta->h_indnodeno = indnodeno;
-
- /* replace source file with link node */
- retval = createindirectlink(hp, H_DIRID(hp), H_NAME(hp));
+ /* Replace source file with link node */
+ retval = createindirectlink(hfsmp, indnodeno, cp->c_parentcnid,
+ cp->c_desc.cd_nameptr, &cp->c_desc.cd_cnid);
if (retval) {
/* put it source file back */
- hint = 0;
- (void) hfsMoveRename(vcb, ldirID, inodename, H_DIRID(hp), H_NAME(hp), &hint);
+ // XXXdbg
+ #if 1
+ {
+ int err;
+ err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
+ if (err)
+ panic("hfs_makelink: error %d from cat_rename backout 1", err);
+ }
+ #else
+ (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
+ #endif
goto out;
}
- }
+ cp->c_rdev = indnodeno;
+ } else {
+ indnodeno = cp->c_rdev;
+ }
/*
* Create a catalog entry for the new link (parentID + name).
*/
- retval = createindirectlink(hp, H_FILEID(dhp), cnp->cn_nameptr);
- if (retval && hp->h_meta->h_nlink == 1) {
- /* get rid of new link */
- (void) hfsDelete(vcb, H_DIRID(hp), H_NAME(hp), TRUE, 0);
-
- /* put it source file back */
- hint = 0;
- (void) hfsMoveRename(vcb, ldirID, inodename, H_DIRID(hp), H_NAME(hp), &hint);
+ retval = createindirectlink(hfsmp, indnodeno, dcp->c_fileid, cnp->cn_nameptr, NULL);
+ if (retval && newlink) {
+ /* Get rid of new link */
+ (void) cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
+
+ /* Put the source file back */
+ // XXXdbg
+ #if 1
+ {
+ int err;
+ err = cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
+ if (err)
+ panic("hfs_makelink: error %d from cat_rename backout 2", err);
+ }
+ #else
+ (void) cat_rename(hfsmp, &to_desc, &dcp->c_desc, &cp->c_desc, NULL);
+ #endif
goto out;
}
/*
- * Finally, if this is a new hardlink then we need to mark the hfs node
+ * Finally, if this is a new hardlink then:
+ * - update HFS Private Data dir
+ * - mark the cnode as a hard link
*/
- if (hp->h_meta->h_nlink == 1) {
- hp->h_meta->h_nlink++;
- hp->h_nodeflags |= IN_CHANGE;
- hp->h_meta->h_metaflags |= IN_DATANODE;
+ if (newlink) {
+ hfsmp->hfs_privdir_attr.ca_entries++;
+ (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
+ &hfsmp->hfs_privdir_attr, NULL, NULL);
+ hfs_volupdate(hfsmp, VOL_MKFILE, 0);
+ cp->c_flag |= (C_CHANGE | C_HARDLINK);
}
out:
- /* unlock catalog b-tree */
- (void) hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_RELEASE, p);
+ /* Unlock catalog b-tree */
+ (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
return (retval);
}
*/
int
hfs_link(ap)
-struct vop_link_args /* {
- struct vnode *a_vp;
- struct vnode *a_tdvp;
- struct componentname *a_cnp;
-} */ *ap;
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
{
+ struct hfsmount *hfsmp;
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 cnode *cp;
+ struct cnode *tdcp;
struct timeval tv;
int error;
+ hfsmp = VTOHFS(vp);
+
#if HFS_DIAGNOSTIC
if ((cnp->cn_flags & HASBUF) == 0)
panic("hfs_link: no name");
if (VTOVCB(tdvp)->vcbSigWord != kHFSPlusSigWord)
return err_link(ap); /* hfs disks don't support hard links */
- if (VTOHFS(vp)->hfs_private_metadata_dir == 0)
+ if (hfsmp->hfs_private_metadata_dir == 0)
return err_link(ap); /* no private metadata dir, no links possible */
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) {
+ cp = VTOC(vp);
+ tdcp = VTOC(tdvp);
+
+ if (cp->c_nlink >= HFS_LINK_MAX) {
VOP_ABORTOP(tdvp, cnp);
error = EMLINK;
goto out1;
}
- if (hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) {
+ if (cp->c_flags & (IMMUTABLE | APPEND)) {
VOP_ABORTOP(tdvp, cnp);
error = EPERM;
goto out1;
goto out1;
}
- hp->h_meta->h_nlink++;
- hp->h_nodeflags |= IN_CHANGE;
+ hfs_global_shared_lock_acquire(hfsmp);
+ if (hfsmp->jnl) {
+ if (journal_start_transaction(hfsmp->jnl) != 0) {
+ hfs_global_shared_lock_release(hfsmp);
+ VOP_ABORTOP(tdvp, cnp);
+ error = EINVAL; /* cannot link to a special file */
+ goto out1;
+ }
+ }
+
+ cp->c_nlink++;
+ cp->c_flag |= C_CHANGE;
tv = time;
+
error = VOP_UPDATE(vp, &tv, &tv, 1);
- if (!error)
- error = hfs_makelink(hp, tdvp, cnp);
+ if (!error) {
+ error = hfs_makelink(hfsmp, cp, tdcp, cnp);
+ }
if (error) {
- hp->h_meta->h_nlink--;
- hp->h_nodeflags |= IN_CHANGE;
+ cp->c_nlink--;
+ cp->c_flag |= C_CHANGE;
+ } else {
+ /* Update the target directory and volume stats */
+ tdcp->c_nlink++;
+ tdcp->c_entries++;
+ tdcp->c_flag |= C_CHANGE | C_UPDATE;
+ tv = time;
+ (void) VOP_UPDATE(tdvp, &tv, &tv, 0);
+
+ hfs_volupdate(hfsmp, VOL_MKFILE,
+ (tdcp->c_cnid == kHFSRootFolderID));
}
+
+ // XXXdbg - need to do this here as well because cp could have changed
+ error = VOP_UPDATE(vp, &tv, &tv, 1);
+
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
+
+ if (hfsmp->jnl) {
+ journal_end_transaction(hfsmp->jnl);
+ }
+ hfs_global_shared_lock_release(hfsmp);
+
out1:
if (tdvp != vp)
VOP_UNLOCK(vp, 0, p);
vput(tdvp);
return (error);
}
-
-#endif