void afs_cache_permit(struct afs_vnode *vnode, struct key *key, long acl_order) { struct afs_permits *permits, *xpermits; struct afs_permit *permit; struct afs_vnode *auth_vnode; int count, loop; _enter("{%x:%u},%x,%lx", vnode->fid.vid, vnode->fid.vnode, key_serial(key), acl_order); auth_vnode = afs_get_auth_inode(vnode, key); if (IS_ERR(auth_vnode)) { _leave(" [get error %ld]", PTR_ERR(auth_vnode)); return; } mutex_lock(&auth_vnode->permits_lock); if (memcmp(&auth_vnode->fid, &vnode->status.parent, sizeof(struct afs_fid)) != 0) { _debug("renamed"); goto out_unlock; } if (auth_vnode->acl_order - acl_order > 0) { _debug("ACL changed?"); goto out_unlock; } _debug("anon access %x", vnode->status.anon_access); auth_vnode->status.anon_access = vnode->status.anon_access; if (key == vnode->volume->cell->anonymous_key) goto out_unlock; xpermits = auth_vnode->permits; count = 0; if (xpermits) { count = xpermits->count; permit = xpermits->permits; for (loop = count; loop > 0; loop--) { if (permit->key == key) { permit->access_mask = vnode->status.caller_access; goto out_unlock; } permit++; } } permits = kmalloc(sizeof(*permits) + sizeof(*permit) * (count + 1), GFP_NOFS); if (!permits) goto out_unlock; if (xpermits) memcpy(permits->permits, xpermits->permits, count * sizeof(struct afs_permit)); _debug("key %x access %x", key_serial(key), vnode->status.caller_access); permits->permits[count].access_mask = vnode->status.caller_access; permits->permits[count].key = key_get(key); permits->count = count + 1; rcu_assign_pointer(auth_vnode->permits, permits); if (xpermits) call_rcu(&xpermits->rcu, afs_dispose_of_permits); out_unlock: mutex_unlock(&auth_vnode->permits_lock); iput(&auth_vnode->vfs_inode); _leave(""); }
static int afs_check_permit(struct afs_vnode *vnode, struct key *key, afs_access_t *_access) { struct afs_permits *permits; struct afs_permit *permit; struct afs_vnode *auth_vnode; bool valid; int loop, ret; _enter("{%x:%u},%x", vnode->fid.vid, vnode->fid.vnode, key_serial(key)); auth_vnode = afs_get_auth_inode(vnode, key); if (IS_ERR(auth_vnode)) { *_access = 0; _leave(" = %ld", PTR_ERR(auth_vnode)); return PTR_ERR(auth_vnode); } ASSERT(S_ISDIR(auth_vnode->vfs_inode.i_mode)); if (key == auth_vnode->volume->cell->anonymous_key) { _debug("anon"); *_access = auth_vnode->status.anon_access; valid = true; } else { valid = false; rcu_read_lock(); permits = rcu_dereference(auth_vnode->permits); if (permits) { permit = permits->permits; for (loop = permits->count; loop > 0; loop--) { if (permit->key == key) { _debug("found in cache"); *_access = permit->access_mask; valid = true; break; } permit++; } } rcu_read_unlock(); } if (!valid) { _debug("no valid permit"); set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); ret = afs_vnode_fetch_status(vnode, auth_vnode, key); if (ret < 0) { iput(&auth_vnode->vfs_inode); *_access = 0; _leave(" = %d", ret); return ret; } *_access = vnode->status.caller_access; } iput(&auth_vnode->vfs_inode); _leave(" = 0 [access %x]", *_access); return 0; }
/* * add the result obtained for a vnode to its or its parent directory's cache * for the key used to access it */ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, long acl_order) { struct afs_permits *permits, *xpermits; struct afs_permit *permit; struct afs_vnode *auth_vnode; int count, loop; _enter("{%x:%u},%x,%lx", vnode->fid.vid, vnode->fid.vnode, key_serial(key), acl_order); auth_vnode = afs_get_auth_inode(vnode, key); if (IS_ERR(auth_vnode)) { _leave(" [get error %ld]", PTR_ERR(auth_vnode)); return; } mutex_lock(&auth_vnode->permits_lock); /* guard against a rename being detected whilst we waited for the * lock */ if (memcmp(&auth_vnode->fid, &vnode->status.parent, sizeof(struct afs_fid)) != 0) { _debug("renamed"); goto out_unlock; } /* have to be careful as the directory's callback may be broken between * us receiving the status we're trying to cache and us getting the * lock to update the cache for the status */ if (auth_vnode->acl_order - acl_order > 0) { _debug("ACL changed?"); goto out_unlock; } /* always update the anonymous mask */ _debug("anon access %x", vnode->status.anon_access); auth_vnode->status.anon_access = vnode->status.anon_access; if (key == vnode->volume->cell->anonymous_key) goto out_unlock; xpermits = auth_vnode->permits; count = 0; if (xpermits) { /* see if the permit is already in the list * - if it is then we just amend the list */ count = xpermits->count; permit = xpermits->permits; for (loop = count; loop > 0; loop--) { if (permit->key == key) { permit->access_mask = vnode->status.caller_access; goto out_unlock; } permit++; } } permits = kmalloc(sizeof(*permits) + sizeof(*permit) * (count + 1), GFP_NOFS); if (!permits) goto out_unlock; if (xpermits) memcpy(permits->permits, xpermits->permits, count * sizeof(struct afs_permit)); _debug("key %x access %x", key_serial(key), vnode->status.caller_access); permits->permits[count].access_mask = vnode->status.caller_access; permits->permits[count].key = key_get(key); permits->count = count + 1; rcu_assign_pointer(auth_vnode->permits, permits); if (xpermits) call_rcu(&xpermits->rcu, afs_dispose_of_permits); out_unlock: mutex_unlock(&auth_vnode->permits_lock); iput(&auth_vnode->vfs_inode); _leave(""); }