/* * When an inode is flagged INODE_DELETING it has been deleted (no directory * entry or open refs are left, though as an optimization H2 might leave * nlinks == 1 to avoid unnecessary block updates). The backend flush then * needs to actually remove it from the topology. * * NOTE: backend flush must still sync and flush the deleted inode to clean * out related chains. * * NOTE: We must clear not only INODE_DELETING, but also INODE_ISUNLINKED * to prevent the vnode reclaim code from trying to delete it twice. */ int hammer2_inode_chain_des(hammer2_inode_t *ip) { int error; error = 0; if (ip->flags & HAMMER2_INODE_DELETING) { hammer2_xop_destroy_t *xop; atomic_clear_int(&ip->flags, HAMMER2_INODE_DELETING | HAMMER2_INODE_ISUNLINKED); xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING); hammer2_xop_start(&xop->head, &hammer2_inode_destroy_desc); error = hammer2_xop_collect(&xop->head, 0); hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); if (error == HAMMER2_ERROR_ENOENT) error = 0; if (error) { kprintf("hammer2: backend unable to " "delete inode %p %ld\n", ip, ip->meta.inum); /* XXX return error somehow? */ } } return error; }
/* * When an inode is flagged INODE_CREATING its chains have not actually * been inserting into the on-media tree yet. */ int hammer2_inode_chain_ins(hammer2_inode_t *ip) { int error; error = 0; if (ip->flags & HAMMER2_INODE_CREATING) { hammer2_xop_create_t *xop; atomic_clear_int(&ip->flags, HAMMER2_INODE_CREATING); xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING); xop->lhc = ip->meta.inum; xop->flags = 0; hammer2_xop_start(&xop->head, &hammer2_inode_create_ins_desc); error = hammer2_xop_collect(&xop->head, 0); hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); if (error == HAMMER2_ERROR_ENOENT) error = 0; if (error) { kprintf("hammer2: backend unable to " "insert inode %p %ld\n", ip, ip->meta.inum); /* XXX return error somehow? */ } } return error; }
/* * Flushes the inode's chain and its sub-topology to media. Interlocks * HAMMER2_INODE_DIRTYDATA by clearing it prior to the flush. Any strategy * function creating or modifying a chain under this inode will re-set the * flag. * * inode must be locked. */ int hammer2_inode_chain_flush(hammer2_inode_t *ip, int flags) { hammer2_xop_fsync_t *xop; int error; atomic_clear_int(&ip->flags, HAMMER2_INODE_DIRTYDATA); xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING | flags); hammer2_xop_start(&xop->head, &hammer2_inode_flush_desc); error = hammer2_xop_collect(&xop->head, HAMMER2_XOP_COLLECT_WAITALL); hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); if (error == HAMMER2_ERROR_ENOENT) error = 0; return error; }
/* * Synchronize the inode's frontend state with the chain state prior * to any explicit flush of the inode or any strategy write call. This * does not flush the inode's chain or its sub-topology to media (higher * level layers are responsible for doing that). * * Called with a locked inode inside a normal transaction. * * inode must be locked. */ int hammer2_inode_chain_sync(hammer2_inode_t *ip) { int error; error = 0; if (ip->flags & (HAMMER2_INODE_RESIZED | HAMMER2_INODE_MODIFIED)) { hammer2_xop_fsync_t *xop; xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING); xop->clear_directdata = 0; if (ip->flags & HAMMER2_INODE_RESIZED) { if ((ip->meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA) && ip->meta.size > HAMMER2_EMBEDDED_BYTES) { ip->meta.op_flags &= ~HAMMER2_OPFLAG_DIRECTDATA; xop->clear_directdata = 1; } xop->osize = ip->osize; } else { xop->osize = ip->meta.size; /* safety */ } xop->ipflags = ip->flags; xop->meta = ip->meta; atomic_clear_int(&ip->flags, HAMMER2_INODE_RESIZED | HAMMER2_INODE_MODIFIED); hammer2_xop_start(&xop->head, &hammer2_inode_chain_sync_desc); error = hammer2_xop_collect(&xop->head, 0); hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); if (error == HAMMER2_ERROR_ENOENT) error = 0; if (error) { kprintf("hammer2: unable to fsync inode %p\n", ip); /* atomic_set_int(&ip->flags, xop->ipflags & (HAMMER2_INODE_RESIZED | HAMMER2_INODE_MODIFIED)); */ /* XXX return error somehow? */ } } return error; }
/* * Each out of sync node sync-thread must issue an all-nodes XOP scan of * the inode. This creates a multiplication effect since the XOP scan itself * issues to all nodes. However, this is the only way we can safely * synchronize nodes which might have disparate I/O bandwidths and the only * way we can safely deal with stalled nodes. */ static int hammer2_sync_slaves(hammer2_thread_t *thr, hammer2_inode_t *ip, hammer2_deferred_list_t *list) { hammer2_xop_scanall_t *xop; hammer2_chain_t *parent; hammer2_chain_t *chain; hammer2_pfs_t *pmp; hammer2_key_t key_next; hammer2_tid_t sync_tid; int cache_index = -1; int needrescan; int wantupdate; int error; int nerror; int idx; int n; pmp = ip->pmp; idx = thr->clindex; /* cluster node we are responsible for */ needrescan = 0; wantupdate = 0; if (ip->cluster.focus == NULL) return (EINPROGRESS); sync_tid = ip->cluster.focus->bref.modify_tid; #if 0 /* * Nothing to do if all slaves are synchronized. * Nothing to do if cluster not authoritatively readable. */ if (pmp->cluster_flags & HAMMER2_CLUSTER_SSYNCED) return(0); if ((pmp->cluster_flags & HAMMER2_CLUSTER_RDHARD) == 0) return(HAMMER2_ERROR_INCOMPLETE); #endif error = 0; /* * The inode is left unlocked during the scan. Issue a XOP * that does *not* include our cluster index to iterate * properly synchronized elements and resolve our cluster index * against it. */ hammer2_inode_lock(ip, HAMMER2_RESOLVE_SHARED); xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING); xop->key_beg = HAMMER2_KEY_MIN; xop->key_end = HAMMER2_KEY_MAX; hammer2_xop_start_except(&xop->head, hammer2_xop_scanall, idx); parent = hammer2_inode_chain(ip, idx, HAMMER2_RESOLVE_ALWAYS | HAMMER2_RESOLVE_SHARED); if (parent->bref.modify_tid != sync_tid) wantupdate = 1; hammer2_inode_unlock(ip); chain = hammer2_chain_lookup(&parent, &key_next, HAMMER2_KEY_MIN, HAMMER2_KEY_MAX, &cache_index, HAMMER2_LOOKUP_SHARED | HAMMER2_LOOKUP_NODIRECT | HAMMER2_LOOKUP_NODATA); error = hammer2_xop_collect(&xop->head, 0); kprintf("XOP_INITIAL xop=%p clindex %d on %s\n", xop, thr->clindex, pmp->pfs_names[thr->clindex]); for (;;) { /* * We are done if our scan is done and the XOP scan is done. * We are done if the XOP scan failed (that is, we don't * have authoritative data to synchronize with). */ int advance_local = 0; int advance_xop = 0; int dodefer = 0; hammer2_chain_t *focus; kprintf("loop xop=%p chain[1]=%p lockcnt=%d\n", xop, xop->head.cluster.array[1].chain, (xop->head.cluster.array[1].chain ? xop->head.cluster.array[1].chain->lockcnt : -1) ); if (chain == NULL && error == ENOENT) break; if (error && error != ENOENT) break; /* * Compare */ if (chain && error == ENOENT) { /* * If we have local chains but the XOP scan is done, * the chains need to be deleted. */ n = -1; focus = NULL; } else if (chain == NULL) { /* * If our local scan is done but the XOP scan is not, * we need to create the missing chain(s). */ n = 1; focus = xop->head.cluster.focus; } else { /* * Otherwise compare to determine the action * needed. */ focus = xop->head.cluster.focus; n = hammer2_chain_cmp(chain, focus); } /* * Take action based on comparison results. */ if (n < 0) { /* * Delete extranious local data. This will * automatically advance the chain. */ nerror = hammer2_sync_destroy(thr, &parent, &chain, 0, idx); } else if (n == 0 && chain->bref.modify_tid != focus->bref.modify_tid) { /* * Matching key but local data or meta-data requires * updating. If we will recurse, we still need to * update to compatible content first but we do not * synchronize modify_tid until the entire recursion * has completed successfully. */ if (focus->bref.type == HAMMER2_BREF_TYPE_INODE) { nerror = hammer2_sync_replace( thr, parent, chain, 0, idx, focus); dodefer = 1; } else { nerror = hammer2_sync_replace( thr, parent, chain, focus->bref.modify_tid, idx, focus); } } else if (n == 0) { /* * 100% match, advance both */ advance_local = 1; advance_xop = 1; nerror = 0; } else if (n > 0) { /* * Insert missing local data. * * If we will recurse, we still need to update to * compatible content first but we do not synchronize * modify_tid until the entire recursion has * completed successfully. */ if (focus->bref.type == HAMMER2_BREF_TYPE_INODE) { nerror = hammer2_sync_insert( thr, &parent, &chain, 0, idx, focus); dodefer = 2; } else { nerror = hammer2_sync_insert( thr, &parent, &chain, focus->bref.modify_tid, idx, focus); } advance_local = 1; advance_xop = 1; } /* * We cannot recurse depth-first because the XOP is still * running in node threads for this scan. Create a placemarker * by obtaining and record the hammer2_inode. * * We excluded our node from the XOP so we must temporarily * add it to xop->head.cluster so it is properly incorporated * into the inode. * * The deferral is pushed onto a LIFO list for bottom-up * synchronization. */ if (error == 0 && dodefer) { hammer2_inode_t *nip; hammer2_deferred_ip_t *defer; KKASSERT(focus->bref.type == HAMMER2_BREF_TYPE_INODE); defer = kmalloc(sizeof(*defer), M_HAMMER2, M_WAITOK | M_ZERO); KKASSERT(xop->head.cluster.array[idx].chain == NULL); xop->head.cluster.array[idx].flags = HAMMER2_CITEM_INVALID; xop->head.cluster.array[idx].chain = chain; nip = hammer2_inode_get(pmp, ip, &xop->head.cluster, idx); xop->head.cluster.array[idx].chain = NULL; hammer2_inode_ref(nip); hammer2_inode_unlock(nip); defer->next = list->base; defer->ip = nip; list->base = defer; ++list->count; needrescan = 1; } /* * If at least one deferral was added and the deferral * list has grown too large, stop adding more. This * will trigger an EAGAIN return. */ if (needrescan && list->count > 1000) break; /* * Advancements for iteration. */ if (advance_xop) { error = hammer2_xop_collect(&xop->head, 0); } if (advance_local) { chain = hammer2_chain_next(&parent, chain, &key_next, key_next, HAMMER2_KEY_MAX, &cache_index, HAMMER2_LOOKUP_SHARED | HAMMER2_LOOKUP_NODIRECT | HAMMER2_LOOKUP_NODATA); } } hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); if (chain) { hammer2_chain_unlock(chain); hammer2_chain_drop(chain); } if (parent) { hammer2_chain_unlock(parent); hammer2_chain_drop(parent); } /* * If we added deferrals we want the caller to synchronize them * and then call us again. * * NOTE: In this situation we do not yet want to synchronize our * inode, setting the error code also has that effect. */ if (error == 0 && needrescan) error = EAGAIN; /* * If no error occurred and work was performed, synchronize the * inode meta-data itself. * * XXX inode lock was lost */ if (error == 0 && wantupdate) { hammer2_xop_ipcluster_t *xop2; hammer2_chain_t *focus; xop2 = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING); hammer2_xop_start_except(&xop2->head, hammer2_xop_ipcluster, idx); error = hammer2_xop_collect(&xop2->head, 0); if (error == 0) { focus = xop2->head.cluster.focus; kprintf("syncthr: update inode %p (%s)\n", focus, (focus ? (char *)focus->data->ipdata.filename : "?")); chain = hammer2_inode_chain_and_parent(ip, idx, &parent, HAMMER2_RESOLVE_ALWAYS | HAMMER2_RESOLVE_SHARED); KKASSERT(parent != NULL); nerror = hammer2_sync_replace( thr, parent, chain, sync_tid, idx, focus); hammer2_chain_unlock(chain); hammer2_chain_drop(chain); hammer2_chain_unlock(parent); hammer2_chain_drop(parent); /* XXX */ } hammer2_xop_retire(&xop2->head, HAMMER2_XOPMASK_VOP); } return error; }
/* * Primary management thread for xops support. Each node has several such * threads which replicate front-end operations on cluster nodes. * * XOPS thread node operations, allowing the function to focus on a single * node in the cluster after validating the operation with the cluster. * This is primarily what prevents dead or stalled nodes from stalling * the front-end. */ void hammer2_primary_xops_thread(void *arg) { hammer2_thread_t *thr = arg; hammer2_pfs_t *pmp; hammer2_xop_head_t *xop; hammer2_xop_group_t *xgrp; uint32_t mask; pmp = thr->pmp; xgrp = &pmp->xop_groups[thr->repidx]; mask = 1U << thr->clindex; lockmgr(&thr->lk, LK_EXCLUSIVE); while ((thr->flags & HAMMER2_THREAD_STOP) == 0) { /* * Handle freeze request */ if (thr->flags & HAMMER2_THREAD_FREEZE) { atomic_set_int(&thr->flags, HAMMER2_THREAD_FROZEN); atomic_clear_int(&thr->flags, HAMMER2_THREAD_FREEZE); } /* * Force idle if frozen until unfrozen or stopped. */ if (thr->flags & HAMMER2_THREAD_FROZEN) { lksleep(&thr->flags, &thr->lk, 0, "frozen", 0); continue; } /* * Reset state on REMASTER request */ if (thr->flags & HAMMER2_THREAD_REMASTER) { atomic_clear_int(&thr->flags, HAMMER2_THREAD_REMASTER); /* reset state */ } /* * Process requests. Each request can be multi-queued. * * If we get behind and the frontend VOP is no longer active, * we retire the request without processing it. The callback * may also abort processing if the frontend VOP becomes * inactive. */ while ((xop = TAILQ_FIRST(&thr->xopq)) != NULL) { TAILQ_REMOVE(&thr->xopq, xop, collect[thr->clindex].entry); if (hammer2_xop_active(xop)) { lockmgr(&thr->lk, LK_RELEASE); xop->func((hammer2_xop_t *)xop, thr->clindex); hammer2_xop_retire(xop, mask); lockmgr(&thr->lk, LK_EXCLUSIVE); } else { hammer2_xop_feed(xop, NULL, thr->clindex, ECONNABORTED); hammer2_xop_retire(xop, mask); } } /* * Wait for event. */ lksleep(&thr->flags, &thr->lk, 0, "h2idle", 0); } /* * Cleanup / termination */ while ((xop = TAILQ_FIRST(&thr->xopq)) != NULL) { kprintf("hammer2_thread: aborting xop %p\n", xop->func); TAILQ_REMOVE(&thr->xopq, xop, collect[thr->clindex].entry); hammer2_xop_retire(xop, mask); } thr->td = NULL; wakeup(thr); lockmgr(&thr->lk, LK_RELEASE); /* thr structure can go invalid after this point */ }
/* * Create a PFS inode under the superroot. This function will create the * inode, its media chains, and also insert it into the media. * * Caller must be in a flush transaction because we are inserting the inode * onto the media. */ hammer2_inode_t * hammer2_inode_create_pfs(hammer2_pfs_t *spmp, const uint8_t *name, size_t name_len, int *errorp) { hammer2_xop_create_t *xop; hammer2_inode_t *pip; hammer2_inode_t *nip; int error; uuid_t pip_uid; uuid_t pip_gid; uint32_t pip_mode; uint8_t pip_comp_algo; uint8_t pip_check_algo; hammer2_tid_t pip_inum; hammer2_key_t lhc; pip = spmp->iroot; nip = NULL; lhc = hammer2_dirhash(name, name_len); *errorp = 0; /* * 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. * * Lock the directory exclusively for now to guarantee that * we can find an unused lhc for the name. Due to collisions, * two different creates can end up with the same lhc so we * cannot depend on the OS to prevent the collision. */ hammer2_inode_lock(pip, 0); pip_uid = pip->meta.uid; pip_gid = pip->meta.gid; pip_mode = pip->meta.mode; pip_comp_algo = pip->meta.comp_algo; pip_check_algo = pip->meta.check_algo; pip_inum = (pip == pip->pmp->iroot) ? 1 : pip->meta.inum; /* * Locate an unused key in the collision space. */ { hammer2_xop_scanlhc_t *sxop; hammer2_key_t lhcbase; lhcbase = lhc; sxop = hammer2_xop_alloc(pip, HAMMER2_XOP_MODIFYING); sxop->lhc = lhc; hammer2_xop_start(&sxop->head, &hammer2_scanlhc_desc); while ((error = hammer2_xop_collect(&sxop->head, 0)) == 0) { if (lhc != sxop->head.cluster.focus->bref.key) break; ++lhc; } hammer2_xop_retire(&sxop->head, HAMMER2_XOPMASK_VOP); if (error) { if (error != HAMMER2_ERROR_ENOENT) goto done2; ++lhc; error = 0; } if ((lhcbase ^ lhc) & ~HAMMER2_DIRHASH_LOMASK) { error = HAMMER2_ERROR_ENOSPC; goto done2; } } /* * Create the inode with the lhc as the key. */ xop = hammer2_xop_alloc(pip, HAMMER2_XOP_MODIFYING); xop->lhc = lhc; xop->flags = HAMMER2_INSERT_PFSROOT; bzero(&xop->meta, sizeof(xop->meta)); xop->meta.type = HAMMER2_OBJTYPE_DIRECTORY; xop->meta.inum = 1; xop->meta.iparent = pip_inum; /* Inherit parent's inode compression mode. */ xop->meta.comp_algo = pip_comp_algo; xop->meta.check_algo = pip_check_algo; xop->meta.version = HAMMER2_INODE_VERSION_ONE; hammer2_update_time(&xop->meta.ctime); xop->meta.mtime = xop->meta.ctime; xop->meta.mode = 0755; xop->meta.nlinks = 1; /* * Regular files and softlinks allow a small amount of data to be * directly embedded in the inode. This flag will be cleared if * the size is extended past the embedded limit. */ if (xop->meta.type == HAMMER2_OBJTYPE_REGFILE || xop->meta.type == HAMMER2_OBJTYPE_SOFTLINK) { xop->meta.op_flags |= HAMMER2_OPFLAG_DIRECTDATA; } 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); hammer2_xop_start(&xop->head, &hammer2_inode_create_desc); error = hammer2_xop_collect(&xop->head, 0); #if INODE_DEBUG kprintf("CREATE INODE %*.*s\n", (int)name_len, (int)name_len, name); #endif if (error) { *errorp = error; goto done; } /* * Set up the new inode if not a hardlink pointer. * * NOTE: *_get() integrates chain's lock into the inode lock. * * NOTE: Only one new inode can currently be created per * transaction. If the need arises we can adjust * hammer2_trans_init() to allow more. * * NOTE: nipdata will have chain's blockset data. */ nip = hammer2_inode_get(pip->pmp, &xop->head, -1, -1); nip->comp_heuristic = 0; done: hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); done2: hammer2_inode_unlock(pip); return (nip); }
/* * Called with a locked inode to finish unlinking an inode after xop_unlink * had been run. This function is responsible for decrementing nlinks. * * We don't bother decrementing nlinks if the file is not open and this was * the last link. * * If the inode is a hardlink target it's chain has not yet been deleted, * otherwise it's chain has been deleted. * * If isopen then any prior deletion was not permanent and the inode is * left intact with nlinks == 0; */ int hammer2_inode_unlink_finisher(hammer2_inode_t *ip, int isopen) { hammer2_pfs_t *pmp; int error; pmp = ip->pmp; /* * Decrement nlinks. If this is the last link and the file is * not open we can just delete the inode and not bother dropping * nlinks to 0 (avoiding unnecessary block updates). */ if (ip->meta.nlinks == 1) { atomic_set_int(&ip->flags, HAMMER2_INODE_ISUNLINKED); if (isopen == 0) goto killit; } hammer2_inode_modify(ip); --ip->meta.nlinks; if ((int64_t)ip->meta.nlinks < 0) ip->meta.nlinks = 0; /* safety */ /* * 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 (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 (ip->vp) hammer2_knote(ip->vp, NOTE_DELETE); /* * nlinks is now an implied zero, delete the inode if not open. * We avoid unnecessary media updates by not bothering to actually * decrement nlinks for the 1->0 transition * * Put the inode on the sideq to ensure that any disconnected chains * get properly flushed (so they can be freed). Defer the deletion * to the sync code, doing it now will desynchronize the inode from * related directory entries (which is bad). * * NOTE: killit can be reached without modifying the inode, so * make sure that it is on the SIDEQ. */ if (isopen == 0) { #if 0 hammer2_xop_destroy_t *xop; #endif killit: atomic_set_int(&ip->flags, HAMMER2_INODE_DELETING); hammer2_inode_delayed_sideq(ip); #if 0 xop = hammer2_xop_alloc(ip, HAMMER2_XOP_MODIFYING); hammer2_xop_start(&xop->head, &hammer2_inode_destroy_desc); error = hammer2_xop_collect(&xop->head, 0); hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); #endif } error = 0; /* XXX */ return error; }
/* * Create a directory entry under dip with the specified name, inode number, * and OBJTYPE (type). * * This returns a UNIX errno code, not a HAMMER2_ERROR_* code. * * Caller must hold dip locked. */ int hammer2_dirent_create(hammer2_inode_t *dip, const char *name, size_t name_len, hammer2_key_t inum, uint8_t type) { hammer2_xop_mkdirent_t *xop; hammer2_key_t lhc; int error; lhc = 0; error = 0; KKASSERT(name != NULL); 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. * * Lock the directory exclusively for now to guarantee that * we can find an unused lhc for the name. Due to collisions, * two different creates can end up with the same lhc so we * cannot depend on the OS to prevent the collision. */ hammer2_inode_modify(dip); /* * If name specified, locate an unused key in the collision space. * Otherwise use the passed-in lhc directly. */ { hammer2_xop_scanlhc_t *sxop; hammer2_key_t lhcbase; lhcbase = lhc; sxop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING); sxop->lhc = lhc; hammer2_xop_start(&sxop->head, &hammer2_scanlhc_desc); while ((error = hammer2_xop_collect(&sxop->head, 0)) == 0) { if (lhc != sxop->head.cluster.focus->bref.key) break; ++lhc; } hammer2_xop_retire(&sxop->head, HAMMER2_XOPMASK_VOP); if (error) { if (error != HAMMER2_ERROR_ENOENT) goto done2; ++lhc; error = 0; } if ((lhcbase ^ lhc) & ~HAMMER2_DIRHASH_LOMASK) { error = HAMMER2_ERROR_ENOSPC; goto done2; } } /* * Create the directory entry with the lhc as the key. */ xop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING); xop->lhc = lhc; bzero(&xop->dirent, sizeof(xop->dirent)); xop->dirent.inum = inum; xop->dirent.type = type; xop->dirent.namlen = name_len; KKASSERT(name_len < HAMMER2_INODE_MAXNAME); hammer2_xop_setname(&xop->head, name, name_len); hammer2_xop_start(&xop->head, &hammer2_inode_mkdirent_desc); error = hammer2_xop_collect(&xop->head, 0); hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); done2: error = hammer2_error_to_errno(error); return error; }
/* * Create a new, normal inode. This function will create the inode, * the media chains, but will not insert the chains onto the media topology * (doing so would require a flush transaction and cause long stalls). * * Caller must be in a normal transaction. */ hammer2_inode_t * hammer2_inode_create_normal(hammer2_inode_t *pip, struct vattr *vap, struct ucred *cred, hammer2_key_t inum, int *errorp) { hammer2_xop_create_t *xop; hammer2_inode_t *dip; hammer2_inode_t *nip; int error; uid_t xuid; uuid_t pip_uid; uuid_t pip_gid; uint32_t pip_mode; uint8_t pip_comp_algo; uint8_t pip_check_algo; hammer2_tid_t pip_inum; uint8_t type; dip = pip->pmp->iroot; KKASSERT(dip != NULL); *errorp = 0; /*hammer2_inode_lock(dip, 0);*/ pip_uid = pip->meta.uid; pip_gid = pip->meta.gid; pip_mode = pip->meta.mode; pip_comp_algo = pip->meta.comp_algo; pip_check_algo = pip->meta.check_algo; pip_inum = (pip == pip->pmp->iroot) ? 1 : pip->meta.inum; /* * Create the in-memory hammer2_inode structure for the specified * inode. */ nip = hammer2_inode_get(dip->pmp, NULL, inum, -1); nip->comp_heuristic = 0; KKASSERT((nip->flags & HAMMER2_INODE_CREATING) == 0 && nip->cluster.nchains == 0); atomic_set_int(&nip->flags, HAMMER2_INODE_CREATING); /* * Setup the inode meta-data */ nip->meta.type = hammer2_get_obj_type(vap->va_type); switch (nip->meta.type) { case HAMMER2_OBJTYPE_CDEV: case HAMMER2_OBJTYPE_BDEV: nip->meta.rmajor = vap->va_rmajor; nip->meta.rminor = vap->va_rminor; break; default: break; } type = nip->meta.type; KKASSERT(nip->meta.inum == inum); nip->meta.iparent = pip_inum; /* Inherit parent's inode compression mode. */ nip->meta.comp_algo = pip_comp_algo; nip->meta.check_algo = pip_check_algo; nip->meta.version = HAMMER2_INODE_VERSION_ONE; hammer2_update_time(&nip->meta.ctime); nip->meta.mtime = nip->meta.ctime; nip->meta.mode = vap->va_mode; nip->meta.nlinks = 1; xuid = hammer2_to_unix_xid(&pip_uid); xuid = vop_helper_create_uid(dip->pmp->mp, pip_mode, xuid, cred, &vap->va_mode); if (vap->va_vaflags & VA_UID_UUID_VALID) nip->meta.uid = vap->va_uid_uuid; else if (vap->va_uid != (uid_t)VNOVAL) hammer2_guid_to_uuid(&nip->meta.uid, vap->va_uid); else hammer2_guid_to_uuid(&nip->meta.uid, xuid); if (vap->va_vaflags & VA_GID_UUID_VALID) nip->meta.gid = vap->va_gid_uuid; else if (vap->va_gid != (gid_t)VNOVAL) hammer2_guid_to_uuid(&nip->meta.gid, vap->va_gid); else nip->meta.gid = pip_gid; /* * Regular files and softlinks allow a small amount of data to be * directly embedded in the inode. This flag will be cleared if * the size is extended past the embedded limit. */ if (nip->meta.type == HAMMER2_OBJTYPE_REGFILE || nip->meta.type == HAMMER2_OBJTYPE_SOFTLINK) { nip->meta.op_flags |= HAMMER2_OPFLAG_DIRECTDATA; } /* * Create the inode using (inum) as the key. Pass pip for * method inheritance. */ xop = hammer2_xop_alloc(pip, HAMMER2_XOP_MODIFYING); xop->lhc = inum; xop->flags = 0; xop->meta = nip->meta; KKASSERT(vap); xop->meta.name_len = hammer2_xop_setname_inum(&xop->head, inum); xop->meta.name_key = inum; nip->meta.name_len = xop->meta.name_len; nip->meta.name_key = xop->meta.name_key; hammer2_inode_modify(nip); /* * Create the inode media chains but leave them detached. We are * not in a flush transaction so we can't mess with media topology * above normal inodes (i.e. the index of the inodes themselves). * * We've already set the INODE_CREATING flag. The inode's media * chains will be inserted onto the media topology on the next * filesystem sync. */ hammer2_xop_start(&xop->head, &hammer2_inode_create_det_desc); error = hammer2_xop_collect(&xop->head, 0); #if INODE_DEBUG kprintf("create inode type %d error %d\n", nip->meta.type, error); #endif if (error) { *errorp = error; goto done; } /* * Associate the media chains created by the backend with the * frontend inode. */ hammer2_inode_repoint(nip, NULL, &xop->head.cluster); done: hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); /*hammer2_inode_unlock(dip);*/ return (nip); }