X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/0b4e3aa066abc0728aacb4bbeb86f53f9737156e..483a1d1004b64bbaaef2c64c17c6b999009a54d2:/bsd/hfs/hfs_link.c diff --git a/bsd/hfs/hfs_link.c b/bsd/hfs/hfs_link.c index f31f86495..97dfde7bb 100644 --- a/bsd/hfs/hfs_link.c +++ b/bsd/hfs/hfs_link.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -20,11 +20,11 @@ * @APPLE_LICENSE_HEADER_END@ */ -#if HFS_HARDLINKS #include #include #include +#include #include #include #include @@ -32,158 +32,198 @@ #include #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, 0); - 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; + cat_cookie_t cookie = {0}; - 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; + /* Reserve some space in the Catalog file. */ + if ((retval = cat_preflight(hfsmp, (2 * CAT_CREATE)+ CAT_RENAME, &cookie, p))) { + return (retval); + } + + /* Lock catalog b-tree */ + retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p); + if (retval) { + goto out2; + } /* * 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); +out2: + cat_postflight(hfsmp, &cookie, p); return (retval); } @@ -199,22 +239,27 @@ out: IN struct componentname *cnp; */ +__private_extern__ 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"); @@ -227,20 +272,22 @@ struct vop_link_args /* { 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_privdir_desc.cd_cnid == 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; @@ -251,17 +298,59 @@ struct vop_link_args /* { 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)); } - FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI); + + // XXXdbg - need to do this here as well because cp could have changed + (void) VOP_UPDATE(vp, &tv, &tv, 1); + + + if (hfsmp->jnl) { + journal_end_transaction(hfsmp->jnl); + } + hfs_global_shared_lock_release(hfsmp); + + /* free the pathname buffer */ + { + char *tmp = cnp->cn_pnbuf; + cnp->cn_pnbuf = NULL; + cnp->cn_flags &= ~HASBUF; + FREE_ZONE(tmp, cnp->cn_pnlen, M_NAMEI); + } + + HFS_KNOTE(vp, NOTE_LINK); + HFS_KNOTE(tdvp, NOTE_WRITE); + out1: if (tdvp != vp) VOP_UNLOCK(vp, 0, p); @@ -269,5 +358,3 @@ out2: vput(tdvp); return (error); } - -#endif