From: Matthew Dillon Date: Sat, 27 Jun 2015 02:12:26 +0000 (-0700) Subject: hammer2 - Refactor frontend part 14/many X-Git-Tag: v4.3.1~318 X-Git-Url: https://gitweb.dragonflybsd.org/~tuxillo/dragonfly.git/commitdiff_plain/e12ae3a59f03333cd0ce1dc645dd4719c32592b1 hammer2 - Refactor frontend part 14/many * Implement nlink, nremove, nrmdir, and nrename. The hardlink handling in nlink and nremove were the most difficult here, but the resulting design is actually cleaner than the complex do-everything routines I had before. * Remove the old hardlink handling clutter in the inode and cluster code. --- diff --git a/sys/vfs/hammer2/hammer2.h b/sys/vfs/hammer2/hammer2.h index 4d75245a37..2896ead85b 100644 --- a/sys/vfs/hammer2/hammer2.h +++ b/sys/vfs/hammer2/hammer2.h @@ -832,6 +832,8 @@ typedef struct hammer2_xop_fifo { struct hammer2_xop_head { hammer2_xop_func_t func; struct hammer2_inode *ip; + struct hammer2_inode *ip2; + struct hammer2_inode *ip3; struct hammer2_xop_group *xgrp; uint32_t check_counter; uint32_t run_mask; @@ -840,6 +842,8 @@ struct hammer2_xop_head { int error; char *name; size_t name_len; + char *name2; + size_t name2_len; hammer2_key_t lkey; hammer2_key_t nkey; hammer2_xop_fifo_t collect[HAMMER2_MAXCLUSTER]; @@ -863,6 +867,22 @@ struct hammer2_xop_nresolve { hammer2_xop_head_t head; }; +struct hammer2_xop_nlink { + hammer2_xop_head_t head; +}; + +struct hammer2_xop_unlink { + hammer2_xop_head_t head; + int isdir; + int dopermanent; +}; + +struct hammer2_xop_nrename { + hammer2_xop_head_t head; + hammer2_tid_t lhc; + int ip_key; +}; + struct hammer2_xop_scanlhc { hammer2_xop_head_t head; hammer2_key_t lhc; @@ -875,6 +895,11 @@ struct hammer2_xop_create { int flags; }; +struct hammer2_xop_connect { + hammer2_xop_head_t head; + hammer2_key_t lhc; +}; + struct hammer2_xop_destroy { hammer2_xop_head_t head; hammer2_key_t lhc; @@ -887,21 +912,29 @@ struct hammer2_xop_flush { typedef struct hammer2_xop_readdir hammer2_xop_readdir_t; typedef struct hammer2_xop_nresolve hammer2_xop_nresolve_t; +typedef struct hammer2_xop_nlink hammer2_xop_nlink_t; +typedef struct hammer2_xop_unlink hammer2_xop_unlink_t; +typedef struct hammer2_xop_nrename hammer2_xop_nrename_t; typedef struct hammer2_xop_strategy hammer2_xop_strategy_t; typedef struct hammer2_xop_create hammer2_xop_create_t; typedef struct hammer2_xop_destroy hammer2_xop_destroy_t; typedef struct hammer2_xop_scanlhc hammer2_xop_scanlhc_t; +typedef struct hammer2_xop_connect hammer2_xop_connect_t; typedef struct hammer2_xop_flush hammer2_xop_flush_t; union hammer2_xop { hammer2_xop_head_t head; hammer2_xop_readdir_t xop_readdir; hammer2_xop_nresolve_t xop_nresolve; + hammer2_xop_nlink_t xop_nlink; + hammer2_xop_unlink_t xop_unlink; + hammer2_xop_nrename_t xop_nrename; hammer2_xop_strategy_t xop_strategy; hammer2_xop_create_t xop_create; hammer2_xop_destroy_t xop_destroy; hammer2_xop_scanlhc_t xop_scanlhc; hammer2_xop_flush_t xop_flush; + hammer2_xop_connect_t xop_connect; }; typedef union hammer2_xop hammer2_xop_t; @@ -1237,27 +1270,15 @@ void hammer2_run_unlinkq(hammer2_pfs_t *pmp); hammer2_inode_t *hammer2_inode_create(hammer2_inode_t *dip, struct vattr *vap, struct ucred *cred, const uint8_t *name, size_t name_len, + hammer2_key_t inum, uint8_t type, uint8_t target_type, int flags, int *errorp); -int hammer2_inode_connect(hammer2_inode_t *ip, hammer2_cluster_t **clusterp, - int hlink, - hammer2_inode_t *dip, hammer2_cluster_t *dcluster, - const uint8_t *name, size_t name_len, - hammer2_key_t key); +int hammer2_inode_connect_simple(hammer2_inode_t *dip, hammer2_inode_t *ip, + const char *name, size_t name_len, + hammer2_key_t lhc); hammer2_inode_t *hammer2_inode_common_parent(hammer2_inode_t *fdip, hammer2_inode_t *tdip); void hammer2_inode_fsync(hammer2_inode_t *ip, hammer2_cluster_t *cparent); -int hammer2_unlink_file(hammer2_inode_t *dip, hammer2_inode_t *ip, - const uint8_t *name, size_t name_len, int isdir, - int *hlinkp, int isopen, int isrename); -int hammer2_cluster_hardlink_consolidate(hammer2_inode_t *ip, - hammer2_cluster_t **clusterp, - hammer2_inode_t *cdip, hammer2_cluster_t *cdcluster, - int nlinks); -int hammer2_cluster_hardlink_deconsolidate(hammer2_inode_t *dip, - hammer2_chain_t **chainp, hammer2_chain_t **ochainp); -int hammer2_cluster_hardlink_find(hammer2_inode_t *dip, - hammer2_cluster_t **cparentp, - hammer2_cluster_t **clusterp); +int hammer2_inode_unlink_finisher(hammer2_inode_t *ip, int isopen); int hammer2_parent_find(hammer2_cluster_t **cparentp, hammer2_cluster_t *cluster); void hammer2_inode_install_hidden(hammer2_pfs_t *pmp); @@ -1281,7 +1302,8 @@ hammer2_media_data_t *hammer2_chain_wdata(hammer2_chain_t *chain); int hammer2_chain_hardlink_find(hammer2_inode_t *dip, hammer2_chain_t **parentp, - hammer2_chain_t **chainp); + hammer2_chain_t **chainp, + int flags); /* * hammer2_cluster.c @@ -1301,6 +1323,7 @@ hammer2_chain_t *hammer2_chain_get(hammer2_chain_t *parent, int generation, hammer2_blockref_t *bref); hammer2_chain_t *hammer2_chain_lookup_init(hammer2_chain_t *parent, int flags); void hammer2_chain_lookup_done(hammer2_chain_t *parent); +hammer2_chain_t *hammer2_chain_getparent(hammer2_chain_t **parentp, int how); hammer2_chain_t *hammer2_chain_lookup(hammer2_chain_t **parentp, hammer2_key_t *key_nextp, hammer2_key_t key_beg, hammer2_key_t key_end, @@ -1394,6 +1417,12 @@ void hammer2_io_bqrelse(hammer2_io_t **diop); */ void hammer2_xop_group_init(hammer2_pfs_t *pmp, hammer2_xop_group_t *xgrp); hammer2_xop_t *hammer2_xop_alloc(hammer2_inode_t *ip); +void hammer2_xop_setname(hammer2_xop_head_t *xop, + const char *name, size_t name_len); +void hammer2_xop_setname2(hammer2_xop_head_t *xop, + const char *name, size_t name_len); +void hammer2_xop_setip2(hammer2_xop_head_t *xop, hammer2_inode_t *ip2); +void hammer2_xop_setip3(hammer2_xop_head_t *xop, hammer2_inode_t *ip3); void hammer2_xop_reinit(hammer2_xop_head_t *xop); void hammer2_xop_helper_create(hammer2_pfs_t *pmp); void hammer2_xop_helper_cleanup(hammer2_pfs_t *pmp); @@ -1407,9 +1436,13 @@ int hammer2_xop_feed(hammer2_xop_head_t *xop, hammer2_chain_t *chain, void hammer2_xop_readdir(hammer2_xop_t *xop, int clidx); void hammer2_xop_nresolve(hammer2_xop_t *xop, int clidx); +void hammer2_xop_unlink(hammer2_xop_t *xop, int clidx); +void hammer2_xop_nrename(hammer2_xop_t *xop, int clidx); +void hammer2_xop_nlink(hammer2_xop_t *xop, int clidx); void hammer2_inode_xop_scanlhc(hammer2_xop_t *xop, int clidx); void hammer2_inode_xop_create(hammer2_xop_t *xop, int clidx); void hammer2_inode_xop_destroy(hammer2_xop_t *xop, int clidx); +void hammer2_inode_xop_connect(hammer2_xop_t *xop, int clidx); void hammer2_inode_xop_flush(hammer2_xop_t *xop, int clidx); #if 0 int hammer2_xop_nmkdir(struct vop_nmkdir_args *ap); diff --git a/sys/vfs/hammer2/hammer2_chain.c b/sys/vfs/hammer2/hammer2_chain.c index cb8ba6c7b1..dcd564ac79 100644 --- a/sys/vfs/hammer2/hammer2_chain.c +++ b/sys/vfs/hammer2/hammer2_chain.c @@ -1535,7 +1535,6 @@ hammer2_chain_lookup_done(hammer2_chain_t *parent) } } -static hammer2_chain_t * hammer2_chain_getparent(hammer2_chain_t **parentp, int how) { @@ -4033,295 +4032,6 @@ hammer2_chain_testcheck(hammer2_chain_t *chain, void *bdata) return r; } -#if 0 -/* - * The chain has been removed from the original directory and replaced - * with a hardlink pointer. Move the chain to the specified parent - * directory, change the filename to "0xINODENUMBER", and adjust the key. - * The chain becomes our invisible hardlink target. - * - * The original chain must be deleted on entry. - * - * Caller is responsible for synchronizing ip->meta.name/name_len. This - * routine may be called from a XOP, so modifying ip->meta directly is out. - */ -static -void -hammer2_chain_hardlink_shiftup( - hammer2_chain_t *chain, - hammer2_key_t inum, - hammer2_chain_t **dchainp, - int *errorp) -{ - hammer2_inode_data_t *nipdata; - hammer2_chain_t *xchain; - hammer2_key_t key_dummy; - hammer2_key_t lhc; - hammer2_blockref_t bref; - - lhc = inum; /* bit 63 not set makes hardlinks invisible */ - KKASSERT((lhc & HAMMER2_DIRHASH_VISIBLE) == 0); - - /* - * Locate the inode or indirect block to create the new - * entry in. lhc represents the inode number so there is - * no collision iteration. - * - * There should be no key collisions with invisible inode keys. - */ - *errorp = 0; - xchain = hammer2_chain_lookup(dchainp, &key_dummy, lhc, lhc, 0); - if (xchain) { - hammer2_chain_unlock(xchain); - hammer2_chain_drop(xchain); - xchain =NULL; - *errorp = ENOSPC; -#if 0 - Debugger("X3"); -#endif - } - - /* - * Handle the error case - */ - if (*errorp) { - panic("error2"); - KKASSERT(xchain == NULL); - return; - } - - /* - * Use xcluster as a placeholder for (lhc). Duplicate cluster to the - * same target bref as xcluster and then delete xcluster. The - * duplication occurs after xcluster in flush order even though - * xcluster is deleted after the duplication. XXX - * - * WARNING! Duplications (to a different parent) can cause indirect - * blocks to be inserted, refactor xcluster. - * - * WARNING! Only key and keybits is extracted from a passed-in bref. - */ - bref = chain->bref; - bref.key = lhc; /* invisible dir entry key */ - bref.keybits = 0; - hammer2_chain_rename(&bref, dchainp, chain, 0); - - /* - * cluster is now 'live' again.. adjust the filename. - * - * Directory entries are inodes but this is a hidden hardlink - * target. The name isn't used but to ease debugging give it - * a name after its inode number. - */ - hammer2_chain_modify(chain, 0); - nipdata = &chain->data->ipdata; - ksnprintf(nipdata->filename, sizeof(nipdata->filename), - "0x%016jx", (intmax_t)inum); - - /* - * Warning: Caller must adjust ip->meta.name_lne, name_key, - * and nlinks. - */ - nipdata->meta.name_len = strlen(nipdata->filename); - nipdata->meta.name_key = lhc; - /*ip->meta.nlinks += nlinks;*/ -} - -/* - * Given an exclusively locked inode and cluster we consolidate the cluster - * for hardlink creation, adding (nlinks) to the file's link count and - * potentially relocating the inode to (cdip) which is a parent directory - * common to both the current location of the inode and the intended new - * hardlink. - * - * Replaces (*clusterp) if consolidation occurred, unlocking the old cluster - * and returning a new locked cluster. - * - * NOTE! This function will also replace ip->cluster. - */ -int -hammer2_chain_hardlink_consolidate( - hammer2_inode_t *ip, - hammer2_chain_t **chainp, - hammer2_inode_t *cdip, - hammer2_chain_t *cdchain, - int nlinks) -{ - hammer2_chain_t *chain; - hammer2_chain_t *parent; - int error; - - chain = *chainp; - if (nlinks == 0 && /* no hardlink needed */ - (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE)) { - return (0); - } - - if (hammer2_hardlink_enable == 0) { /* disallow hardlinks */ - hammer2_chain_unlock(chain); - hammer2_chain_drop(chain); - *chainp = NULL; - return (ENOTSUP); - } - - parent = NULL; - - /* - * 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 && - (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE) == 0) { - if (nlinks) { - hammer2_inode_modify(ip); - ip->meta.nlinks += nlinks; -#if 0 - hammer2_cluster_modify(cluster, 0); - wipdata = &hammer2_cluster_wdata(cluster)->ipdata; - wipdata->meta.nlinks += nlinks; - hammer2_cluster_modsync(cluster); - ripdata = wipdata; -#endif - } - error = 0; - goto done; - } - - /* - * Cluster is the real inode. The originating directory is locked - * by the caller so we can manipulate it without worrying about races - * against other lookups. - * - * If cluster is visible we need to delete it from the current - * location and create a hardlink pointer in its place. If it is - * not visible we need only delete it. Then later cluster will be - * renamed to a parent directory and converted (if necessary) to - * a hidden inode (via shiftup). - * - * NOTE! We must hold cparent locked through the delete/create/rename - * operation to ensure that other threads block resolving to - * the same hardlink, otherwise the other threads may not see - * the hardlink. - */ - KKASSERT((cluster->focus->flags & HAMMER2_CHAIN_DELETED) == 0); - cparent = hammer2_cluster_parent(cluster); - - hammer2_cluster_delete(cparent, cluster, 0); - - KKASSERT(ip->meta.type != HAMMER2_OBJTYPE_HARDLINK); - if (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE) { - const hammer2_inode_data_t *ripdata; - hammer2_inode_data_t *wipdata; - hammer2_cluster_t *ncluster; - hammer2_key_t lhc; - - ncluster = NULL; - lhc = cluster->focus->bref.key; - error = hammer2_cluster_create(cparent, &ncluster, - lhc, 0, - HAMMER2_BREF_TYPE_INODE, - HAMMER2_INODE_BYTES, - 0); - hammer2_cluster_modify(ncluster, 0); - wipdata = &hammer2_cluster_wdata(ncluster)->ipdata; - - /* wipdata->meta.comp_algo = ip->meta.comp_algo; */ - wipdata->meta.comp_algo = 0; - wipdata->meta.check_algo = 0; - wipdata->meta.version = HAMMER2_INODE_VERSION_ONE; - wipdata->meta.inum = ip->meta.inum; - wipdata->meta.target_type = ip->meta.type; - wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK; - wipdata->meta.uflags = 0; - wipdata->meta.rmajor = 0; - wipdata->meta.rminor = 0; - wipdata->meta.ctime = 0; - wipdata->meta.mtime = 0; - wipdata->meta.atime = 0; - wipdata->meta.btime = 0; - bzero(&wipdata->meta.uid, sizeof(wipdata->meta.uid)); - bzero(&wipdata->meta.gid, sizeof(wipdata->meta.gid)); - wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA; - wipdata->meta.cap_flags = 0; - wipdata->meta.mode = 0; - wipdata->meta.size = 0; - wipdata->meta.nlinks = 1; - wipdata->meta.iparent = 0; /* XXX */ - wipdata->meta.pfs_type = 0; - wipdata->meta.pfs_inum = 0; - bzero(&wipdata->meta.pfs_clid, sizeof(wipdata->meta.pfs_clid)); - bzero(&wipdata->meta.pfs_fsid, sizeof(wipdata->meta.pfs_fsid)); - wipdata->meta.data_quota = 0; - /* wipdata->data_count = 0; */ - wipdata->meta.inode_quota = 0; - /* wipdata->inode_count = 0; */ - wipdata->meta.attr_tid = 0; - wipdata->meta.dirent_tid = 0; - bzero(&wipdata->u, sizeof(wipdata->u)); - ripdata = &hammer2_cluster_rdata(cluster)->ipdata; - KKASSERT(ip->meta.name_len <= sizeof(wipdata->filename)); - bcopy(ripdata->filename, wipdata->filename, - ip->meta.name_len); - wipdata->meta.name_key = ncluster->focus->bref.key; - wipdata->meta.name_len = ip->meta.name_len; - /* XXX transaction ids */ - hammer2_cluster_modsync(ncluster); - hammer2_cluster_unlock(ncluster); - hammer2_cluster_drop(ncluster); - } - - /* - * cluster represents the hardlink target and is now flagged deleted. - * duplicate it to the parent directory and adjust nlinks. - * - * WARNING! The shiftup() call can cause ncluster to be moved into - * an indirect block, and our ncluster will wind up pointing - * to the older/original version. - */ - KKASSERT(cluster->focus->flags & HAMMER2_CHAIN_DELETED); - hammer2_chain_hardlink_shiftup(cluster, ip, cdip, cdcluster, - nlinks, &error); - - if (error == 0) - hammer2_inode_repoint(ip, cdip, cluster); - -done: - /* - * Cleanup, cluster/ncluster already dealt with. - * - * Return the shifted cluster in *clusterp. - */ - if (cparent) { - hammer2_cluster_unlock(cparent); - hammer2_cluster_drop(cparent); - } - *clusterp = cluster; - - return (error); -} - -/* - * If (*ochainp) is non-NULL it points to the forward OBJTYPE_HARDLINK - * inode while (*chainp) points to the resolved (hidden hardlink - * target) inode. In this situation when nlinks is 1 we wish to - * deconsolidate the hardlink, moving it back to the directory that now - * represents the only remaining link. - */ -int -hammer2_chain_hardlink_deconsolidate( - hammer2_inode_t *dip, - hammer2_chain_t **chainp, - hammer2_chain_t **ochainp) -{ - if (*ochainp == NULL) - return (0); - /* XXX */ - return (0); -} - -#endif - /* * The caller presents a shared-locked (parent, chain) where the chain * is of type HAMMER2_OBJTYPE_HARDLINK. The caller must hold the ip @@ -4336,7 +4046,8 @@ hammer2_chain_hardlink_deconsolidate( int hammer2_chain_hardlink_find(hammer2_inode_t *dip, hammer2_chain_t **parentp, - hammer2_chain_t **chainp) + hammer2_chain_t **chainp, + int flags) { hammer2_chain_t *parent; hammer2_chain_t *rchain; @@ -4357,8 +4068,7 @@ hammer2_chain_hardlink_find(hammer2_inode_t *dip, int nloops; rchain = hammer2_chain_lookup(parentp, &key_dummy, lhc, lhc, - &cache_index, - HAMMER2_LOOKUP_SHARED); + &cache_index, flags); if (rchain) break; @@ -4374,13 +4084,15 @@ hammer2_chain_hardlink_find(hammer2_inode_t *dip, parent->bref.type == HAMMER2_BREF_TYPE_INODE) { nloops = 1; } + if (parent->bref.flags & HAMMER2_BREF_FLAG_PFSROOT) + goto done; + if (parent->parent == NULL) + goto done; parent = parent->parent; - if (parent == NULL) - break; hammer2_chain_ref(parent); hammer2_chain_unlock(*parentp); hammer2_chain_lock(parent, HAMMER2_RESOLVE_ALWAYS | - HAMMER2_RESOLVE_SHARED); + flags); if ((*parentp)->parent == parent) { hammer2_chain_drop(*parentp); *parentp = parent; @@ -4389,12 +4101,14 @@ hammer2_chain_hardlink_find(hammer2_inode_t *dip, hammer2_chain_drop(parent); hammer2_chain_lock(*parentp, HAMMER2_RESOLVE_ALWAYS | - HAMMER2_RESOLVE_SHARED); + flags); parent = NULL; /* safety */ + /* retry */ } } } +done: *chainp = rchain; - return (rchain ? EIO : 0); + return (rchain ? EINVAL : 0); } diff --git a/sys/vfs/hammer2/hammer2_cluster.c b/sys/vfs/hammer2/hammer2_cluster.c index f8f10bd625..a634ce3c1f 100644 --- a/sys/vfs/hammer2/hammer2_cluster.c +++ b/sys/vfs/hammer2/hammer2_cluster.c @@ -1954,6 +1954,7 @@ hammer2_cluster_snapshot(hammer2_cluster_t *ocluster, vat.va_mode = 0755; nip = hammer2_inode_create(hmp->spmp->iroot, &vat, proc0.p_ucred, pmp->name, name_len, + 1, 0, 0, HAMMER2_INSERT_PFSROOT, &error); if (nip) { @@ -2165,390 +2166,3 @@ hammer2_cluster_load_async(hammer2_cluster_t *cluster, hammer2_adjreadcounter(&chain->bref, chain->bytes); hammer2_io_getblk(hmp, bref->data_off, chain->bytes, iocb); } - -/* - * The cluster has been removed from the original directory and replaced - * with a hardlink pointer. Move the cluster to the specified parent - * directory, change the filename to "0xINODENUMBER", and adjust the key. - * The cluster becomes our invisible hardlink target. - * - * The original cluster must be deleted on entry. - */ -static -void -hammer2_cluster_hardlink_shiftup( - hammer2_cluster_t *cluster, - hammer2_inode_t *ip, hammer2_inode_t *dip, - hammer2_cluster_t *dcluster, - int nlinks, int *errorp) -{ - hammer2_inode_data_t *nipdata; - hammer2_cluster_t *xcluster; - hammer2_key_t key_dummy; - hammer2_key_t lhc; - hammer2_blockref_t bref; - - lhc = ip->meta.inum; -#if 0 - iptmp = &hammer2_cluster_rdata(cluster)->ipdata; - lhc = iptmp->meta.inum; -#endif - KKASSERT((lhc & HAMMER2_DIRHASH_VISIBLE) == 0); - - /* - * Locate the inode or indirect block to create the new - * entry in. lhc represents the inode number so there is - * no collision iteration. - * - * There should be no key collisions with invisible inode keys. - * - * WARNING! Must use inode_lock_ex() on dip to handle a stale - * dip->cluster cache. - */ - *errorp = 0; - xcluster = hammer2_cluster_lookup(dcluster, &key_dummy, - lhc, lhc, 0); - if (xcluster) { - kprintf("X3 chain %p dip %p dchain %p dip->chain %p\n", - xcluster->focus, dip, dcluster->focus, - dip->cluster.focus); - hammer2_cluster_unlock(xcluster); - hammer2_cluster_drop(xcluster); - xcluster = NULL; - *errorp = ENOSPC; -#if 0 - Debugger("X3"); -#endif - } - - /* - * Handle the error case - */ - if (*errorp) { - panic("error2"); - KKASSERT(xcluster == NULL); - return; - } - - /* - * Use xcluster as a placeholder for (lhc). Duplicate cluster to the - * same target bref as xcluster and then delete xcluster. The - * duplication occurs after xcluster in flush order even though - * xcluster is deleted after the duplication. XXX - * - * WARNING! Duplications (to a different parent) can cause indirect - * blocks to be inserted, refactor xcluster. - * - * WARNING! Only key and keybits is extracted from a passed-in bref. - */ - hammer2_cluster_bref(cluster, &bref); - bref.key = lhc; /* invisible dir entry key */ - bref.keybits = 0; - hammer2_cluster_rename(&bref, dcluster, cluster, 0); - - /* - * cluster is now 'live' again.. adjust the filename. - * - * Directory entries are inodes but this is a hidden hardlink - * target. The name isn't used but to ease debugging give it - * a name after its inode number. - */ - hammer2_inode_modify(ip); - hammer2_cluster_modify(cluster, 0); - - nipdata = &hammer2_cluster_wdata(cluster)->ipdata; - ksnprintf(nipdata->filename, sizeof(nipdata->filename), - "0x%016jx", (intmax_t)nipdata->meta.inum); - ip->meta.name_len = strlen(nipdata->filename); - ip->meta.name_key = lhc; - ip->meta.nlinks += nlinks; - - /* - * Resync nipdata->meta from the local copy. - */ - nipdata->meta = ip->meta; - hammer2_cluster_modsync(cluster); -} - -/* - * Given an exclusively locked inode and cluster we consolidate the cluster - * for hardlink creation, adding (nlinks) to the file's link count and - * potentially relocating the inode to (cdip) which is a parent directory - * common to both the current location of the inode and the intended new - * hardlink. - * - * Replaces (*clusterp) if consolidation occurred, unlocking the old cluster - * and returning a new locked cluster. - * - * NOTE! This function will also replace ip->cluster. - */ -int -hammer2_cluster_hardlink_consolidate( - hammer2_inode_t *ip, - hammer2_cluster_t **clusterp, - hammer2_inode_t *cdip, - hammer2_cluster_t *cdcluster, - int nlinks) -{ - hammer2_cluster_t *cluster; - hammer2_cluster_t *cparent; - int error; - - cluster = *clusterp; - if (nlinks == 0 && /* no hardlink needed */ - (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE)) { - return (0); - } - - if (hammer2_hardlink_enable == 0) { /* disallow hardlinks */ - hammer2_cluster_unlock(cluster); - hammer2_cluster_drop(cluster); - *clusterp = NULL; - return (ENOTSUP); - } - - cparent = NULL; - - /* - * 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 && - (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE) == 0) { - if (nlinks) { - hammer2_inode_modify(ip); - ip->meta.nlinks += nlinks; -#if 0 - hammer2_cluster_modify(cluster, 0); - wipdata = &hammer2_cluster_wdata(cluster)->ipdata; - wipdata->meta.nlinks += nlinks; - hammer2_cluster_modsync(cluster); - ripdata = wipdata; -#endif - } - error = 0; - goto done; - } - - /* - * Cluster is the real inode. The originating directory is locked - * by the caller so we can manipulate it without worrying about races - * against other lookups. - * - * If cluster is visible we need to delete it from the current - * location and create a hardlink pointer in its place. If it is - * not visible we need only delete it. Then later cluster will be - * renamed to a parent directory and converted (if necessary) to - * a hidden inode (via shiftup). - * - * NOTE! We must hold cparent locked through the delete/create/rename - * operation to ensure that other threads block resolving to - * the same hardlink, otherwise the other threads may not see - * the hardlink. - */ - KKASSERT((cluster->focus->flags & HAMMER2_CHAIN_DELETED) == 0); - cparent = hammer2_cluster_parent(cluster); - - hammer2_cluster_delete(cparent, cluster, 0); - - KKASSERT(ip->meta.type != HAMMER2_OBJTYPE_HARDLINK); - if (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE) { - const hammer2_inode_data_t *ripdata; - hammer2_inode_data_t *wipdata; - hammer2_cluster_t *ncluster; - hammer2_key_t lhc; - - ncluster = NULL; - lhc = cluster->focus->bref.key; - error = hammer2_cluster_create(ip->pmp, cparent, &ncluster, - lhc, 0, - HAMMER2_BREF_TYPE_INODE, - HAMMER2_INODE_BYTES, - 0); - hammer2_cluster_modify(ncluster, 0); - wipdata = &hammer2_cluster_wdata(ncluster)->ipdata; - - /* wipdata->meta.comp_algo = ip->meta.comp_algo; */ - wipdata->meta.comp_algo = 0; - wipdata->meta.check_algo = 0; - wipdata->meta.version = HAMMER2_INODE_VERSION_ONE; - wipdata->meta.inum = ip->meta.inum; - wipdata->meta.target_type = ip->meta.type; - wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK; - wipdata->meta.uflags = 0; - wipdata->meta.rmajor = 0; - wipdata->meta.rminor = 0; - wipdata->meta.ctime = 0; - wipdata->meta.mtime = 0; - wipdata->meta.atime = 0; - wipdata->meta.btime = 0; - bzero(&wipdata->meta.uid, sizeof(wipdata->meta.uid)); - bzero(&wipdata->meta.gid, sizeof(wipdata->meta.gid)); - wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA; - wipdata->meta.cap_flags = 0; - wipdata->meta.mode = 0; - wipdata->meta.size = 0; - wipdata->meta.nlinks = 1; - wipdata->meta.iparent = 0; /* XXX */ - wipdata->meta.pfs_type = 0; - wipdata->meta.pfs_inum = 0; - bzero(&wipdata->meta.pfs_clid, sizeof(wipdata->meta.pfs_clid)); - bzero(&wipdata->meta.pfs_fsid, sizeof(wipdata->meta.pfs_fsid)); - wipdata->meta.data_quota = 0; - /* wipdata->data_count = 0; */ - wipdata->meta.inode_quota = 0; - /* wipdata->inode_count = 0; */ - wipdata->meta.attr_tid = 0; - wipdata->meta.dirent_tid = 0; - bzero(&wipdata->u, sizeof(wipdata->u)); - ripdata = &hammer2_cluster_rdata(cluster)->ipdata; - KKASSERT(ip->meta.name_len <= sizeof(wipdata->filename)); - bcopy(ripdata->filename, wipdata->filename, - ip->meta.name_len); - wipdata->meta.name_key = ncluster->focus->bref.key; - wipdata->meta.name_len = ip->meta.name_len; - /* XXX transaction ids */ - hammer2_cluster_modsync(ncluster); - hammer2_cluster_unlock(ncluster); - hammer2_cluster_drop(ncluster); - } - - /* - * cluster represents the hardlink target and is now flagged deleted. - * duplicate it to the parent directory and adjust nlinks. - * - * WARNING! The shiftup() call can cause ncluster to be moved into - * an indirect block, and our ncluster will wind up pointing - * to the older/original version. - */ - KKASSERT(cluster->focus->flags & HAMMER2_CHAIN_DELETED); - hammer2_cluster_hardlink_shiftup(cluster, ip, cdip, cdcluster, - nlinks, &error); - - if (error == 0) - hammer2_inode_repoint(ip, cdip, cluster); - -done: - /* - * Cleanup, cluster/ncluster already dealt with. - * - * Return the shifted cluster in *clusterp. - */ - if (cparent) { - hammer2_cluster_unlock(cparent); - hammer2_cluster_drop(cparent); - } - *clusterp = cluster; - - return (error); -} - -/* - * If (*ochainp) is non-NULL it points to the forward OBJTYPE_HARDLINK - * inode while (*chainp) points to the resolved (hidden hardlink - * target) inode. In this situation when nlinks is 1 we wish to - * deconsolidate the hardlink, moving it back to the directory that now - * represents the only remaining link. - */ -int -hammer2_cluster_hardlink_deconsolidate( - hammer2_inode_t *dip, - hammer2_chain_t **chainp, - hammer2_chain_t **ochainp) -{ - if (*ochainp == NULL) - return (0); - /* XXX */ - return (0); -} - -/* - * The caller presents a locked cluster with an obj_type of - * HAMMER2_OBJTYPE_HARDLINK in (*clusterp). This routine will locate - * the inode and replace (*clusterp) with a new locked cluster containing - * the target hardlink, also locked. The original cluster will be - * unlocked and released. - * - * If cparentp is not NULL a locked cluster representing the hardlink's - * parent is also returned. - * - * If we are unable to locate the hardlink target EIO is returned, - * (*cparentp) is set to NULL, the original passed-in (*clusterp) - * will be unlocked and released and (*clusterp) will be set to NULL - * as well. - */ -int -hammer2_cluster_hardlink_find(hammer2_inode_t *dip, - hammer2_cluster_t **cparentp, - hammer2_cluster_t **clusterp) -{ - const hammer2_inode_data_t *ipdata; - hammer2_cluster_t *cluster; - hammer2_cluster_t *cparent; - hammer2_cluster_t *rcluster; - hammer2_inode_t *ip; - hammer2_inode_t *pip; - hammer2_key_t key_dummy; - hammer2_key_t lhc; - - cluster = *clusterp; - pip = dip; - hammer2_inode_ref(pip); /* for loop */ - - /* - * Locate the hardlink. pip is referenced and not locked. - * Unlock and release (*clusterp) after extracting the needed - * data. - */ - ipdata = &hammer2_cluster_rdata(cluster)->ipdata; - lhc = ipdata->meta.inum; - ipdata = NULL; /* safety */ - hammer2_cluster_unlock(cluster); - hammer2_cluster_drop(cluster); - *clusterp = NULL; /* safety */ - - rcluster = NULL; - cparent = NULL; - - while ((ip = pip) != NULL) { - hammer2_inode_lock(ip, HAMMER2_RESOLVE_ALWAYS); - cparent = hammer2_inode_cluster(ip, HAMMER2_RESOLVE_ALWAYS); - hammer2_inode_drop(ip); /* loop */ - KKASSERT(hammer2_cluster_type(cparent) == - HAMMER2_BREF_TYPE_INODE); - rcluster = hammer2_cluster_lookup(cparent, &key_dummy, - lhc, lhc, 0); - if (rcluster) - break; - hammer2_cluster_lookup_done(cparent); /* discard parent */ - cparent = NULL; /* safety */ - pip = ip->pip; /* safe, ip held locked */ - if (pip) - hammer2_inode_ref(pip); /* loop */ - hammer2_inode_unlock(ip, NULL); - } - - /* - * chain is locked, ip is locked. Unlock ip, return the locked - * chain. *ipp is already set w/a ref count and not locked. - * - * (cparent is already unlocked). - */ - *clusterp = rcluster; - if (rcluster) { - if (cparentp) { - *cparentp = cparent; - hammer2_inode_unlock(ip, NULL); - } else { - hammer2_inode_unlock(ip, cparent); - } - return (0); - } else { - if (cparentp) - *cparentp = NULL; - if (ip) - hammer2_inode_unlock(ip, cparent); - return (EIO); - } -} diff --git a/sys/vfs/hammer2/hammer2_inode.c b/sys/vfs/hammer2/hammer2_inode.c index ee577a0212..1202ba15cf 100644 --- a/sys/vfs/hammer2/hammer2_inode.c +++ b/sys/vfs/hammer2/hammer2_inode.c @@ -43,10 +43,6 @@ #define INODE_DEBUG 0 -static void hammer2_inode_move_to_hidden(hammer2_cluster_t **cparentp, - hammer2_cluster_t **clusterp, - hammer2_tid_t inum); - RB_GENERATE2(hammer2_inode_tree, hammer2_inode, rbnode, hammer2_inode_cmp, hammer2_tid_t, meta.inum); @@ -620,6 +616,7 @@ hammer2_inode_t * hammer2_inode_create(hammer2_inode_t *dip, struct vattr *vap, struct ucred *cred, const uint8_t *name, size_t name_len, + hammer2_key_t inum, uint8_t type, uint8_t target_type, int flags, int *errorp) { hammer2_xop_scanlhc_t *sxop; @@ -694,7 +691,6 @@ hammer2_inode_create(hammer2_inode_t *dip, if (vap) { xop->meta.type = hammer2_get_obj_type(vap->va_type); - xop->meta.inum = hammer2_trans_newinum(dip->pmp); switch (xop->meta.type) { case HAMMER2_OBJTYPE_CDEV: @@ -705,10 +701,12 @@ hammer2_inode_create(hammer2_inode_t *dip, default: break; } + type = xop->meta.type; } else { - xop->meta.type = HAMMER2_OBJTYPE_DIRECTORY; - xop->meta.inum = 1; + xop->meta.type = type; + xop->meta.target_type = target_type; } + xop->meta.inum = inum; /* Inherit parent's inode compression mode. */ xop->meta.comp_algo = dip_comp_algo; @@ -752,13 +750,11 @@ hammer2_inode_create(hammer2_inode_t *dip, * the size is extended past the embedded limit. */ if (xop->meta.type == HAMMER2_OBJTYPE_REGFILE || - xop->meta.type == HAMMER2_OBJTYPE_SOFTLINK) { + xop->meta.type == HAMMER2_OBJTYPE_SOFTLINK || + xop->meta.type == HAMMER2_OBJTYPE_HARDLINK) { xop->meta.op_flags |= HAMMER2_OPFLAG_DIRECTDATA; } - - xop->head.name = kmalloc(name_len + 1, M_HAMMER2, M_WAITOK | M_ZERO); - xop->head.name_len = name_len; - bcopy(name, xop->head.name, name_len); + hammer2_xop_setname(&xop->head, name, name_len); xop->meta.name_len = name_len; xop->meta.name_key = lhc; KKASSERT(name_len < HAMMER2_INODE_MAXNAME); @@ -777,7 +773,7 @@ hammer2_inode_create(hammer2_inode_t *dip, } /* - * Set up the new inode. + * Set up the new inode if not a hardlink pointer. * * NOTE: *_get() integrates chain's lock into the inode lock. * @@ -787,8 +783,12 @@ hammer2_inode_create(hammer2_inode_t *dip, * * NOTE: nipdata will have chain's blockset data. */ - nip = hammer2_inode_get(dip->pmp, dip, &xop->head.cluster); - nip->comp_heuristic = 0; + if (type != HAMMER2_OBJTYPE_HARDLINK) { + nip = hammer2_inode_get(dip->pmp, dip, &xop->head.cluster); + nip->comp_heuristic = 0; + } else { + nip = NULL; + } done: hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); @@ -799,201 +799,90 @@ done2: } /* - * Connect the target inode represented by (cluster) to the media topology - * at (dip, name, len). The caller can pass a rough *chainp, this function - * will issue lookup()s to position the parent chain properly for the - * chain insertion. + * Connect the disconnected inode (ip) to the directory (dip) with the + * specified (name, name_len). If name is NULL, (lhc) will be used as + * the directory key and the inode's embedded name will not be modified + * for future recovery purposes. * - * If hlink is TRUE this function creates an OBJTYPE_HARDLINK directory - * entry instead of connecting (cluster). - * - * If hlink is FALSE this function expects (cluster) to be unparented. + * dip and ip must both be locked exclusively (dip in particular to avoid + * lhc collisions). */ int -hammer2_inode_connect(hammer2_inode_t *ip, hammer2_cluster_t **clusterp, - int hlink, - hammer2_inode_t *dip, hammer2_cluster_t *dcluster, - const uint8_t *name, size_t name_len, - hammer2_key_t lhc) +hammer2_inode_connect_simple(hammer2_inode_t *dip, hammer2_inode_t *ip, + const char *name, size_t name_len, + hammer2_key_t lhc) { - hammer2_inode_data_t *wipdata; - hammer2_cluster_t *ocluster; - hammer2_cluster_t *ncluster; - hammer2_pfs_t *pmp; - hammer2_key_t key_dummy; + hammer2_xop_scanlhc_t *sxop; + hammer2_xop_connect_t *xop; + hammer2_inode_t *opip; + hammer2_key_t lhcbase; int error; /* - * Since ocluster is either disconnected from the topology or - * represents a hardlink terminus which is always a parent of or - * equal to dip, we should be able to safely lock dip->chain for - * our setup. - * - * WARNING! Must use inode_lock_ex() on dip to handle a stale - * dip->cluster. - * - * If name is non-NULL we calculate lhc, else we use the passed-in - * lhc. + * Calculate the lhc and resolve the collision space. */ - ocluster = *clusterp; - pmp = dip->pmp; - if (name) { - lhc = hammer2_dirhash(name, name_len); - - /* - * Locate the inode or indirect block to create the new - * entry in. At the same time check for key collisions - * and iterate until we don't get one. - */ - error = 0; - while (error == 0) { - ncluster = hammer2_cluster_lookup(dcluster, &key_dummy, - lhc, lhc, 0); - if (ncluster == NULL) + lhc = lhcbase = hammer2_dirhash(name, name_len); + sxop = &hammer2_xop_alloc(dip)->xop_scanlhc; + sxop->lhc = lhc; + hammer2_xop_start(&sxop->head, hammer2_inode_xop_scanlhc); + while ((error = hammer2_xop_collect(&sxop->head, 0)) == 0) { + if (lhc != sxop->head.cluster.focus->bref.key) break; - if ((lhc & HAMMER2_DIRHASH_LOMASK) == - HAMMER2_DIRHASH_LOMASK) { - error = ENOSPC; - } - hammer2_cluster_unlock(ncluster); - hammer2_cluster_drop(ncluster); - ncluster = NULL; ++lhc; } - } else { - /* - * Reconnect to specific key (used when moving - * unlinked-but-open files into the hidden directory). - */ - ncluster = hammer2_cluster_lookup(dcluster, &key_dummy, - lhc, lhc, 0); - KKASSERT(ncluster == NULL); - error = 0; - } + hammer2_xop_retire(&sxop->head, HAMMER2_XOPMASK_VOP); - if (error == 0) { - if (hlink) { - /* - * Hardlink pointer needed, create totally fresh - * directory entry. - * - * We must refactor ocluster because it might have - * been shifted into an indirect cluster by the - * create. - */ - KKASSERT(ncluster == NULL); - error = hammer2_cluster_create(pmp, - dcluster, &ncluster, - lhc, 0, - HAMMER2_BREF_TYPE_INODE, - HAMMER2_INODE_BYTES, - 0); - } else { - /* - * Reconnect the original cluster under the new name. - * Original cluster must have already been deleted by - * teh caller. - * - * WARNING! Can cause held-over clusters to require a - * refactor. Fortunately we have none (our - * locked clusters are passed into and - * modified by the call). - */ - ncluster = ocluster; - ocluster = NULL; - error = hammer2_cluster_create(pmp, dcluster, &ncluster, - lhc, 0, - HAMMER2_BREF_TYPE_INODE, - HAMMER2_INODE_BYTES, - 0); + if (error) { + if (error != ENOENT) + goto done; + ++lhc; + error = 0; } + if ((lhcbase ^ lhc) & ~HAMMER2_DIRHASH_LOMASK) { + error = ENOSPC; + goto done; + } + } else { + error = 0; } /* - * Unlock stuff. + * Formally reconnect the in-memory structure. ip must + * be locked exclusively to safely change ip->pip. */ - KKASSERT(error != EAGAIN); + if (ip->pip != dip) { + hammer2_inode_ref(dip); + opip = ip->pip; + ip->pip = dip; + if (opip) + hammer2_inode_drop(opip); + } /* - * ncluster should be NULL on error, leave ocluster - * (ocluster == *clusterp) alone. + * Connect her up */ - if (error) { - KKASSERT(ncluster == NULL); - return (error); - } + xop = &hammer2_xop_alloc(dip)->xop_connect; + if (name) + hammer2_xop_setname(&xop->head, name, name_len); + hammer2_xop_setip2(&xop->head, ip); + xop->lhc = lhc; + hammer2_xop_start(&xop->head, hammer2_inode_xop_connect); + error = hammer2_xop_collect(&xop->head, 0); + hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); /* - * Directory entries are inodes so if the name has changed we have - * to update the inode. - * - * When creating an OBJTYPE_HARDLINK entry remember to unlock the - * cluster, the caller will access the hardlink via the actual hardlink - * target file and not the hardlink pointer entry, so we must still - * return ocluster. + * On success make the same adjustments to ip->meta or the + * next flush may blow up the chain. */ - if (hlink && hammer2_hardlink_enable >= 0) { - /* - * Create the HARDLINK pointer. oip represents the hardlink - * target in this situation. - * - * We will return ocluster (the hardlink target). - */ - hammer2_cluster_modify(ncluster, 0); - KKASSERT(name_len < HAMMER2_INODE_MAXNAME); - wipdata = &hammer2_cluster_wdata(ncluster)->ipdata; - bcopy(name, wipdata->filename, name_len); - wipdata->meta.name_key = lhc; - wipdata->meta.name_len = name_len; - wipdata->meta.target_type = - hammer2_cluster_rdata(ocluster)->ipdata.meta.type; - wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK; - wipdata->meta.inum = - hammer2_cluster_rdata(ocluster)->ipdata.meta.inum; - wipdata->meta.version = HAMMER2_INODE_VERSION_ONE; - wipdata->meta.nlinks = 1; - wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA; - hammer2_cluster_modsync(ncluster); - hammer2_cluster_unlock(ncluster); - hammer2_cluster_drop(ncluster); - ncluster = ocluster; - ocluster = NULL; - } else { - /* - * ncluster is a duplicate of ocluster at the new location. - * We must fixup the name stored in the inode data. - * The bref key has already been adjusted by inode_connect(). - */ + if (error == 0) { hammer2_inode_modify(ip); - hammer2_cluster_modify(ncluster, 0); - wipdata = &hammer2_cluster_wdata(ncluster)->ipdata; - - KKASSERT(name_len < HAMMER2_INODE_MAXNAME); - bcopy(name, wipdata->filename, name_len); ip->meta.name_key = lhc; - ip->meta.name_len = name_len; - ip->meta.nlinks = 1; - - /* - * Resync wipdata->meta from the local copy. - */ - wipdata->meta = ip->meta; - hammer2_cluster_modsync(ncluster); - } - - /* - * We are replacing ocluster with ncluster, unlock ocluster. In the - * case where ocluster is left unchanged the code above sets - * ncluster to ocluster and ocluster to NULL, resulting in a NOP here. - */ - if (ocluster) { - hammer2_cluster_unlock(ocluster); - hammer2_cluster_drop(ocluster); + if (name) + ip->meta.name_len = name_len; } - *clusterp = ncluster; - - return (0); +done: + return error; } /* @@ -1156,311 +1045,73 @@ hammer2_inode_repoint_one(hammer2_inode_t *ip, hammer2_cluster_t *cluster, } /* - * Unlink the file (dip, name, name_len), from the specified directory inode. - * If the caller also has the hammer2_inode the caller must pass it locked as - * (ip), but may pass NULL if it does not have the inode in hand. - * - * The directory inode does not need to be locked. - * - * isdir determines whether a directory/non-directory check should be made. - * No check is made if isdir is set to -1. - * - * isopen specifies whether special unlink-with-open-descriptor handling - * must be performed. If set to -1 the caller is deleting a PFS and we - * check whether the chain is mounted or not (chain->pmp != NULL). 1 is - * implied if it is mounted. + * Called with a locked inode to finish unlinking an inode after xop_unlink + * had been run. This function is responsible for decrementing nlinks and + * moving deleted inodes to the hidden directory if they are still open. * - * If isopen is 1 and nlinks drops to 0 this function must move the chain - * to a special hidden directory until last-close occurs on the file. + * We don't bother decrementing nlinks if the file is not open and this was + * the last link. * - * NOTE! The underlying file can still be active with open descriptors - * or if the inode is being manually held (e.g. for rename). + * If the inode is a hardlink target it's chain has not yet been deleted, + * otherwise it's chain has been deleted. * - * NOTE! When unlinking an open file the inode will be temporarily moved to - * a hidden directory, otherwise the inode will be deleted. + * If isopen then any prior deletion was not permanent and the inode must + * be moved to the hidden directory. */ int -hammer2_unlink_file(hammer2_inode_t *dip, hammer2_inode_t *ip, - const uint8_t *name, size_t name_len, - int isdir, int *hlinkp, - int isopen, int isrename) +hammer2_inode_unlink_finisher(hammer2_inode_t *ip, int isopen) { - const hammer2_inode_data_t *ripdata; - hammer2_cluster_t *cparent; - hammer2_cluster_t *hcluster; - hammer2_cluster_t *hparent; - hammer2_cluster_t *cluster; - hammer2_cluster_t *dparent; - hammer2_cluster_t *dcluster; - hammer2_key_t key_dummy; - hammer2_key_t key_next; - hammer2_key_t lhc; - int last_link; + hammer2_pfs_t *pmp; int error; - int hlink; - int myip; - uint8_t type; - - error = 0; - hlink = 0; - myip = 0; - hcluster = NULL; - hparent = NULL; - lhc = hammer2_dirhash(name, name_len); -again: - /* - * Locate the filename in the directory and instantiate the ip - * if necessary. If the ip is already known we must still locate - * the filename to adjust cparent for possible deletion. - */ - hammer2_inode_lock(dip, HAMMER2_RESOLVE_ALWAYS); - cparent = hammer2_inode_cluster(dip, HAMMER2_RESOLVE_ALWAYS); - cluster = hammer2_cluster_lookup(cparent, &key_next, - lhc, lhc + HAMMER2_DIRHASH_LOMASK, 0); - while (cluster) { - if (hammer2_cluster_type(cluster) == HAMMER2_BREF_TYPE_INODE) { - ripdata = &hammer2_cluster_rdata(cluster)->ipdata; - if (ripdata->meta.name_len == name_len && - bcmp(ripdata->filename, name, name_len) == 0) { - break; - } - } - cluster = hammer2_cluster_next(cparent, cluster, &key_next, - key_next, - lhc + HAMMER2_DIRHASH_LOMASK, - 0); - } - hammer2_inode_unlock(dip, NULL); /* retain cparent */ + pmp = ip->pmp; /* - * Not found or wrong type (isdir < 0 disables the type check). - * If a hardlink pointer, type checks use the hardlink target. + * Decrement nlinks. If this is the last link and the file is + * not open, the chain has already been removed and we don't bother + * dirtying the inode. */ - if (cluster == NULL) { - error = ENOENT; - goto done; + if (ip->meta.nlinks == 1) { + atomic_set_int(&ip->flags, HAMMER2_INODE_ISUNLINKED); + if (isopen == 0) + return 0; } - ripdata = &hammer2_cluster_rdata(cluster)->ipdata; - type = ripdata->meta.type; - if (type == HAMMER2_OBJTYPE_HARDLINK) { - hlink = 1; - type = ripdata->meta.target_type; - } - - if (type == HAMMER2_OBJTYPE_DIRECTORY && isdir == 0) { - error = ENOTDIR; - goto done; - } - if (type != HAMMER2_OBJTYPE_DIRECTORY && isdir >= 1) { - error = EISDIR; - goto done; - } + hammer2_inode_modify(ip); + --ip->meta.nlinks; + if ((int64_t)ip->meta.nlinks < 0) + ip->meta.nlinks = 0; /* safety */ /* - * Hardlink must be resolved. We can't hold the parent locked - * while we do this or we could deadlock. The physical file will - * be located at or above the current directory. - * - * We loop to reacquire the hardlink origination. - * - * NOTE: hammer2_hardlink_find() will locate the hardlink target, - * returning a modified hparent and hcluster. + * If nlinks is not zero we are done. However, this should only be + * possible with a hardlink target. If the inode is an embedded + * hardlink nlinks should have dropped to zero, warn and proceed + * with the next step. */ - if (ripdata->meta.type == HAMMER2_OBJTYPE_HARDLINK) { - if (hcluster == NULL) { - hcluster = cluster; - cluster = NULL; /* safety */ - hammer2_cluster_unlock(cparent); - hammer2_cluster_drop(cparent); - cparent = NULL; /* safety */ - ripdata = NULL; /* safety (associated w/cparent) */ - error = hammer2_cluster_hardlink_find(dip, &hparent, &hcluster); - - /* - * If we couldn't find the hardlink target then some - * parent directory containing the hardlink pointer - * probably got renamed to above the original target, - * a case not yet handled by H2. - */ - if (error) { - kprintf("H2 unlink_file: hardlink target for " - "\"%s\" not found\n", - name); - kprintf("(likely due to known directory " - "rename bug)\n"); - goto done; - } - goto again; - } + if (ip->meta.nlinks) { + if ((ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE) == 0) + return 0; + kprintf("hammer2_inode_unlink: nlinks was not 0 (%jd)\n", + (intmax_t)ip->meta.nlinks); + return 0; } /* - * If this is a directory the directory must be empty. However, if - * isdir < 0 we are doing a rename and the directory does not have - * to be empty, and if isdir > 1 we are deleting a PFS/snapshot - * and the directory does not have to be empty. + * nlinks is now zero, the inode should have already been deleted. + * If the file is open it was deleted non-permanently and must be + * moved to the hidden directory. * - * NOTE: We check the full key range here which covers both visible - * and invisible entries. Theoretically there should be no - * invisible (hardlink target) entries if there are no visible - * entries. + * When moving to the hidden directory we force the name_key to the + * inode number to avoid collisions. */ - if (type == HAMMER2_OBJTYPE_DIRECTORY && isdir == 1) { - dparent = hammer2_cluster_lookup_init(cluster, 0); - dcluster = hammer2_cluster_lookup(dparent, &key_dummy, - 0, (hammer2_key_t)-1, - HAMMER2_LOOKUP_NODATA); - if (dcluster) { - hammer2_cluster_unlock(dcluster); - hammer2_cluster_drop(dcluster); - hammer2_cluster_lookup_done(dparent); - error = ENOTEMPTY; - goto done; - } - hammer2_cluster_lookup_done(dparent); - dparent = NULL; - /* dcluster NULL */ - } - - /* - * If this was a hardlink then (cparent, cluster) is the hardlink - * pointer, which we can simply destroy outright. Discard the - * clusters and replace with the hardlink target. - */ - if (hcluster) { - hammer2_cluster_delete(cparent, cluster, - HAMMER2_DELETE_PERMANENT); - hammer2_cluster_unlock(cparent); - hammer2_cluster_drop(cparent); - hammer2_cluster_unlock(cluster); - hammer2_cluster_drop(cluster); - cparent = hparent; - cluster = hcluster; - hparent = NULL; - hcluster = NULL; - } - - /* - * This leaves us with the hardlink target or non-hardlinked file - * or directory in (cparent, cluster). - * - * Delete the target when nlinks reaches 0 with special handling - * to avoid I/O (to avoid actually updating the inode) for the 1->0 - * transition, if possible. This optimization makes rm -rf very - * fast. - * - * NOTE! In DragonFly the vnops function calls cache_unlink() after - * calling us here to clean out the namecache association, - * (which does not represent a ref for the open-test), and to - * force finalization of the vnode if/when the last ref gets - * dropped. - */ - KKASSERT(cluster != NULL); - - /* - * Instantiate ip if necessary for ip->meta data consolidation. - */ - if (ip == NULL) { - ip = hammer2_inode_get(dip->pmp, dip, cluster); - myip = 1; - } - - - /* - * Note: nlinks is negative when decrementing, positive when - * incrementing. - */ - last_link = (ip->meta.nlinks - (isrename ? 0 : 1) == 0); - - if (last_link) { - /* - * Target nlinks has reached 0, file now unlinked (but may - * still be open). - * - * On the last link when not renaming, we have to special - * case the file if it is still open to prevent the bulk - * free from freeing blocks out from under it. - */ - if (isrename == 0) - atomic_set_int(&ip->flags, HAMMER2_INODE_ISUNLINKED); - - if (isrename == 0 && isopen) { - /* - * If an unlinked file is still open we must update - * the inodes link count. - */ - /*hammer2_cluster_modify(cluster, 0);*/ - hammer2_inode_modify(ip); - if (isrename == 0) - --ip->meta.nlinks; - if ((int64_t)ip->meta.nlinks < 0) /* safety */ - ip->meta.nlinks = 0; - hammer2_inode_move_to_hidden(&cparent, &cluster, - ip->meta.inum); - /* hammer2_cluster_modsync(cluster); */ - } else { - /* - * This won't get everything if a vnode is still - * present, but the cache_unlink() call the caller - * makes will. - */ - hammer2_cluster_delete(cparent, cluster, - HAMMER2_DELETE_PERMANENT); - } - } else if (hlink == 0) { - /* - * In this situation a normal non-hardlinked file (which can - * only have nlinks == 1) still has a non-zero nlinks, the - * caller must be doing a RENAME operation, and only wishes - * to remove file in order to be able to reconnect it under - * a different name. - * - * In this situation we do a temporary deletion of the - * chain in order to allow the file to be reconnected in - * a different location. - */ - KKASSERT(isrename != 0); - hammer2_cluster_delete(cparent, cluster, 0); + if (isopen) { + hammer2_inode_lock(pmp->ihidden, HAMMER2_RESOLVE_ALWAYS); + error = hammer2_inode_connect_simple(pmp->ihidden, ip, + NULL, 0, ip->meta.inum); + hammer2_inode_unlock(pmp->ihidden, NULL); } else { - /* - * Links remain, must update the inode link count. - */ - /*hammer2_cluster_modify(cluster, 0);*/ - hammer2_inode_modify(ip); - if (isrename == 0) - --ip->meta.nlinks; - if ((int64_t)ip->meta.nlinks < 0) - ip->meta.nlinks = 0; - /* hammer2_cluster_modsync(cluster); */ - } - - if (myip) { - hammer2_inode_unlock(ip, NULL); - } - - error = 0; -done: - if (cparent) { - hammer2_cluster_unlock(cparent); - hammer2_cluster_drop(cparent); - } - if (cluster) { - hammer2_cluster_unlock(cluster); - hammer2_cluster_drop(cluster); - } - if (hparent) { - hammer2_cluster_unlock(hparent); - hammer2_cluster_drop(hparent); - } - if (hcluster) { - hammer2_cluster_unlock(hcluster); - hammer2_cluster_drop(hcluster); + error = 0; } - if (hlinkp) - *hlinkp = hlink; - return error; } @@ -1571,6 +1222,7 @@ hammer2_inode_install_hidden(hammer2_pfs_t *pmp) hammer2_trans_done(pmp); } +#if 0 /* * If an open file is unlinked H2 needs to retain the file in the topology * to ensure that its backing store is not recovered by the bulk free scan. @@ -1602,6 +1254,7 @@ hammer2_inode_move_to_hidden(hammer2_cluster_t **cparentp, hammer2_inode_unlock(pmp->ihidden, dcluster); KKASSERT(error == 0); } +#endif /* * Find the directory common to both fdip and tdip. @@ -1773,7 +1426,10 @@ hammer2_inode_fsync(hammer2_inode_t *ip, hammer2_cluster_t *cparent) } /* - * Inode create helper (threaded, backend). + * Inode create helper (threaded, backend) + * + * Used by ncreate, nmknod, nsymlink, nmkdir. + * Used by nlink and rename to create HARDLINK pointers. * * Frontend holds the parent directory ip locked exclusively. We * create the inode and feed the exclusively locked chain to the @@ -1831,10 +1487,88 @@ fail: } /* - * Inode delete helper + * Inode delete helper (backend, threaded) */ void hammer2_inode_xop_destroy(hammer2_xop_t *arg, int clindex) { /*hammer2_xop_inode_t *xop = &arg->xop_inode;*/ } + +void +hammer2_inode_xop_connect(hammer2_xop_t *arg, int clindex) +{ + hammer2_xop_connect_t *xop = &arg->xop_connect; + hammer2_inode_data_t *wipdata; + hammer2_chain_t *parent; + hammer2_chain_t *chain; + hammer2_pfs_t *pmp; + hammer2_key_t key_dummy; + int cache_index = -1; + int error; + + /* + * Get directory, then issue a lookup to prime the parent chain + * for the create. The lookup is expected to fail. + */ + pmp = xop->head.ip->pmp; + parent = hammer2_inode_chain(xop->head.ip, clindex, + HAMMER2_RESOLVE_ALWAYS); + if (parent == NULL) { + chain = NULL; + error = EIO; + goto fail; + } + chain = hammer2_chain_lookup(&parent, &key_dummy, + xop->lhc, xop->lhc, + &cache_index, 0); + if (chain) { + hammer2_chain_unlock(chain); + hammer2_chain_drop(chain); + chain = NULL; + error = EEXIST; + goto fail; + } + + /* + * Adjust the filename in the inode, set the name key. + * + * NOTE: Frontend must also adjust ip2->meta on success, we can't + * do it here. + */ + chain = hammer2_inode_chain(xop->head.ip2, clindex, + HAMMER2_RESOLVE_ALWAYS); + hammer2_chain_modify(chain, 0); + wipdata = &chain->data->ipdata; + + hammer2_inode_modify(xop->head.ip2); + if (xop->head.name) { + bzero(wipdata->filename, sizeof(wipdata->filename)); + bcopy(xop->head.name, wipdata->filename, xop->head.name_len); + wipdata->meta.name_len = xop->head.name_len; + } + wipdata->meta.name_key = xop->lhc; + + /* + * Reconnect the chain to the new parent directory + */ + error = hammer2_chain_create(&parent, &chain, pmp, + xop->lhc, 0, + HAMMER2_BREF_TYPE_INODE, + HAMMER2_INODE_BYTES, + 0); + + /* + * Feed result back. + */ +fail: + hammer2_xop_feed(&xop->head, NULL, clindex, error); + if (parent) { + hammer2_chain_unlock(parent); + hammer2_chain_drop(parent); + } + if (chain) { + hammer2_chain_unlock(chain); + hammer2_chain_drop(chain); + } +} diff --git a/sys/vfs/hammer2/hammer2_ioctl.c b/sys/vfs/hammer2/hammer2_ioctl.c index baef345a67..c50b1baaf8 100644 --- a/sys/vfs/hammer2/hammer2_ioctl.c +++ b/sys/vfs/hammer2/hammer2_ioctl.c @@ -554,6 +554,7 @@ hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data) hammer2_trans_init(hmp->spmp, 0); nip = hammer2_inode_create(hmp->spmp->iroot, NULL, NULL, pfs->name, strlen(pfs->name), + 1, HAMMER2_OBJTYPE_DIRECTORY, 0, HAMMER2_INSERT_PFSROOT, &error); if (error == 0) { hammer2_inode_modify(nip); @@ -607,16 +608,30 @@ hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data) static int hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data) { - hammer2_dev_t *hmp; hammer2_ioc_pfs_t *pfs = data; + hammer2_pfs_t *spmp; + hammer2_xop_unlink_t *xop; + hammer2_inode_t *dip; int error; - hmp = ip->pmp->iroot->cluster.focus->hmp; /* XXX */ - hammer2_trans_init(hmp->spmp, 0); - error = hammer2_unlink_file(hmp->spmp->iroot, NULL, - pfs->name, strlen(pfs->name), - 2, NULL, 0, 0); - hammer2_trans_done(hmp->spmp); + pfs->name[sizeof(pfs->name) - 1] = 0; /* ensure termination */ + + /* XXX */ + spmp = ip->pmp->iroot->cluster.focus->hmp->spmp; + dip = spmp->iroot; + hammer2_trans_init(spmp, 0); + hammer2_inode_lock(dip, HAMMER2_RESOLVE_ALWAYS); + + xop = &hammer2_xop_alloc(dip)->xop_unlink; + hammer2_xop_setname(&xop->head, pfs->name, strlen(pfs->name)); + xop->isdir = 2; + xop->dopermanent = 1; + hammer2_xop_start(&xop->head, hammer2_xop_unlink); + + error = hammer2_xop_collect(&xop->head, 0); + + hammer2_inode_unlock(dip, NULL); + hammer2_trans_done(spmp); return (error); } diff --git a/sys/vfs/hammer2/hammer2_thread.c b/sys/vfs/hammer2/hammer2_thread.c index 95886d0b0a..4f0f7508c3 100644 --- a/sys/vfs/hammer2/hammer2_thread.c +++ b/sys/vfs/hammer2/hammer2_thread.c @@ -985,6 +985,37 @@ hammer2_xop_alloc(hammer2_inode_t *ip) return xop; } +void +hammer2_xop_setname(hammer2_xop_head_t *xop, const char *name, size_t name_len) +{ + xop->name = kmalloc(name_len + 1, M_HAMMER2, M_WAITOK | M_ZERO); + xop->name_len = name_len; + bcopy(name, xop->name, name_len); +} + +void +hammer2_xop_setname2(hammer2_xop_head_t *xop, const char *name, size_t name_len) +{ + xop->name2 = kmalloc(name_len + 1, M_HAMMER2, M_WAITOK | M_ZERO); + xop->name2_len = name_len; + bcopy(name, xop->name2, name_len); +} + + +void +hammer2_xop_setip2(hammer2_xop_head_t *xop, hammer2_inode_t *ip2) +{ + xop->ip2 = ip2; + hammer2_inode_ref(ip2); +} + +void +hammer2_xop_setip3(hammer2_xop_head_t *xop, hammer2_inode_t *ip3) +{ + xop->ip3 = ip3; + hammer2_inode_ref(ip3); +} + void hammer2_xop_reinit(hammer2_xop_head_t *xop) { @@ -1136,11 +1167,24 @@ hammer2_xop_retire(hammer2_xop_head_t *xop, uint32_t mask) hammer2_inode_drop(xop->ip); xop->ip = NULL; } + if (xop->ip2) { + hammer2_inode_drop(xop->ip2); + xop->ip2 = NULL; + } + if (xop->ip3) { + hammer2_inode_drop(xop->ip3); + xop->ip3 = NULL; + } if (xop->name) { kfree(xop->name, M_HAMMER2); xop->name = NULL; xop->name_len = 0; } + if (xop->name2) { + kfree(xop->name2, M_HAMMER2); + xop->name2 = NULL; + xop->name2_len = 0; + } objcache_put(cache_xops, xop); } diff --git a/sys/vfs/hammer2/hammer2_vnops.c b/sys/vfs/hammer2/hammer2_vnops.c index 5e8e7046b2..eacd146134 100644 --- a/sys/vfs/hammer2/hammer2_vnops.c +++ b/sys/vfs/hammer2/hammer2_vnops.c @@ -1074,10 +1074,7 @@ hammer2_vop_nresolve(struct vop_nresolve_args *ap) xop = &hammer2_xop_alloc(dip)->xop_nresolve; ncp = ap->a_nch->ncp; - xop->head.name = kmalloc(ncp->nc_nlen + 1, M_HAMMER2, - M_WAITOK | M_ZERO); - xop->head.name_len = ncp->nc_nlen; - bcopy(ncp->nc_name, xop->head.name, ncp->nc_nlen); + hammer2_xop_setname(&xop->head, ncp->nc_name, ncp->nc_nlen); /* * Note: In DragonFly the kernel handles '.' and '..'. @@ -1189,7 +1186,9 @@ hammer2_vop_nmkdir(struct vop_nmkdir_args *ap) hammer2_pfs_memory_wait(dip->pmp); hammer2_trans_init(dip->pmp, 0); nip = hammer2_inode_create(dip, ap->a_vap, ap->a_cred, - name, name_len, 0, &error); + name, name_len, + hammer2_trans_newinum(dip->pmp), 0, 0, + 0, &error); if (error) { KKASSERT(nip == NULL); *ap->a_vpp = NULL; @@ -1253,14 +1252,11 @@ static int hammer2_vop_nlink(struct vop_nlink_args *ap) { + hammer2_xop_nlink_t *xop1; hammer2_inode_t *fdip; /* target directory to create link in */ hammer2_inode_t *tdip; /* target directory to create link in */ hammer2_inode_t *cdip; /* common parent directory */ hammer2_inode_t *ip; /* inode we are hardlinking to */ - hammer2_cluster_t *cluster; - hammer2_cluster_t *fdcluster; - hammer2_cluster_t *tdcluster; - hammer2_cluster_t *cdcluster; struct namecache *ncp; const uint8_t *name; size_t name_len; @@ -1301,36 +1297,56 @@ hammer2_vop_nlink(struct vop_nlink_args *ap) hammer2_inode_lock(cdip, HAMMER2_RESOLVE_ALWAYS); hammer2_inode_lock(fdip, HAMMER2_RESOLVE_ALWAYS); hammer2_inode_lock(tdip, HAMMER2_RESOLVE_ALWAYS); - cdcluster = hammer2_inode_cluster(cdip, HAMMER2_RESOLVE_ALWAYS); - fdcluster = hammer2_inode_cluster(fdip, HAMMER2_RESOLVE_ALWAYS); - tdcluster = hammer2_inode_cluster(tdip, HAMMER2_RESOLVE_ALWAYS); - hammer2_inode_lock(ip, HAMMER2_RESOLVE_ALWAYS); - cluster = hammer2_inode_cluster(ip, HAMMER2_RESOLVE_ALWAYS); + error = 0; - error = hammer2_cluster_hardlink_consolidate(ip, &cluster, - cdip, cdcluster, 1); - if (error) - goto done; + /* + * If ip is not a hardlink target we must convert it to a hardlink. + * If fdip != cdip we must shift the inode to cdip. + */ + if (fdip != cdip || (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE)) { + xop1 = &hammer2_xop_alloc(fdip)->xop_nlink; + hammer2_xop_setip2(&xop1->head, ip); + hammer2_xop_setip3(&xop1->head, cdip); + + hammer2_xop_start(&xop1->head, hammer2_xop_nlink); + error = hammer2_xop_collect(&xop1->head, 0); + hammer2_xop_retire(&xop1->head, HAMMER2_XOPMASK_VOP); + if (error == ENOENT) + error = 0; + } /* - * Create a directory entry connected to the specified cluster. - * - * WARNING! chain can get moved by the connect (indirectly due to - * potential indirect block creation). + * Must synchronize original inode whos chains are now a hardlink + * target. We must match what the backend XOP did to the + * chains. + */ + if (error == 0 && (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE)) { + hammer2_inode_modify(ip); + ip->meta.name_key = ip->meta.inum; + ip->meta.name_len = 18; /* "0x%016jx" */ + } + + /* + * Create the hardlink target and bump nlinks. */ - error = hammer2_inode_connect(ip, &cluster, 1, - tdip, tdcluster, - name, name_len, 0); + if (error == 0) { + hammer2_inode_create(tdip, NULL, NULL, + name, name_len, + ip->meta.inum, + HAMMER2_OBJTYPE_HARDLINK, ip->meta.type, + 0, &error); + hammer2_inode_modify(ip); + ++ip->meta.nlinks; + } if (error == 0) { cache_setunresolved(ap->a_nch); cache_setvp(ap->a_nch, ap->a_vp); } -done: - hammer2_inode_unlock(ip, cluster); - hammer2_inode_unlock(tdip, tdcluster); - hammer2_inode_unlock(fdip, fdcluster); - hammer2_inode_unlock(cdip, cdcluster); + hammer2_inode_unlock(ip, NULL); + hammer2_inode_unlock(tdip, NULL); + hammer2_inode_unlock(fdip, NULL); + hammer2_inode_unlock(cdip, NULL); hammer2_inode_drop(cdip); hammer2_trans_done(ip->pmp); @@ -1369,7 +1385,9 @@ hammer2_vop_ncreate(struct vop_ncreate_args *ap) hammer2_trans_init(dip->pmp, 0); nip = hammer2_inode_create(dip, ap->a_vap, ap->a_cred, - name, name_len, 0, &error); + name, name_len, + hammer2_trans_newinum(dip->pmp), 0, 0, + 0, &error); if (error) { KKASSERT(nip == NULL); *ap->a_vpp = NULL; @@ -1415,7 +1433,9 @@ hammer2_vop_nmknod(struct vop_nmknod_args *ap) hammer2_trans_init(dip->pmp, 0); nip = hammer2_inode_create(dip, ap->a_vap, ap->a_cred, - name, name_len, 0, &error); + name, name_len, + hammer2_trans_newinum(dip->pmp), 0, 0, + 0, &error); if (error) { KKASSERT(nip == NULL); *ap->a_vpp = NULL; @@ -1460,7 +1480,9 @@ hammer2_vop_nsymlink(struct vop_nsymlink_args *ap) ap->a_vap->va_type = VLNK; /* enforce type */ nip = hammer2_inode_create(dip, ap->a_vap, ap->a_cred, - name, name_len, 0, &error); + name, name_len, + hammer2_trans_newinum(dip->pmp), 0, 0, + 0, &error); if (error) { KKASSERT(nip == NULL); *ap->a_vpp = NULL; @@ -1530,11 +1552,12 @@ static int hammer2_vop_nremove(struct vop_nremove_args *ap) { + hammer2_xop_unlink_t *xop; hammer2_inode_t *dip; + hammer2_inode_t *ip; struct namecache *ncp; - const uint8_t *name; - size_t name_len; int error; + int isopen; LOCKSTART; dip = VTOI(ap->a_dvp); @@ -1544,14 +1567,42 @@ hammer2_vop_nremove(struct vop_nremove_args *ap) } ncp = ap->a_nch->ncp; - name = ncp->nc_name; - name_len = ncp->nc_nlen; hammer2_pfs_memory_wait(dip->pmp); hammer2_trans_init(dip->pmp, 0); - error = hammer2_unlink_file(dip, NULL, name, name_len, - 0, NULL, - cache_isopen(ap->a_nch), 0); + hammer2_inode_lock(dip, HAMMER2_RESOLVE_ALWAYS); + + /* + * The unlink XOP unlinks the path from the directory and + * locates and returns the cluster associated with the real inode. + * We have to handle nlinks here on the frontend. + */ + xop = &hammer2_xop_alloc(dip)->xop_unlink; + hammer2_xop_setname(&xop->head, ncp->nc_name, ncp->nc_nlen); + isopen = cache_isopen(ap->a_nch); + xop->isdir = 0; + xop->dopermanent = isopen ? 0 : HAMMER2_DELETE_PERMANENT; + hammer2_xop_start(&xop->head, hammer2_xop_unlink); + + /* + * Collect the real inode and adjust nlinks, destroy the real + * inode if nlinks transitions to 0 and it was the real inode + * (else it has already been removed). + */ + error = hammer2_xop_collect(&xop->head, 0); + hammer2_inode_unlock(dip, NULL); + + if (error == 0) { + ip = hammer2_inode_get(dip->pmp, dip, &xop->head.cluster); + hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); + if (ip) { + hammer2_inode_unlink_finisher(ip, isopen); + hammer2_inode_unlock(ip, NULL); + } + } else { + hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); + } + hammer2_run_unlinkq(dip->pmp); hammer2_trans_done(dip->pmp); if (error == 0) @@ -1567,10 +1618,11 @@ static int hammer2_vop_nrmdir(struct vop_nrmdir_args *ap) { + hammer2_xop_unlink_t *xop; hammer2_inode_t *dip; + hammer2_inode_t *ip; struct namecache *ncp; - const uint8_t *name; - size_t name_len; + int isopen; int error; LOCKSTART; @@ -1580,16 +1632,38 @@ hammer2_vop_nrmdir(struct vop_nrmdir_args *ap) return(EROFS); } - ncp = ap->a_nch->ncp; - name = ncp->nc_name; - name_len = ncp->nc_nlen; - hammer2_pfs_memory_wait(dip->pmp); hammer2_trans_init(dip->pmp, 0); + hammer2_inode_lock(dip, HAMMER2_RESOLVE_ALWAYS); + + xop = &hammer2_xop_alloc(dip)->xop_unlink; + + ncp = ap->a_nch->ncp; + hammer2_xop_setname(&xop->head, ncp->nc_name, ncp->nc_nlen); + isopen = cache_isopen(ap->a_nch); + xop->isdir = 1; + xop->dopermanent = isopen ? 0 : HAMMER2_DELETE_PERMANENT; + hammer2_xop_start(&xop->head, hammer2_xop_unlink); + + /* + * Collect the real inode and adjust nlinks, destroy the real + * inode if nlinks transitions to 0 and it was the real inode + * (else it has already been removed). + */ + error = hammer2_xop_collect(&xop->head, 0); + hammer2_inode_unlock(dip, NULL); + + if (error == 0) { + ip = hammer2_inode_get(dip->pmp, dip, &xop->head.cluster); + hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); + if (ip) { + hammer2_inode_unlink_finisher(ip, isopen); + hammer2_inode_unlock(ip, NULL); + } + } else { + hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); + } hammer2_run_unlinkq(dip->pmp); - error = hammer2_unlink_file(dip, NULL, name, name_len, - 1, NULL, - cache_isopen(ap->a_nch), 0); hammer2_trans_done(dip->pmp); if (error == 0) cache_unlink(ap->a_nch); @@ -1610,17 +1684,13 @@ hammer2_vop_nrename(struct vop_nrename_args *ap) hammer2_inode_t *fdip; hammer2_inode_t *tdip; hammer2_inode_t *ip; - hammer2_cluster_t *cluster; - hammer2_cluster_t *fdcluster; - hammer2_cluster_t *tdcluster; - hammer2_cluster_t *cdcluster; const uint8_t *fname; size_t fname_len; const uint8_t *tname; size_t tname_len; int error; int tnch_error; - int hlink; + hammer2_key_t tlhc; if (ap->a_fdvp->v_mount != ap->a_tdvp->v_mount) return(EXDEV); @@ -1650,126 +1720,171 @@ hammer2_vop_nrename(struct vop_nrename_args *ap) * ip represents the actual file and not the hardlink marker. */ ip = VTOI(fncp->nc_vp); - cluster = NULL; - /* * The common parent directory must be locked first to avoid deadlocks. * Also note that fdip and/or tdip might match cdip. - * - * WARNING! fdip may not match ip->pip. That is, if the source file - * is already a hardlink then what we are renaming is the - * hardlink pointer, not the hardlink itself. The hardlink - * directory (ip->pip) will already be at a common parent - * of fdrip. - * - * Be sure to use ip->pip when finding the common parent - * against tdip or we might accidently move the hardlink - * target into a subdirectory that makes it inaccessible to - * other pointers. */ cdip = hammer2_inode_common_parent(ip->pip, tdip); hammer2_inode_lock(cdip, HAMMER2_RESOLVE_ALWAYS); hammer2_inode_lock(fdip, HAMMER2_RESOLVE_ALWAYS); hammer2_inode_lock(tdip, HAMMER2_RESOLVE_ALWAYS); - cdcluster = hammer2_inode_cluster(cdip, HAMMER2_RESOLVE_ALWAYS); - fdcluster = hammer2_inode_cluster(fdip, HAMMER2_RESOLVE_ALWAYS); - tdcluster = hammer2_inode_cluster(tdip, HAMMER2_RESOLVE_ALWAYS); + hammer2_inode_ref(ip); /* extra ref */ + error = 0; /* - * Keep a tight grip on the inode so the temporary unlinking from - * the source location prior to linking to the target location - * does not cause the cluster to be destroyed. - * - * NOTE: To avoid deadlocks we cannot lock (ip) while we are - * unlinking elements from their directories. Locking - * the nlinks field does not lock the whole inode. + * If ip is a hardlink target and fdip != cdip we must shift the + * inode to cdip. */ - hammer2_inode_ref(ip); + if (fdip != cdip && + (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE) == 0) { + hammer2_xop_nlink_t *xop1; + + xop1 = &hammer2_xop_alloc(fdip)->xop_nlink; + hammer2_xop_setip2(&xop1->head, ip); + hammer2_xop_setip3(&xop1->head, cdip); + + hammer2_xop_start(&xop1->head, hammer2_xop_nlink); + error = hammer2_xop_collect(&xop1->head, 0); + hammer2_xop_retire(&xop1->head, HAMMER2_XOPMASK_VOP); + } /* - * Remove target if it exists. + * Delete the target namespace. */ - error = hammer2_unlink_file(tdip, NULL, tname, tname_len, - -1, NULL, - cache_isopen(ap->a_tnch), 0); - tnch_error = error; - if (error && error != ENOENT) - goto done2; + { + hammer2_xop_unlink_t *xop2; + hammer2_inode_t *tip; + int isopen; + + /* + * The unlink XOP unlinks the path from the directory and + * locates and returns the cluster associated with the real + * inode. We have to handle nlinks here on the frontend. + */ + xop2 = &hammer2_xop_alloc(tdip)->xop_unlink; + hammer2_xop_setname(&xop2->head, tname, tname_len); + isopen = cache_isopen(ap->a_tnch); + xop2->isdir = -1; + xop2->dopermanent = isopen ? 0 : HAMMER2_DELETE_PERMANENT; + hammer2_xop_start(&xop2->head, hammer2_xop_unlink); + + /* + * Collect the real inode and adjust nlinks, destroy the real + * inode if nlinks transitions to 0 and it was the real inode + * (else it has already been removed). + */ + tnch_error = hammer2_xop_collect(&xop2->head, 0); + /* hammer2_inode_unlock(tdip, NULL); */ + + if (tnch_error == 0) { + tip = hammer2_inode_get(tdip->pmp, NULL, + &xop2->head.cluster); + hammer2_xop_retire(&xop2->head, HAMMER2_XOPMASK_VOP); + if (tip) { + hammer2_inode_unlink_finisher(tip, isopen); + hammer2_inode_unlock(tip, NULL); + } + } else { + hammer2_xop_retire(&xop2->head, HAMMER2_XOPMASK_VOP); + } + /* hammer2_inode_lock(tdip, HAMMER2_RESOLVE_ALWAYS); */ + + if (tnch_error && tnch_error != ENOENT) { + error = tnch_error; + goto done2; + } + } /* - * When renaming a hardlinked file we may have to re-consolidate - * the location of the hardlink target. - * - * If ip represents a regular file the consolidation code essentially - * does nothing other than return the same locked cluster that was - * passed in. + * Resolve the collision space for (tdip, tname, tname_len) * - * The returned cluster will be locked. - * - * WARNING! We do not currently have a local copy of ipdata but - * we do use one later remember that it must be reloaded - * on any modification to the inode, including connects. + * tdip must be held exclusively locked to prevent races. */ - hammer2_inode_lock(ip, HAMMER2_RESOLVE_ALWAYS); - cluster = hammer2_inode_cluster(ip, HAMMER2_RESOLVE_ALWAYS); - error = hammer2_cluster_hardlink_consolidate(ip, &cluster, - cdip, cdcluster, 0); - if (error) - goto done1; + { + hammer2_xop_scanlhc_t *sxop; + hammer2_tid_t lhcbase; + + tlhc = hammer2_dirhash(tname, tname_len); + lhcbase = tlhc; + sxop = &hammer2_xop_alloc(tdip)->xop_scanlhc; + sxop->lhc = tlhc; + hammer2_xop_start(&sxop->head, hammer2_inode_xop_scanlhc); + while ((error = hammer2_xop_collect(&sxop->head, 0)) == 0) { + if (tlhc != sxop->head.cluster.focus->bref.key) + break; + ++tlhc; + } + hammer2_xop_retire(&sxop->head, HAMMER2_XOPMASK_VOP); + + if (error) { + if (error != ENOENT) + goto done2; + ++tlhc; + error = 0; + } + if ((lhcbase ^ tlhc) & ~HAMMER2_DIRHASH_LOMASK) { + error = ENOSPC; + goto done2; + } + } /* - * Disconnect (fdip, fname) from the source directory. This will - * disconnect (ip) if it represents a direct file. If (ip) represents - * a hardlink the HARDLINK pointer object will be removed but the - * hardlink will stay intact. + * Everything is setup, do the rename. * - * Always pass nch as NULL because we intend to reconnect the inode, - * so we don't want hammer2_unlink_file() to rename it to the hidden - * open-but-unlinked directory. + * We have to synchronize ip->meta to the underlying operation. * - * The target cluster may be marked DELETED but will not be destroyed - * since we retain our hold on ip and cluster. - * - * NOTE: We pass nlinks as 0 (not -1) in order to retain the file's - * link count. + * NOTE: To avoid deadlocks we cannot lock (ip) while we are + * unlinking elements from their directories. Locking + * the nlinks field does not lock the whole inode. */ - error = hammer2_unlink_file(fdip, ip, fname, fname_len, - -1, &hlink, 0, 1); - KKASSERT(error != EAGAIN); - if (error) - goto done1; + hammer2_inode_lock(ip, HAMMER2_RESOLVE_ALWAYS); + if (error == 0) { + hammer2_xop_nrename_t *xop4; + + xop4 = &hammer2_xop_alloc(fdip)->xop_nrename; + xop4->lhc = tlhc; + xop4->ip_key = ip->meta.name_key; + hammer2_xop_setip2(&xop4->head, ip); + hammer2_xop_setip3(&xop4->head, tdip); + hammer2_xop_setname(&xop4->head, fname, fname_len); + hammer2_xop_setname2(&xop4->head, tname, tname_len); + hammer2_xop_start(&xop4->head, hammer2_xop_nrename); + + error = hammer2_xop_collect(&xop4->head, 0); + hammer2_xop_retire(&xop4->head, HAMMER2_XOPMASK_VOP); + + if (error == ENOENT) + error = 0; + if (error == 0 && + (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE)) { + hammer2_inode_modify(ip); + ip->meta.name_len = tname_len; + ip->meta.name_key = tlhc; + + } + } /* - * Reconnect ip to target directory using cluster. Chains cannot - * actually be moved, so this will duplicate the cluster in the new - * spot and assign it to the ip, replacing the old cluster. - * - * WARNING: Because recursive locks are allowed and we unlinked the - * file that we have a cluster-in-hand for just above, the - * cluster might have been delete-duplicated. We must - * refactor the cluster. - * - * WARNING: Chain locks can lock buffer cache buffers, to avoid - * deadlocks we want to unlock before issuing a cache_*() - * op (that might have to lock a vnode). - * - * NOTE: Pass nlinks as 0 because we retained the link count from - * the unlink, so we do not have to modify it. + * Fixup ip->pip if we were renaming the actual file and not a + * hardlink pointer. */ - error = hammer2_inode_connect(ip, &cluster, hlink, - tdip, tdcluster, - tname, tname_len, 0); - if (error == 0) { - KKASSERT(cluster != NULL); - hammer2_inode_repoint(ip, (hlink ? ip->pip : tdip), cluster); + if (error == 0 && (ip->meta.name_key & HAMMER2_DIRHASH_VISIBLE)) { + hammer2_inode_t *opip; + + if (ip->pip != tdip) { + hammer2_inode_ref(tdip); + opip = ip->pip; + ip->pip = tdip; + if (opip) + hammer2_inode_drop(opip); + } } -done1: - hammer2_inode_unlock(ip, cluster); + hammer2_inode_unlock(ip, NULL); done2: - hammer2_inode_unlock(tdip, tdcluster); - hammer2_inode_unlock(fdip, fdcluster); - hammer2_inode_unlock(cdip, cdcluster); + hammer2_inode_unlock(tdip, NULL); + hammer2_inode_unlock(fdip, NULL); + hammer2_inode_unlock(cdip, NULL); hammer2_inode_drop(ip); hammer2_inode_drop(cdip); hammer2_run_unlinkq(fdip->pmp); diff --git a/sys/vfs/hammer2/hammer2_xops.c b/sys/vfs/hammer2/hammer2_xops.c index 9077fc9983..b28b808ee2 100644 --- a/sys/vfs/hammer2/hammer2_xops.c +++ b/sys/vfs/hammer2/hammer2_xops.c @@ -60,6 +60,9 @@ #include "hammer2.h" +/* + * Backend for hammer2_vop_readdir() + */ void hammer2_xop_readdir(hammer2_xop_t *arg, int clindex) { @@ -117,6 +120,9 @@ done: hammer2_xop_feed(&xop->head, NULL, clindex, error); } +/* + * Backend for hammer2_vop_nresolve() + */ void hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex) { @@ -173,9 +179,10 @@ hammer2_xop_nresolve(hammer2_xop_t *arg, int clindex) error = 0; if (chain) { if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { - error = hammer2_chain_hardlink_find(xop->head.ip, - &parent, - &chain); + error = hammer2_chain_hardlink_find( + xop->head.ip, + &parent, &chain, + HAMMER2_RESOLVE_SHARED); } } done: @@ -188,6 +195,441 @@ done: } } +/* + * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and helper + * for hammer2_vop_nrename(). + * + * This function does locates and removes the directory entry. If the + * entry is a hardlink pointer, this function will also remove the + * hardlink target if the target's nlinks is 1. + * + * The frontend is responsible for moving open inodes to the hidden directory + * and for decrementing nlinks. + */ +void +hammer2_xop_unlink(hammer2_xop_t *arg, int clindex) +{ + hammer2_xop_unlink_t *xop = &arg->xop_unlink; + hammer2_chain_t *parent; + hammer2_chain_t *chain; + const hammer2_inode_data_t *ripdata; + const char *name; + size_t name_len; + uint8_t type; + hammer2_key_t key_next; + hammer2_key_t lhc; + int cache_index = -1; /* XXX */ + int error; + + /* + * Requires exclusive lock + */ + parent = hammer2_inode_chain(xop->head.ip, clindex, + HAMMER2_RESOLVE_ALWAYS); + if (parent == NULL) { + kprintf("xop_nresolve: NULL parent\n"); + chain = NULL; + error = EIO; + goto done; + } + name = xop->head.name; + name_len = xop->head.name_len; + + /* + * Lookup the directory entry + */ + lhc = hammer2_dirhash(name, name_len); + chain = hammer2_chain_lookup(&parent, &key_next, + lhc, lhc + HAMMER2_DIRHASH_LOMASK, + &cache_index, + HAMMER2_LOOKUP_ALWAYS); + while (chain) { + ripdata = &chain->data->ipdata; + if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && + ripdata->meta.name_len == name_len && + bcmp(ripdata->filename, name, name_len) == 0) { + break; + } + chain = hammer2_chain_next(&parent, chain, &key_next, + key_next, + lhc + HAMMER2_DIRHASH_LOMASK, + &cache_index, + HAMMER2_LOOKUP_ALWAYS); + } + + /* + * If the directory entry is a HARDLINK pointer then obtain the + * underlying file type for the directory typing tests and delete + * the HARDLINK pointer chain permanently. The frontend is left + * responsible for handling nlinks on and deleting the actual inode. + * + * If the directory entry is the actual inode then use its type + * for the directory typing tests and delete the chain, permanency + * depends on whether the inode is open or not. + * + * Check directory typing and delete the entry. Note that + * nlinks adjustments are made on the real inode by the frontend, + * not here. + */ + error = 0; + if (chain) { + int dopermanent = xop->dopermanent; + + type = chain->data->ipdata.meta.type; + if (type == HAMMER2_OBJTYPE_HARDLINK) { + type = chain->data->ipdata.meta.target_type; + dopermanent |= HAMMER2_DELETE_PERMANENT; + } + if (type == HAMMER2_OBJTYPE_DIRECTORY && + xop->isdir == 0) { + error = ENOTDIR; + } else + if (type != HAMMER2_OBJTYPE_DIRECTORY && + xop->isdir >= 1) { + error = EISDIR; + } else { + hammer2_chain_delete(parent, chain, xop->dopermanent); + } + } + + /* + * If the entry is a hardlink pointer, resolve it. If this is the + * last link, delete it. We aren't the frontend so we can't adjust + * nlinks. + */ + if (chain) { + if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { + error = hammer2_chain_hardlink_find( + xop->head.ip, + &parent, &chain, + 0); + if (chain && + (int64_t)chain->data->ipdata.meta.nlinks <= 1) { + hammer2_chain_delete(parent, chain, + xop->dopermanent); + } + } + } + + /* + * Chains passed to feed are expected to be locked shared. + */ + if (chain) { + hammer2_chain_unlock(chain); + hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS | + HAMMER2_RESOLVE_SHARED); + } + + /* + * We always return the hardlink target (the real inode) for + * further action. + */ +done: + hammer2_xop_feed(&xop->head, chain, clindex, error); + if (chain) + hammer2_chain_drop(chain); + if (parent) { + hammer2_chain_unlock(parent); + hammer2_chain_drop(parent); + } +} + +/* + * Backend for hammer2_vop_nlink() and hammer2_vop_nrename() + * + * Convert the target {dip,ip} to a hardlink target and replace + * the original namespace with a hardlink pointer. + */ +void +hammer2_xop_nlink(hammer2_xop_t *arg, int clindex) +{ + hammer2_xop_nlink_t *xop = &arg->xop_nlink; + hammer2_pfs_t *pmp; + hammer2_inode_data_t *wipdata; + hammer2_chain_t *parent; + hammer2_chain_t *chain; + hammer2_chain_t *tmp; + hammer2_inode_t *ip; + hammer2_key_t key_dummy; + int cache_index = -1; + int error; + + /* + * We need the precise parent chain to issue the deletion. + */ + ip = xop->head.ip2; + pmp = ip->pmp; + parent = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS); + if (parent) + hammer2_chain_getparent(&parent, HAMMER2_RESOLVE_ALWAYS); + if (parent == NULL) { + chain = NULL; + error = EIO; + goto done; + } + chain = hammer2_inode_chain(ip, clindex, HAMMER2_RESOLVE_ALWAYS); + if (chain == NULL) { + error = EIO; + goto done; + } + hammer2_chain_delete(parent, chain, 0); + + /* + * Replace the namespace with a hardlink pointer if the chain being + * moved is not already a hardlink target. + */ + if (chain->data->ipdata.meta.name_key & HAMMER2_DIRHASH_VISIBLE) { + tmp = NULL; + error = hammer2_chain_create(&parent, &tmp, pmp, + chain->bref.key, 0, + HAMMER2_BREF_TYPE_INODE, + HAMMER2_INODE_BYTES, + 0); + if (error) + goto done; + hammer2_chain_modify(tmp, 0); + wipdata = &tmp->data->ipdata; + bzero(wipdata, sizeof(*wipdata)); + wipdata->meta.name_key = chain->data->ipdata.meta.name_key; + wipdata->meta.name_len = chain->data->ipdata.meta.name_len; + bcopy(chain->data->ipdata.filename, wipdata->filename, + chain->data->ipdata.meta.name_len); + wipdata->meta.target_type = chain->data->ipdata.meta.type; + wipdata->meta.type = HAMMER2_OBJTYPE_HARDLINK; + wipdata->meta.inum = ip->meta.inum; + wipdata->meta.version = HAMMER2_INODE_VERSION_ONE; + wipdata->meta.nlinks = 1; + wipdata->meta.op_flags = HAMMER2_OPFLAG_DIRECTDATA; + + hammer2_chain_unlock(tmp); + hammer2_chain_drop(tmp); + } + + hammer2_chain_unlock(parent); + hammer2_chain_drop(parent); + + /* + * Ok, back to the deleted chain. We must reconnect this chain + * as a hardlink target to cdir (ip3). + * + * WARNING! Frontend assumes filename length is 18 bytes. + */ + hammer2_chain_modify(chain, 0); + wipdata = &chain->data->ipdata; + ksnprintf(wipdata->filename, sizeof(wipdata->filename), + "0x%016jx", (intmax_t)ip->meta.inum); + wipdata->meta.name_len = strlen(wipdata->filename); + wipdata->meta.name_key = ip->meta.inum; + + /* + * We must seek parent properly for the create. + */ + parent = hammer2_inode_chain(xop->head.ip3, clindex, + HAMMER2_RESOLVE_ALWAYS); + if (parent == NULL) { + error = EIO; + goto done; + } + tmp = hammer2_chain_lookup(&parent, &key_dummy, + ip->meta.inum, ip->meta.inum, + &cache_index, 0); + if (tmp) { + hammer2_chain_unlock(tmp); + hammer2_chain_drop(tmp); + error = EEXIST; + goto done; + } + error = hammer2_chain_create(&parent, &chain, pmp, + wipdata->meta.name_key, 0, + HAMMER2_BREF_TYPE_INODE, + HAMMER2_INODE_BYTES, + 0); + /* + * To avoid having to scan the collision space we can simply + * reuse the inode's original name_key. But ip->meta.name_key + * may have already been updated by the front-end, so use xop->lhc. + * + * (frontend is responsible for fixing up ip->pip). + */ +done: + hammer2_xop_feed(&xop->head, NULL, clindex, error); + if (parent) { + hammer2_chain_unlock(parent); + hammer2_chain_drop(parent); + } + if (chain) { + hammer2_chain_unlock(chain); + hammer2_chain_drop(chain); + } +} + +/* + * Backend for hammer2_vop_nrename() + * + * This handles the final step of renaming, either renaming the + * actual inode or renaming the hardlink pointer. + */ +void +hammer2_xop_nrename(hammer2_xop_t *arg, int clindex) +{ + hammer2_xop_nrename_t *xop = &arg->xop_nrename; + hammer2_pfs_t *pmp; + hammer2_chain_t *parent; + hammer2_chain_t *chain; + hammer2_chain_t *tmp; + hammer2_inode_t *ip; + hammer2_key_t key_dummy; + int cache_index = -1; + int error; + + /* + * We need the precise parent chain to issue the deletion. + * + * If this is not a hardlink target we can act on the inode, + * otherwise we have to locate the hardlink pointer. + */ + ip = xop->head.ip2; + pmp = ip->pmp; + chain = NULL; + + if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) { + /* + * Find ip's direct parent chain. + */ + parent = hammer2_inode_chain(ip, clindex, + HAMMER2_RESOLVE_ALWAYS); + if (parent) + hammer2_chain_getparent(&parent, + HAMMER2_RESOLVE_ALWAYS); + if (parent == NULL) { + error = EIO; + goto done; + } + chain = hammer2_inode_chain(ip, clindex, + HAMMER2_RESOLVE_ALWAYS); + if (chain == NULL) { + error = EIO; + goto done; + } + } else { + /* + * head.ip is fdip, do a namespace search. + */ + const hammer2_inode_data_t *ripdata; + hammer2_key_t lhc; + hammer2_key_t key_next; + const char *name; + size_t name_len; + + parent = hammer2_inode_chain(xop->head.ip, clindex, + HAMMER2_RESOLVE_ALWAYS | + HAMMER2_RESOLVE_SHARED); + if (parent == NULL) { + kprintf("xop_nrename: NULL parent\n"); + error = EIO; + goto done; + } + name = xop->head.name; + name_len = xop->head.name_len; + + /* + * Lookup the directory entry + */ + lhc = hammer2_dirhash(name, name_len); + chain = hammer2_chain_lookup(&parent, &key_next, + lhc, lhc + HAMMER2_DIRHASH_LOMASK, + &cache_index, + HAMMER2_LOOKUP_ALWAYS); + while (chain) { + ripdata = &chain->data->ipdata; + if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && + ripdata->meta.name_len == name_len && + bcmp(ripdata->filename, name, name_len) == 0) { + break; + } + chain = hammer2_chain_next(&parent, chain, &key_next, + key_next, + lhc + HAMMER2_DIRHASH_LOMASK, + &cache_index, + HAMMER2_LOOKUP_ALWAYS); + } + } + + /* + * Delete it, then create it in the new namespace. + */ + hammer2_chain_delete(parent, chain, 0); + hammer2_chain_unlock(parent); + hammer2_chain_drop(parent); + parent = NULL; /* safety */ + + + /* + * Ok, back to the deleted chain. We must reconnect this chain + * to tdir (ip3). The chain (a real inode or a hardlink pointer) + * is not otherwise modified. + * + * Frontend is expected to replicate the same inode meta data + * modifications. + * + * NOTE! This chain may not represent the actual inode, it + * can be a hardlink pointer. + * + * XXX in-inode parent directory specification? + */ + if (chain->data->ipdata.meta.name_key != xop->lhc || + xop->head.name_len != xop->head.name2_len || + bcmp(xop->head.name, xop->head.name2, xop->head.name_len) != 0) { + hammer2_inode_data_t *wipdata; + + hammer2_chain_modify(chain, 0); + wipdata = &chain->data->ipdata; + + bzero(wipdata->filename, sizeof(wipdata->filename)); + bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len); + wipdata->meta.name_key = xop->lhc; + wipdata->meta.name_len = xop->head.name2_len; + } + + /* + * We must seek parent properly for the create. + */ + parent = hammer2_inode_chain(xop->head.ip3, clindex, + HAMMER2_RESOLVE_ALWAYS); + if (parent == NULL) { + error = EIO; + goto done; + } + tmp = hammer2_chain_lookup(&parent, &key_dummy, + xop->lhc, xop->lhc, + &cache_index, 0); + if (tmp) { + hammer2_chain_unlock(tmp); + hammer2_chain_drop(tmp); + error = EEXIST; + goto done; + } + + error = hammer2_chain_create(&parent, &chain, pmp, + xop->lhc, 0, + HAMMER2_BREF_TYPE_INODE, + HAMMER2_INODE_BYTES, + 0); + /* + * (frontend is responsible for fixing up ip->pip). + */ +done: + hammer2_xop_feed(&xop->head, NULL, clindex, error); + if (parent) { + hammer2_chain_unlock(parent); + hammer2_chain_drop(parent); + } + if (chain) { + hammer2_chain_unlock(chain); + hammer2_chain_drop(chain); + } +} + /* * Directory collision resolver scan helper (backend, threaded). *