/* * validate a vnode/inode * - there are several things we need to check * - parent dir data changes (rm, rmdir, rename, mkdir, create, link, * symlink) * - parent dir metadata changed (security changes) * - dentry data changed (write, truncate) * - dentry metadata changed (security changes) */ int afs_validate(struct afs_vnode *vnode, struct key *key) { int ret; _enter("{v={%x:%u} fl=%lx},%x", vnode->fid.vid, vnode->fid.vnode, vnode->flags, key_serial(key)); if (vnode->cb_promised && !test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) && !test_bit(AFS_VNODE_MODIFIED, &vnode->flags) && !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { if (vnode->cb_expires < get_seconds() + 10) { _debug("callback expired"); set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); } else { goto valid; } } if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) goto valid; mutex_lock(&vnode->validate_lock); /* if the promise has expired, we need to check the server again to get * a new promise - note that if the (parent) directory's metadata was * changed then the security may be different and we may no longer have * access */ if (!vnode->cb_promised || test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { _debug("not promised"); ret = afs_vnode_fetch_status(vnode, NULL, key); if (ret < 0) goto error_unlock; _debug("new promise [fl=%lx]", vnode->flags); } if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { _debug("file already deleted"); ret = -ESTALE; goto error_unlock; } /* if the vnode's data version number changed then its contents are * different */ if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) afs_zap_data(vnode); clear_bit(AFS_VNODE_MODIFIED, &vnode->flags); mutex_unlock(&vnode->validate_lock); valid: _leave(" = 0"); return 0; error_unlock: mutex_unlock(&vnode->validate_lock); _leave(" = %d", ret); return ret; }
/* * handle the data invalidation side of a callback being broken */ void afs_broken_callback_work(struct work_struct *work) { struct afs_vnode *vnode = container_of(work, struct afs_vnode, cb_broken_work); _enter(""); if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) return; /* we're only interested in dealing with a broken callback on *this* * vnode and only if no-one else has dealt with it yet */ if (!mutex_trylock(&vnode->validate_lock)) return; /* someone else is dealing with it */ if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { if (S_ISDIR(vnode->vfs_inode.i_mode)) afs_clear_permits(vnode); if (afs_vnode_fetch_status(vnode, NULL, NULL) < 0) goto out; if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) goto out; /* if the vnode's data version number changed then its contents * are different */ if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) afs_zap_data(vnode); } out: mutex_unlock(&vnode->validate_lock); /* avoid the potential race whereby the mutex_trylock() in this * function happens again between the clear_bit() and the * mutex_unlock() */ if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { _debug("requeue"); queue_work(afs_callback_update_worker, &vnode->cb_broken_work); } _leave(""); }
/* * validate a vnode/inode * - there are several things we need to check * - parent dir data changes (rm, rmdir, rename, mkdir, create, link, * symlink) * - parent dir metadata changed (security changes) * - dentry data changed (write, truncate) * - dentry metadata changed (security changes) */ int afs_validate(struct afs_vnode *vnode, struct key *key) { time64_t now = ktime_get_real_seconds(); bool valid = false; int ret; _enter("{v={%x:%u} fl=%lx},%x", vnode->fid.vid, vnode->fid.vnode, vnode->flags, key_serial(key)); /* Quickly check the callback state. Ideally, we'd use read_seqbegin * here, but we have no way to pass the net namespace to the RCU * cleanup for the server record. */ read_seqlock_excl(&vnode->cb_lock); if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) { vnode->cb_s_break = vnode->cb_interest->server->cb_s_break; } else if (!test_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags) && !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && vnode->cb_expires_at - 10 > now) { valid = true; } } else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { valid = true; } read_sequnlock_excl(&vnode->cb_lock); if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) clear_nlink(&vnode->vfs_inode); if (valid) goto valid; mutex_lock(&vnode->validate_lock); /* if the promise has expired, we need to check the server again to get * a new promise - note that if the (parent) directory's metadata was * changed then the security may be different and we may no longer have * access */ if (!test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { _debug("not promised"); ret = afs_fetch_status(vnode, key); if (ret < 0) { if (ret == -ENOENT) { set_bit(AFS_VNODE_DELETED, &vnode->flags); ret = -ESTALE; } goto error_unlock; } _debug("new promise [fl=%lx]", vnode->flags); } if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { _debug("file already deleted"); ret = -ESTALE; goto error_unlock; } /* if the vnode's data version number changed then its contents are * different */ if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) afs_zap_data(vnode); clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags); mutex_unlock(&vnode->validate_lock); valid: _leave(" = 0"); return 0; error_unlock: mutex_unlock(&vnode->validate_lock); _leave(" = %d", ret); return ret; }