/* * Refactor the locked chains of a cluster. */ void hammer2_cluster_refactor(hammer2_cluster_t *cluster) { int i; cluster->focus = NULL; for (i = 0; i < cluster->nchains; ++i) { if (cluster->array[i]) { hammer2_chain_refactor(&cluster->array[i]); if (cluster->focus == NULL) cluster->focus = cluster->array[i]; } } }
/* * Connect the target inode represented by (*chainp) 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. * * If hlink is TRUE this function creates an OBJTYPE_HARDLINK directory * entry instead of connecting (*chainp). * * If hlink is FALSE this function uses chain_duplicate() to make a copy * if (*chainp) in the directory entry. (*chainp) is likely to be deleted * by the caller in this case (e.g. rename). */ int hammer2_inode_connect(hammer2_trans_t *trans, hammer2_chain_t **chainp, int hlink, hammer2_inode_t *dip, hammer2_chain_t **dchainp, const uint8_t *name, size_t name_len, hammer2_key_t lhc) { hammer2_inode_data_t *ipdata; hammer2_chain_t *nchain; hammer2_chain_t *ochain; hammer2_key_t key_dummy; int cache_index = -1; int error; /* * Since ochain 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->chain cache. */ ochain = *chainp; /* * If name is non-NULL we calculate lhc, else we use the passed-in * lhc. */ 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) { nchain = hammer2_chain_lookup(dchainp, &key_dummy, lhc, lhc, &cache_index, 0); if (nchain == NULL) break; if ((lhc & HAMMER2_DIRHASH_LOMASK) == HAMMER2_DIRHASH_LOMASK) { error = ENOSPC; } hammer2_chain_unlock(nchain); nchain = NULL; ++lhc; } } else { /* * Reconnect to specific key (used when moving * unlinked-but-open files into the hidden directory). */ nchain = hammer2_chain_lookup(dchainp, &key_dummy, lhc, lhc, &cache_index, 0); KKASSERT(nchain == NULL); } if (error == 0) { if (hlink) { /* * Hardlink pointer needed, create totally fresh * directory entry. * * We must refactor ochain because it might have * been shifted into an indirect chain by the * create. */ KKASSERT(nchain == NULL); error = hammer2_chain_create(trans, dchainp, &nchain, lhc, 0, HAMMER2_BREF_TYPE_INODE, HAMMER2_INODE_BYTES); hammer2_chain_refactor(&ochain); } else { /* * Reconnect the original chain and rename. Use * chain_duplicate(). The caller will likely delete * or has already deleted the original chain in * this case. * * NOTE: chain_duplicate() generates a new chain * with CHAIN_DELETED cleared (ochain typically * has it set from the file unlink). * * WARNING! Can cause held-over chains to require a * refactor. Fortunately we have none (our * locked chains are passed into and * modified by the call). */ nchain = ochain; ochain = NULL; hammer2_chain_duplicate(trans, NULL, &nchain, NULL, 0, 3); error = hammer2_chain_create(trans, dchainp, &nchain, lhc, 0, HAMMER2_BREF_TYPE_INODE, HAMMER2_INODE_BYTES); } } /* * Unlock stuff. */ KKASSERT(error != EAGAIN); /* * nchain should be NULL on error, leave ochain (== *chainp) alone. */ if (error) { KKASSERT(nchain == NULL); return (error); } /* * 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 * chain, the caller will access the hardlink via the actual hardlink * target file and not the hardlink pointer entry, so we must still * return ochain. */ if (hlink && hammer2_hardlink_enable >= 0) { /* * Create the HARDLINK pointer. oip represents the hardlink * target in this situation. * * We will return ochain (the hardlink target). */ hammer2_chain_modify(trans, &nchain, 0); KKASSERT(name_len < HAMMER2_INODE_MAXNAME); ipdata = &nchain->data->ipdata; bcopy(name, ipdata->filename, name_len); ipdata->name_key = lhc; ipdata->name_len = name_len; ipdata->target_type = ochain->data->ipdata.type; ipdata->type = HAMMER2_OBJTYPE_HARDLINK; ipdata->inum = ochain->data->ipdata.inum; ipdata->nlinks = 1; hammer2_chain_unlock(nchain); nchain = ochain; ochain = NULL; } else if (hlink && hammer2_hardlink_enable < 0) { /* * Create a snapshot (hardlink fake mode for debugging). * (ochain already flushed above so we can just copy the * bref XXX). * * Since this is a snapshot we return nchain in the fake * hardlink case. */ hammer2_chain_modify(trans, &nchain, 0); KKASSERT(name_len < HAMMER2_INODE_MAXNAME); ipdata = &nchain->data->ipdata; *ipdata = ochain->data->ipdata; bcopy(name, ipdata->filename, name_len); ipdata->name_key = lhc; ipdata->name_len = name_len; atomic_clear_int(&nchain->core->flags, HAMMER2_CORE_COUNTEDBREFS); kprintf("created fake hardlink %*.*s\n", (int)name_len, (int)name_len, name); } else { /* * nchain is a duplicate of ochain at the new location. * We must fixup the name stored in oip. The bref key * has already been set up. */ hammer2_chain_modify(trans, &nchain, 0); ipdata = &nchain->data->ipdata; KKASSERT(name_len < HAMMER2_INODE_MAXNAME); bcopy(name, ipdata->filename, name_len); ipdata->name_key = lhc; ipdata->name_len = name_len; ipdata->nlinks = 1; } /* * We are replacing ochain with nchain, unlock ochain. In the * case where ochain is left unchanged the code above sets * nchain to ochain and ochain to NULL, resulting in a NOP here. */ if (ochain) hammer2_chain_unlock(ochain); *chainp = nchain; return (0); }
/* * Connect the target inode represented by (*chainp) to the media topology * at (dip, name, len). * * If hlink is TRUE this function creates an OBJTYPE_HARDLINK directory * entry instead of connecting (*chainp). * * If hlink is FALSE this function uses chain_duplicate() to make a copy * if (*chainp) in the directory entry. (*chainp) is likely to be deleted * by the caller in this case (e.g. rename). */ int hammer2_inode_connect(hammer2_trans_t *trans, int hlink, hammer2_inode_t *dip, hammer2_chain_t **chainp, const uint8_t *name, size_t name_len) { hammer2_inode_data_t *ipdata; hammer2_chain_t *nchain; hammer2_chain_t *parent; hammer2_chain_t *ochain; hammer2_key_t lhc; int error; ochain = *chainp; /* * Since ochain 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. */ parent = hammer2_chain_lookup_init(dip->chain, 0); 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) { nchain = hammer2_chain_lookup(&parent, lhc, lhc, 0); if (nchain == NULL) break; if ((lhc & HAMMER2_DIRHASH_LOMASK) == HAMMER2_DIRHASH_LOMASK) error = ENOSPC; hammer2_chain_unlock(nchain); nchain = NULL; ++lhc; } if (error == 0) { if (hlink) { /* * Hardlink pointer needed, create totally fresh * directory entry. */ KKASSERT(nchain == NULL); error = hammer2_chain_create(trans, &parent, &nchain, lhc, 0, HAMMER2_BREF_TYPE_INODE, HAMMER2_INODE_BYTES); hammer2_chain_refactor(&ochain); } else { /* * Reconnect the original chain and rename. Use * chain_duplicate(). The caller will likely delete * or has already deleted the original chain in * this case. * * NOTE: chain_duplicate() generates a new chain * with CHAIN_DELETED cleared (ochain typically * has it set from the file unlink). */ nchain = ochain; ochain = NULL; hammer2_chain_duplicate(trans, NULL, -1, &nchain, NULL); error = hammer2_chain_create(trans, &parent, &nchain, lhc, 0, HAMMER2_BREF_TYPE_INODE, HAMMER2_INODE_BYTES); } } /* * Unlock stuff. */ KKASSERT(error != EAGAIN); hammer2_chain_lookup_done(parent); parent = NULL; /* * nchain should be NULL on error, leave ochain (== *chainp) alone. */ if (error) { KKASSERT(nchain == NULL); return (error); } /* * 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 * chain, the caller will access the hardlink via the actual hardlink * target file and not the hardlink pointer entry, so we must still * return ochain. */ if (hlink && hammer2_hardlink_enable >= 0) { /* * Create the HARDLINK pointer. oip represents the hardlink * target in this situation. * * We will return ochain (the hardlink target). */ hammer2_chain_modify(trans, &nchain, HAMMER2_MODIFY_ASSERTNOCOPY); KKASSERT(name_len < HAMMER2_INODE_MAXNAME); ipdata = &nchain->data->ipdata; bcopy(name, ipdata->filename, name_len); ipdata->name_key = lhc; ipdata->name_len = name_len; ipdata->target_type = ochain->data->ipdata.type; ipdata->type = HAMMER2_OBJTYPE_HARDLINK; ipdata->inum = ochain->data->ipdata.inum; ipdata->nlinks = 1; hammer2_chain_unlock(nchain); nchain = ochain; ochain = NULL; } else if (hlink && hammer2_hardlink_enable < 0) { /* * Create a snapshot (hardlink fake mode for debugging). * (ochain already flushed above so we can just copy the * bref XXX). * * Since this is a snapshot we return nchain in the fake * hardlink case. */ hammer2_chain_modify(trans, &nchain, HAMMER2_MODIFY_ASSERTNOCOPY); KKASSERT(name_len < HAMMER2_INODE_MAXNAME); ipdata = &nchain->data->ipdata; *ipdata = ochain->data->ipdata; bcopy(name, ipdata->filename, name_len); ipdata->name_key = lhc; ipdata->name_len = name_len; kprintf("created fake hardlink %*.*s\n", (int)name_len, (int)name_len, name); } else { /* * nchain is a duplicate of ochain at the new location. * We must fixup the name stored in oip. The bref key * has already been set up. */ hammer2_chain_modify(trans, &nchain, HAMMER2_MODIFY_ASSERTNOCOPY); ipdata = &nchain->data->ipdata; KKASSERT(name_len < HAMMER2_INODE_MAXNAME); bcopy(name, ipdata->filename, name_len); ipdata->name_key = lhc; ipdata->name_len = name_len; ipdata->nlinks = 1; } /* * We are replacing ochain with nchain, unlock ochain. In the * case where ochain is left unchanged the code above sets * nchain to ochain and ochain to NULL, resulting in a NOP here. */ if (ochain) hammer2_chain_unlock(ochain); *chainp = nchain; return (0); }
/* * ochain represents the target file inode. We need to move it to the * specified common parent directory (dip) and rename it to a special * invisible "0xINODENUMBER" filename. * * We use chain_duplicate and duplicate ochain at the new location, * renaming it appropriately. We create a temporary chain and * then delete it to placemark where the duplicate will go. Both of * these use the inode number for (lhc) (the key), generating the * invisible filename. */ static hammer2_chain_t * hammer2_hardlink_shiftup(hammer2_trans_t *trans, hammer2_chain_t **ochainp, hammer2_inode_t *dip, int *errorp) { hammer2_inode_data_t *nipdata; hammer2_chain_t *parent; hammer2_chain_t *ochain; hammer2_chain_t *nchain; hammer2_chain_t *tmp; hammer2_key_t lhc; hammer2_blockref_t bref; ochain = *ochainp; *errorp = 0; lhc = ochain->data->ipdata.inum; 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. */ retry: parent = hammer2_chain_lookup_init(dip->chain, 0); nchain = hammer2_chain_lookup(&parent, lhc, lhc, 0); if (nchain) { kprintf("X3 chain %p parent %p dip %p dip->chain %p\n", nchain, parent, dip, dip->chain); hammer2_chain_unlock(nchain); nchain = NULL; *errorp = ENOSPC; #if 1 Debugger("X3"); #endif } /* * Create entry in common parent directory using the seek position * calculated above. */ if (*errorp == 0) { KKASSERT(nchain == NULL); *errorp = hammer2_chain_create(trans, &parent, &nchain, lhc, 0, HAMMER2_BREF_TYPE_INODE,/* n/a */ HAMMER2_INODE_BYTES); /* n/a */ hammer2_chain_refactor(&ochain); *ochainp = ochain; } /* * Cleanup and handle retries. */ if (*errorp == EAGAIN) { hammer2_chain_ref(parent); hammer2_chain_lookup_done(parent); hammer2_chain_wait(parent); hammer2_chain_drop(parent); goto retry; } /* * Handle the error case */ if (*errorp) { KKASSERT(nchain == NULL); hammer2_chain_lookup_done(parent); return (NULL); } /* * Use chain as a placeholder for (lhc), delete it and replace * it with our duplication. * * Gain a second lock on ochain for the duplication function to * unlock, maintain the caller's original lock across the call. * * This is a bit messy. */ hammer2_chain_delete(trans, nchain); hammer2_chain_lock(ochain, HAMMER2_RESOLVE_ALWAYS); tmp = ochain; bref = tmp->bref; bref.key = lhc; /* invisible dir entry key */ bref.keybits = 0; hammer2_chain_duplicate(trans, parent, nchain->index, &tmp, &bref); hammer2_chain_lookup_done(parent); hammer2_chain_unlock(nchain); /* no longer needed */ /* * Now set chain to our duplicate and modify it appropriately. * * 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. */ nchain = tmp; tmp = NULL; /* safety */ hammer2_chain_modify(trans, &nchain, HAMMER2_MODIFY_ASSERTNOCOPY); nipdata = &nchain->data->ipdata; ksnprintf(nipdata->filename, sizeof(nipdata->filename), "0x%016jx", (intmax_t)nipdata->inum); nipdata->name_len = strlen(nipdata->filename); nipdata->name_key = lhc; return (nchain); }