struct pefs_dircache_entry * pefs_dircache_enclookup(struct pefs_dircache *pd, char const *encname, size_t encname_len) { struct pefs_dircache_entry *pde; struct pefs_dircache_listhead *head; uint32_t h; h = dircache_hashname(pd, encname, encname_len); head = &dircache_enctbl[h & pefs_dircache_hashmask]; mtx_lock(&dircache_mtx); LIST_FOREACH(pde, head, pde_enchash_entry) { if (pde->pde_encnamehash == h && pde->pde_dircache == pd && pde->pde_encnamelen == encname_len && memcmp(pde->pde_encname, encname, encname_len) == 0) { mtx_unlock(&dircache_mtx); PEFSDEBUG("pefs_dircache_enclookup: found %s -> %s\n", pde->pde_name, pde->pde_encname); return (pde); } } mtx_unlock(&dircache_mtx); PEFSDEBUG("pefs_dircache_enclookup: not found %s\n", encname); return (NULL); }
static int pefs_node_lookup_key(struct pefs_mount *pm, struct vnode *lvp, struct vnode *ldvp, struct ucred *cred, struct pefs_tkey *ptk) { char *namebuf; char *encname; int error, encname_len, name_len; namebuf = malloc((MAXNAMLEN + 1)*2, M_PEFSBUF, M_WAITOK | M_ZERO); encname = namebuf + MAXNAMLEN + 1; encname_len = MAXNAMLEN + 1; error = pefs_node_lookup_name(lvp, ldvp, cred, encname, &encname_len); if (error != 0) { free(namebuf, M_PEFSBUF); return (error); } PEFSDEBUG("pefs_node_lookup_key: encname=%.*s\n", encname_len, encname); name_len = pefs_name_decrypt(NULL, pefs_rootkey(pm), ptk, encname, encname_len, namebuf, MAXNAMLEN + 1); if (name_len > 0) pefs_key_ref(ptk->ptk_key); else PEFSDEBUG("pefs_node_lookup_key: not found: %.*s\n", encname_len, encname); free(namebuf, M_PEFSBUF); return (error); }
struct pefs_dircache_entry * pefs_dircache_lookup(struct pefs_dircache *pd, char const *name, size_t name_len) { struct pefs_dircache_entry *pde; struct pefs_dircache_listhead *head; uint32_t h; MPASS(pd != NULL); MPASS((pd->pd_flags & PD_UPDATING) == 0); MPASS(LIST_EMPTY(DIRCACHE_STALEHEAD(pd))); h = dircache_hashname(pd, name, name_len); head = &dircache_tbl[h & pefs_dircache_hashmask]; mtx_lock(&dircache_mtx); LIST_FOREACH(pde, head, pde_hash_entry) { if (pde->pde_namehash == h && pde->pde_dircache == pd && pde->pde_gen == pd->pd_gen && pde->pde_namelen == name_len && memcmp(pde->pde_name, name, name_len) == 0) { mtx_unlock(&dircache_mtx); PEFSDEBUG("pefs_dircache_lookup: found %s -> %s\n", pde->pde_name, pde->pde_encname); return (pde); } } mtx_unlock(&dircache_mtx); PEFSDEBUG("pefs_dircache_lookup: not found %s\n", name); return (NULL); }
static void dircache_update(struct pefs_dircache_entry *pde, int onlist) { struct pefs_dircache *pd = pde->pde_dircache; sx_assert(&pd->pd_lock, SA_XLOCKED); if ((pd->pd_flags & PD_UPDATING) != 0) { PEFSDEBUG("pefs_dircache_update: %s -> %s\n", pde->pde_name, pde->pde_encname); pde->pde_gen = pd->pd_gen; if (onlist != 0) LIST_REMOVE(pde, pde_dir_entry); LIST_INSERT_HEAD(DIRCACHE_ACTIVEHEAD(pd), pde, pde_dir_entry); } else if (pd->pd_gen == 0 || pd->pd_gen != pde->pde_gen) { PEFSDEBUG("pefs_dircache: inconsistent cache: " "gen=%ld old_gen=%ld name=%s\n", pd->pd_gen, pde->pde_gen, pde->pde_name); dircache_expire(pd); pde->pde_gen = 0; if (onlist == 0) LIST_INSERT_HEAD(DIRCACHE_STALEHEAD(pd), pde, pde_dir_entry); } }
/* * Initialise cache headers */ int pefs_init(struct vfsconf *vfsp) { PEFSDEBUG("pefs_init\n"); LIST_INIT(&pefs_node_freelist); TASK_INIT(&pefs_task_freenode, 0, pefs_node_free_proc, NULL); pefs_taskq = taskqueue_create("pefs_taskq", M_WAITOK, taskqueue_thread_enqueue, &pefs_taskq); taskqueue_start_threads(&pefs_taskq, 1, PVFS, "pefs taskq"); pefs_node_zone = uma_zcreate("pefs_node", sizeof(struct pefs_node), NULL, NULL, NULL, (uma_fini) bzero, UMA_ALIGN_PTR, 0); pefs_nodehash_tbl = hashinit(desiredvnodes / 8, M_PEFSHASH, &pefs_nodehash_mask); pefs_nodes = 0; mtx_init(&pefs_node_listmtx, "pefs_node_list", NULL, MTX_DEF); pefs_dircache_init(); pefs_crypto_init(); return (0); }
void pefs_dircache_abortupdate(struct pefs_dircache *pd) { sx_assert(&pd->pd_lock, SA_XLOCKED); if ((pd->pd_flags & PD_UPDATING) != 0) { PEFSDEBUG("pefs_dircache_abortupdate: gen=%lu %p\n", pd->pd_gen, pd); dircache_expire(pd); pd->pd_flags &= ~PD_UPDATING; } DIRCACHE_ASSERT(pd); }
struct pefs_dircache_entry * pefs_dircache_insert(struct pefs_dircache *pd, struct pefs_tkey *ptk, char const *name, size_t name_len, char const *encname, size_t encname_len) { struct pefs_dircache_listhead *head; struct pefs_dircache_entry *pde; MPASS(ptk->ptk_key != NULL); sx_assert(&pd->pd_lock, SA_XLOCKED); if (name_len == 0 || name_len >= sizeof(pde->pde_name) || encname_len == 0 || encname_len >= sizeof(pde->pde_encname)) panic("pefs: invalid file name length: %zd/%zd", name_len, encname_len); pde = uma_zalloc(dircache_entry_zone, M_WAITOK | M_ZERO); pde->pde_dircache = pd; pde->pde_tkey = *ptk; pefs_key_ref(pde->pde_tkey.ptk_key); pde->pde_namelen = name_len; memcpy(pde->pde_name, name, name_len); pde->pde_name[name_len] = '\0'; pde->pde_namehash = dircache_hashname(pd, pde->pde_name, pde->pde_namelen); pde->pde_encnamelen = encname_len; memcpy(pde->pde_encname, encname, encname_len); pde->pde_encname[encname_len] = '\0'; pde->pde_encnamehash = dircache_hashname(pd, pde->pde_encname, pde->pde_encnamelen); /* Insert into list and set pge_gen */ dircache_update(pde, 0); mtx_lock(&dircache_mtx); head = &dircache_tbl[pde->pde_namehash & pefs_dircache_hashmask]; LIST_INSERT_HEAD(head, pde, pde_hash_entry); head = &dircache_enctbl[pde->pde_encnamehash & pefs_dircache_hashmask]; LIST_INSERT_HEAD(head, pde, pde_enchash_entry); dircache_entries++; mtx_unlock(&dircache_mtx); PEFSDEBUG("pefs_dircache_insert: hash=%x enchash=%x: %s -> %s\n", pde->pde_namehash, pde->pde_encnamehash, pde->pde_name, pde->pde_encname); return (pde); }
static void pefs_insmntque_dtr(struct vnode *vp, void *_pn) { struct pefs_node *pn = _pn; PEFSDEBUG("pefs_insmntque_dtr: free node %p\n", pn); vp->v_data = NULL; vp->v_vnlock = &vp->v_lock; pefs_key_release(pn->pn_tkey.ptk_key); uma_zfree(pefs_node_zone, pn); vp->v_op = &dead_vnodeops; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); vgone(vp); vput(vp); }
static void dircache_entry_free(struct pefs_dircache_entry *pde) { MPASS(pde != NULL); PEFSDEBUG("dircache_entry_free: %s -> %s\n", pde->pde_name, pde->pde_encname); pefs_key_release(pde->pde_tkey.ptk_key); LIST_REMOVE(pde, pde_dir_entry); mtx_lock(&dircache_mtx); LIST_REMOVE(pde, pde_hash_entry); LIST_REMOVE(pde, pde_enchash_entry); dircache_entries--; mtx_unlock(&dircache_mtx); uma_zfree(dircache_entry_zone, pde); }
static void dircache_expire(struct pefs_dircache *pd) { struct pefs_dircache_entry *pde; pd->pd_gen = 0; if (LIST_EMPTY(DIRCACHE_STALEHEAD(pd))) { pd->pd_flags ^= PD_SWAPEDHEADS; } else while (!LIST_EMPTY(DIRCACHE_ACTIVEHEAD(pd))) { pde = LIST_FIRST(DIRCACHE_ACTIVEHEAD(pd)); pde->pde_gen = 0; LIST_REMOVE(pde, pde_dir_entry); LIST_INSERT_HEAD(DIRCACHE_STALEHEAD(pd), pde, pde_dir_entry); PEFSDEBUG("dircache_expire: active entry: %p\n", pde); } MPASS(LIST_EMPTY(DIRCACHE_ACTIVEHEAD(pd))); }
void pefs_dircache_beginupdate(struct pefs_dircache *pd, u_long gen) { if (sx_try_upgrade(&pd->pd_lock) == 0) { /* vnode should be locked to avoid races */ sx_unlock(&pd->pd_lock); sx_xlock(&pd->pd_lock); } if (gen != 0 && pd->pd_gen != gen) { PEFSDEBUG("pefs_dircache_beginupdate: update: gen=%lu %p\n", gen, pd); if (!LIST_EMPTY(DIRCACHE_ACTIVEHEAD(pd))) { /* Assert consistent state */ MPASS(LIST_EMPTY(DIRCACHE_STALEHEAD(pd))); dircache_expire(pd); } pd->pd_gen = gen; pd->pd_flags |= PD_UPDATING; MPASS(LIST_EMPTY(DIRCACHE_ACTIVEHEAD(pd))); } }
/* * Make a new or get existing pefs node. * vp is the alias vnode * lvp is the lower vnode * ldvp is the lower directory vnode, used if no key specified * * The lvp assumed to be locked and having "spare" reference. This routine * vrele lvp if pefs node was taken from hash. Otherwise it "transfers" the * caller's "spare" reference to created pefs vnode. */ static int pefs_node_get(struct mount *mp, struct vnode *lvp, struct vnode **vpp, pefs_node_init_fn *init_fn, void *context) { struct pefs_node *pn; struct vnode *vp; int error; ASSERT_VOP_LOCKED(lvp, "pefs_node_get"); /* Lookup the hash firstly */ *vpp = pefs_nodehash_get(mp, lvp); if (*vpp != NULL) { vrele(lvp); return (0); } /* * We do not serialize vnode creation, instead we will check for * duplicates later, when adding new vnode to hash. * * Note that duplicate can only appear in hash if the lvp is * locked LK_SHARED. */ /* * Do the MALLOC before the getnewvnode since doing so afterward * might cause a bogus v_data pointer to get dereferenced * elsewhere if MALLOC should block. */ pn = uma_zalloc(pefs_node_zone, M_WAITOK | M_ZERO); pn->pn_lowervp = lvp; /* pn->pn_lowervp should be initialized before calling init_fn. */ error = init_fn(mp, pn, context); MPASS(!(((pn->pn_flags & PN_HASKEY) == 0) ^ (pn->pn_tkey.ptk_key == NULL))); if (error != 0) { uma_zfree(pefs_node_zone, pn); return (error); } error = getnewvnode("pefs", mp, &pefs_vnodeops, &vp); if (error != 0) { pefs_key_release(pn->pn_tkey.ptk_key); uma_zfree(pefs_node_zone, pn); return (error); } if (pn->pn_tkey.ptk_key == NULL) PEFSDEBUG("pefs_node_get: creating node without key: %p\n", pn); pn->pn_vnode = vp; vp->v_type = lvp->v_type; vp->v_data = pn; vp->v_vnlock = lvp->v_vnlock; if (vp->v_vnlock == NULL) panic("pefs_node_get: Passed a NULL vnlock.\n"); error = insmntque1(vp, mp, pefs_insmntque_dtr, pn); if (error != 0) return (error); /* * Atomically insert our new node into the hash or vget existing * if someone else has beaten us to it. */ *vpp = pefs_nodehash_insert(mp, pn); if (*vpp != NULL) { vrele(lvp); vp->v_vnlock = &vp->v_lock; pn->pn_lowervp = NULL; vrele(vp); MPASS(PEFS_LOWERVP(*vpp) == lvp); ASSERT_VOP_LOCKED(*vpp, "pefs_node_get: duplicate"); return (0); } if (vp->v_type == VDIR) pn->pn_dircache = pefs_dircache_get(); *vpp = vp; MPASS(PEFS_LOWERVP(*vpp) == lvp); ASSERT_VOP_LOCKED(*vpp, "pefs_node_get"); return (0); }