/* * Given an exclusively locked inode and chain we consolidate its chain * for hardlink creation, adding (nlinks) to the file's link count and * potentially relocating the inode to a directory common to ip->pip and tdip. * * Replaces (*chainp) if consolidation occurred, unlocking the old chain * and returning a new locked chain. * * NOTE! This function will also replace ip->chain. */ int hammer2_hardlink_consolidate(hammer2_trans_t *trans, hammer2_inode_t *ip, hammer2_chain_t **chainp, hammer2_inode_t *cdip, hammer2_chain_t **cdchainp, int nlinks) { hammer2_inode_data_t *ipdata; hammer2_chain_t *chain; hammer2_chain_t *nchain; int error; chain = *chainp; if (nlinks == 0 && /* no hardlink needed */ (chain->data->ipdata.name_key & HAMMER2_DIRHASH_VISIBLE)) { return (0); } if (hammer2_hardlink_enable < 0) { /* fake hardlinks */ return (0); } if (hammer2_hardlink_enable == 0) { /* disallow hardlinks */ hammer2_chain_unlock(chain); *chainp = NULL; return (ENOTSUP); } /* * If no change in the hardlink's target directory is required and * this is already a hardlink target, all we need to do is adjust * the link count. */ if (cdip == ip->pip && (chain->data->ipdata.name_key & HAMMER2_DIRHASH_VISIBLE) == 0) { if (nlinks) { hammer2_chain_modify(trans, &chain, 0); chain->data->ipdata.nlinks += nlinks; } error = 0; goto done; } /* * chain is the real inode. If it's visible we have to convert it * to a hardlink pointer. If it is not visible then it is already * a hardlink target and only needs to be deleted. */ KKASSERT((chain->flags & HAMMER2_CHAIN_DELETED) == 0); KKASSERT(chain->data->ipdata.type != HAMMER2_OBJTYPE_HARDLINK); if (chain->data->ipdata.name_key & HAMMER2_DIRHASH_VISIBLE) { /* * We are going to duplicate chain later, causing its * media block to be shifted to the duplicate. Even though * we are delete-duplicating nchain here it might decide not * to reallocate the block. Set FORCECOW to force it to. */ nchain = chain; hammer2_chain_lock(nchain, HAMMER2_RESOLVE_ALWAYS); atomic_set_int(&nchain->flags, HAMMER2_CHAIN_FORCECOW); hammer2_chain_delete_duplicate(trans, &nchain, HAMMER2_DELDUP_RECORE); KKASSERT((chain->flags & HAMMER2_CHAIN_DUPLICATED) == 0); ipdata = &nchain->data->ipdata; ipdata->target_type = ipdata->type; ipdata->type = HAMMER2_OBJTYPE_HARDLINK; ipdata->uflags = 0; ipdata->rmajor = 0; ipdata->rminor = 0; ipdata->ctime = 0; ipdata->mtime = 0; ipdata->atime = 0; ipdata->btime = 0; bzero(&ipdata->uid, sizeof(ipdata->uid)); bzero(&ipdata->gid, sizeof(ipdata->gid)); ipdata->op_flags = HAMMER2_OPFLAG_DIRECTDATA; ipdata->cap_flags = 0; ipdata->mode = 0; ipdata->size = 0; ipdata->nlinks = 1; ipdata->iparent = 0; /* XXX */ ipdata->pfs_type = 0; ipdata->pfs_inum = 0; bzero(&ipdata->pfs_clid, sizeof(ipdata->pfs_clid)); bzero(&ipdata->pfs_fsid, sizeof(ipdata->pfs_fsid)); ipdata->data_quota = 0; ipdata->data_count = 0; ipdata->inode_quota = 0; ipdata->inode_count = 0; ipdata->attr_tid = 0; ipdata->dirent_tid = 0; bzero(&ipdata->u, sizeof(ipdata->u)); /* XXX transaction ids */ } else { hammer2_chain_delete(trans, chain, 0); nchain = NULL; } /* * chain represents the hardlink target and is now flagged deleted. * duplicate it to the parent directory and adjust nlinks. * * WARNING! The shiftup() call can cause nchain to be moved into * an indirect block, and our nchain will wind up pointing * to the older/original version. */ KKASSERT(chain->flags & HAMMER2_CHAIN_DELETED); hammer2_hardlink_shiftup(trans, &chain, cdip, cdchainp, nlinks, &error); if (error == 0) hammer2_inode_repoint(ip, cdip, chain); /* * Unlock the original chain last as the lock blocked races against * the creation of the new hardlink target. */ if (nchain) hammer2_chain_unlock(nchain); done: /* * Cleanup, chain/nchain already dealt with. */ *chainp = chain; hammer2_inode_drop(cdip); return (error); }
/* * Given an exclusively locked inode we consolidate its chain for hardlink * creation, adding (nlinks) to the file's link count and potentially * relocating the inode to a directory common to ip->pip and tdip. * * Replaces (*chainp) if consolidation occurred, unlocking the old chain * and returning a new locked chain. * * NOTE! This function will also replace ip->chain. */ int hammer2_hardlink_consolidate(hammer2_trans_t *trans, hammer2_inode_t *ip, hammer2_chain_t **chainp, hammer2_inode_t *tdip, int nlinks) { hammer2_inode_data_t *ipdata; hammer2_inode_t *fdip; hammer2_inode_t *cdip; hammer2_chain_t *chain; hammer2_chain_t *nchain; int error; chain = *chainp; if (nlinks == 0 && /* no hardlink needed */ (chain->data->ipdata.name_key & HAMMER2_DIRHASH_VISIBLE)) { return (0); } if (hammer2_hardlink_enable < 0) { /* fake hardlinks */ return (0); } if (hammer2_hardlink_enable == 0) { /* disallow hardlinks */ hammer2_chain_unlock(chain); *chainp = NULL; return (ENOTSUP); } /* * cdip will be returned with a ref, but not locked. */ fdip = ip->pip; cdip = hammer2_inode_common_parent(fdip, tdip); /* * If no change in the hardlink's target directory is required and * this is already a hardlink target, all we need to do is adjust * the link count. * * XXX The common parent is a big wiggly due to duplication from * renames. Compare the core (RBTREE) pointer instead of the * ip's. */ if (cdip == fdip && (chain->data->ipdata.name_key & HAMMER2_DIRHASH_VISIBLE) == 0) { if (nlinks) { hammer2_chain_modify(trans, &chain, 0); chain->data->ipdata.nlinks += nlinks; } error = 0; goto done; } /* * We either have to move an existing hardlink target or we have * to create a fresh hardlink target. * * Hardlink targets are hidden inodes in a parent directory common * to all directory entries referencing the hardlink. */ nchain = hammer2_hardlink_shiftup(trans, &chain, cdip, &error); if (error == 0) { /* * Bump nlinks on duplicated hidden inode, repoint * ip->chain. */ hammer2_chain_modify(trans, &nchain, 0); nchain->data->ipdata.nlinks += nlinks; hammer2_inode_repoint(ip, cdip, nchain); /* * If the old chain is not a hardlink target then replace * it with a OBJTYPE_HARDLINK pointer. * * If the old chain IS a hardlink target then delete it. */ if (chain->data->ipdata.name_key & HAMMER2_DIRHASH_VISIBLE) { /* * Replace original non-hardlink that's been dup'd * with a special hardlink directory entry. We must * set the DIRECTDATA flag to prevent sub-chains * from trying to synchronize to the inode if the * file is extended afterwords. */ hammer2_chain_modify(trans, &chain, 0); hammer2_chain_delete_duplicate(trans, &chain, HAMMER2_DELDUP_RECORE); ipdata = &chain->data->ipdata; ipdata->target_type = ipdata->type; ipdata->type = HAMMER2_OBJTYPE_HARDLINK; ipdata->uflags = 0; ipdata->rmajor = 0; ipdata->rminor = 0; ipdata->ctime = 0; ipdata->mtime = 0; ipdata->atime = 0; ipdata->btime = 0; bzero(&ipdata->uid, sizeof(ipdata->uid)); bzero(&ipdata->gid, sizeof(ipdata->gid)); ipdata->op_flags = HAMMER2_OPFLAG_DIRECTDATA; ipdata->cap_flags = 0; ipdata->mode = 0; ipdata->size = 0; ipdata->nlinks = 1; ipdata->iparent = 0; /* XXX */ ipdata->pfs_type = 0; ipdata->pfs_inum = 0; bzero(&ipdata->pfs_clid, sizeof(ipdata->pfs_clid)); bzero(&ipdata->pfs_fsid, sizeof(ipdata->pfs_fsid)); ipdata->data_quota = 0; ipdata->data_count = 0; ipdata->inode_quota = 0; ipdata->inode_count = 0; ipdata->attr_tid = 0; ipdata->dirent_tid = 0; bzero(&ipdata->u, sizeof(ipdata->u)); /* XXX transaction ids */ } else { hammer2_chain_delete(trans, chain); } /* * Return the new chain. */ hammer2_chain_unlock(chain); chain = nchain; } else { /* * Return an error */ hammer2_chain_unlock(chain); chain = NULL; } /* * Cleanup, chain/nchain already dealt with. */ done: *chainp = chain; hammer2_inode_drop(cdip); return (error); }