int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, u64 child_nodeid, struct qstr *name) { int err = -ENOTDIR; struct inode *parent; struct dentry *dir; struct dentry *entry; parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid); if (!parent) return -ENOENT; inode_lock(parent); if (!S_ISDIR(parent->i_mode)) goto unlock; err = -ENOENT; dir = d_find_alias(parent); if (!dir) goto unlock; name->hash = full_name_hash(dir, name->name, name->len); entry = d_lookup(dir, name); dput(dir); if (!entry) goto unlock; fuse_dir_changed(parent); fuse_invalidate_entry(entry); if (child_nodeid != 0 && d_really_is_positive(entry)) { inode_lock(d_inode(entry)); if (get_node_id(d_inode(entry)) != child_nodeid) { err = -ENOENT; goto badentry; } if (d_mountpoint(entry)) { err = -EBUSY; goto badentry; } if (d_is_dir(entry)) { shrink_dcache_parent(entry); if (!simple_empty(entry)) { err = -ENOTEMPTY; goto badentry; } d_inode(entry)->i_flags |= S_DEAD; } dont_mount(entry); clear_nlink(d_inode(entry)); err = 0; badentry: inode_unlock(d_inode(entry)); if (!err) d_delete(entry); } else { err = 0; } dput(entry); unlock: inode_unlock(parent); iput(parent); return err; }
static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) { struct dentry *parent = dget_parent(dentry); struct gfs2_sbd *sdp = GFS2_SB(parent->d_inode); struct gfs2_inode *dip = GFS2_I(parent->d_inode); struct inode *inode = dentry->d_inode; struct gfs2_holder d_gh; struct gfs2_inode *ip = NULL; int error; int had_lock=0; if (inode) { if (is_bad_inode(inode)) goto invalid; ip = GFS2_I(inode); } if (sdp->sd_args.ar_localcaching) goto valid; had_lock = (gfs2_glock_is_locked_by_me(dip->i_gl) != NULL); if (!had_lock) { error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); if (error) goto fail; } error = gfs2_dir_check(parent->d_inode, &dentry->d_name, ip); switch (error) { case 0: if (!inode) goto invalid_gunlock; break; case -ENOENT: if (!inode) goto valid_gunlock; goto invalid_gunlock; default: goto fail_gunlock; } valid_gunlock: if (!had_lock) gfs2_glock_dq_uninit(&d_gh); valid: dput(parent); return 1; invalid_gunlock: if (!had_lock) gfs2_glock_dq_uninit(&d_gh); invalid: if (inode && S_ISDIR(inode->i_mode)) { if (have_submounts(dentry)) goto valid; shrink_dcache_parent(dentry); } d_drop(dentry); dput(parent); return 0; fail_gunlock: gfs2_glock_dq_uninit(&d_gh); fail: dput(parent); return 0; }
int lefuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, u64 child_nodeid, struct qstr *name) { int err = -ENOTDIR; struct inode *parent; struct dentry *dir; struct dentry *entry; parent = ilookup5(sb, parent_nodeid, lefuse_inode_eq, &parent_nodeid); if (!parent) return -ENOENT; mutex_lock(&parent->i_mutex); if (!S_ISDIR(parent->i_mode)) goto unlock; err = -ENOENT; dir = d_find_alias(parent); if (!dir) goto unlock; entry = d_lookup(dir, name); dput(dir); if (!entry) goto unlock; lefuse_invalidate_attr(parent); fuse_invalidate_entry(entry); if (child_nodeid != 0 && entry->d_inode) { mutex_lock(&entry->d_inode->i_mutex); if (get_node_id(entry->d_inode) != child_nodeid) { err = -ENOENT; goto badentry; } if (d_mountpoint(entry)) { err = -EBUSY; goto badentry; } if (S_ISDIR(entry->d_inode->i_mode)) { shrink_dcache_parent(entry); if (!simple_empty(entry)) { err = -ENOTEMPTY; goto badentry; } entry->d_inode->i_flags |= S_DEAD; } dont_mount(entry); clear_nlink(entry->d_inode); err = 0; badentry: mutex_unlock(&entry->d_inode->i_mutex); if (!err) d_delete(entry); } else { err = 0; } dput(entry); unlock: mutex_unlock(&parent->i_mutex); iput(parent); return err; }
/** * nilfs_tree_is_busy() - try to shrink dentries of a checkpoint * @root_dentry: root dentry of the tree to be shrunk * * This function returns true if the tree was in-use. */ static bool nilfs_tree_is_busy(struct dentry *root_dentry) { shrink_dcache_parent(root_dentry); return d_count(root_dentry) > 1; }
/* * Check whether the dentry is still valid * * If the entry validity timeout has expired and the dentry is * positive, try to redo the lookup. If the lookup results in a * different inode, then let the VFS invalidate the dentry and redo * the lookup once more. If the lookup results in the same inode, * then refresh the attributes, timeouts and mark the dentry valid. */ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) { struct inode *inode = entry->d_inode; struct dentry *parent; struct fuse_conn *fc; int ret; if (inode && is_bad_inode(inode)) goto invalid; else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) || (nd->flags & LOOKUP_REVAL)) { int err; struct fuse_entry_out outarg; struct fuse_req *req; struct fuse_forget_link *forget; u64 attr_version; /* For negative dentries, always do a fresh lookup */ if (!inode) goto invalid; fc = get_fuse_conn(inode); req = fuse_get_req_nopages(fc); ret = PTR_ERR(req); if (IS_ERR(req)) goto out; forget = fuse_alloc_forget(); if (!forget) { fuse_put_request(fc, req); ret = -ENOMEM; goto out; } attr_version = fuse_get_attr_version(fc); parent = dget_parent(entry); fuse_lookup_init(fc, req, get_node_id(parent->d_inode), &entry->d_name, &outarg); fuse_request_send(fc, req); dput(parent); err = req->out.h.error; fuse_put_request(fc, req); /* Zero nodeid is same as -ENOENT */ if (!err && !outarg.nodeid) err = -ENOENT; if (!err) { struct fuse_inode *fi = get_fuse_inode(inode); if (outarg.nodeid != get_node_id(inode)) { fuse_queue_forget(fc, forget, outarg.nodeid, 1); goto invalid; } spin_lock(&fc->lock); fi->nlookup++; spin_unlock(&fc->lock); } kfree(forget); if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) goto invalid; fuse_change_attributes(inode, &outarg.attr, entry_attr_timeout(&outarg), attr_version); fuse_change_entry_timeout(entry, &outarg); } else if (inode) { fc = get_fuse_conn(inode); if (fc->readdirplus_auto) { parent = dget_parent(entry); fuse_advise_use_readdirplus(parent->d_inode); dput(parent); } } ret = 1; out: return ret; invalid: ret = 0; if (inode) { if (S_ISDIR(inode->i_mode)) { if (have_submounts(entry)) { ret = 1; goto out; } shrink_dcache_parent(entry); } else { if (d_mountpoint(entry)) { ret = 1; goto out; } } } d_drop(entry); goto out; }
static void sel_remove_entries(struct dentry *de) { d_genocide(de); shrink_dcache_parent(de); }
static void presto_put_super(struct super_block *sb) { struct presto_cache *cache; struct upc_channel *channel; struct super_operations *sops; struct list_head *lh; int err; ENTRY; cache = presto_cache_find(sb->s_dev); if (!cache) { EXIT; goto exit; } channel = &izo_channels[presto_c2m(cache)]; sops = filter_c2csops(cache->cache_filter); err = izo_clear_all_fsetroots(cache); if (err) { CERROR("%s: err %d\n", __FUNCTION__, err); } PRESTO_FREE(cache->cache_vfsmount, sizeof(struct vfsmount)); /* look at kill_super - fsync_super is not exported GRRR but probably not needed */ unlock_super(sb); shrink_dcache_parent(cache->cache_root); dput(cache->cache_root); //fsync_super(sb); lock_super(sb); if (sops->write_super) sops->write_super(sb); if (sops->put_super) sops->put_super(sb); /* free any remaining async upcalls when the filesystem is unmounted */ spin_lock(&channel->uc_lock); lh = channel->uc_pending.next; while ( lh != &channel->uc_pending) { struct upc_req *req; req = list_entry(lh, struct upc_req, rq_chain); /* assignment must be here: we are about to free &lh */ lh = lh->next; if ( ! (req->rq_flags & REQ_ASYNC) ) continue; list_del(&(req->rq_chain)); PRESTO_FREE(req->rq_data, req->rq_bufsize); PRESTO_FREE(req, sizeof(struct upc_req)); } list_del(&cache->cache_channel_list); spin_unlock(&channel->uc_lock); presto_free_cache(cache); exit: CDEBUG(D_MALLOC, "after umount: kmem %ld, vmem %ld\n", presto_kmemory, presto_vmemory); MOD_DEC_USE_COUNT; return ; }
static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { struct afs_vnode *vnode, *dir; struct afs_fid uninitialized_var(fid); struct dentry *parent; struct key *key; void *dir_version; int ret; vnode = AFS_FS_I(dentry->d_inode); if (dentry->d_inode) _enter("{v={%x:%u} n=%s fl=%lx},", vnode->fid.vid, vnode->fid.vnode, dentry->d_name.name, vnode->flags); else _enter("{neg n=%s}", dentry->d_name.name); key = afs_request_key(AFS_FS_S(dentry->d_sb)->volume->cell); if (IS_ERR(key)) key = NULL; /* lock down the parent dentry so we can peer at it */ parent = dget_parent(dentry); if (!parent->d_inode) goto out_bad; dir = AFS_FS_I(parent->d_inode); /* validate the parent directory */ if (test_bit(AFS_VNODE_MODIFIED, &dir->flags)) afs_validate(dir, key); if (test_bit(AFS_VNODE_DELETED, &dir->flags)) { _debug("%s: parent dir deleted", dentry->d_name.name); goto out_bad; } dir_version = (void *) (unsigned long) dir->status.data_version; if (dentry->d_fsdata == dir_version) goto out_valid; /* the dir contents are unchanged */ _debug("dir modified"); /* search the directory for this vnode */ ret = afs_do_lookup(&dir->vfs_inode, dentry, &fid, key); switch (ret) { case 0: /* the filename maps to something */ if (!dentry->d_inode) goto out_bad; if (is_bad_inode(dentry->d_inode)) { printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", parent->d_name.name, dentry->d_name.name); goto out_bad; } /* if the vnode ID has changed, then the dirent points to a * different file */ if (fid.vnode != vnode->fid.vnode) { _debug("%s: dirent changed [%u != %u]", dentry->d_name.name, fid.vnode, vnode->fid.vnode); goto not_found; } /* if the vnode ID uniqifier has changed, then the file has * been deleted and replaced, and the original vnode ID has * been reused */ if (fid.unique != vnode->fid.unique) { _debug("%s: file deleted (uq %u -> %u I:%llu)", dentry->d_name.name, fid.unique, vnode->fid.unique, (unsigned long long)dentry->d_inode->i_version); spin_lock(&vnode->lock); set_bit(AFS_VNODE_DELETED, &vnode->flags); spin_unlock(&vnode->lock); goto not_found; } goto out_valid; case -ENOENT: /* the filename is unknown */ _debug("%s: dirent not found", dentry->d_name.name); if (dentry->d_inode) goto not_found; goto out_valid; default: _debug("failed to iterate dir %s: %d", parent->d_name.name, ret); goto out_bad; } out_valid: dentry->d_fsdata = dir_version; out_skip: dput(parent); key_put(key); _leave(" = 1 [valid]"); return 1; /* the dirent, if it exists, now points to a different vnode */ not_found: spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NFSFS_RENAMED; spin_unlock(&dentry->d_lock); out_bad: if (dentry->d_inode) { /* don't unhash if we have submounts */ if (have_submounts(dentry)) goto out_skip; } _debug("dropping dentry %s/%s", parent->d_name.name, dentry->d_name.name); shrink_dcache_parent(dentry); d_drop(dentry); dput(parent); key_put(key); _leave(" = 0 [bad]"); return 0; }
/* * rename uses retrying to avoid race-conditions: at least they should be minimal. * it tries to allocate all the blocks, then sanity-checks, and if the sanity- * checks fail, it tries to restart itself again. Very practical - no changes * are done until we know everything works ok.. and then all the changes can be * done in one fell swoop when we have claimed all the buffers needed. * * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ static int do_sysv_rename(struct inode * old_dir, struct dentry * old_dentry, struct inode * new_dir, struct dentry * new_dentry) { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; struct sysv_dir_entry * old_de, * new_de; int retval; goto start_up; try_again: brelse(old_bh); brelse(new_bh); brelse(dir_bh); current->counter = 0; schedule(); start_up: old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; old_bh = sysv_find_entry(old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); retval = -ENOENT; if (!old_bh) goto end_rename; old_inode = old_dentry->d_inode; /* don't cross mnt-points */ retval = -EPERM; new_inode = new_dentry->d_inode; new_bh = sysv_find_entry(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); if (new_bh) { if (!new_inode) { brelse(new_bh); new_bh = NULL; } } if (new_inode == old_inode) { retval = 0; goto end_rename; } if (S_ISDIR(old_inode->i_mode)) { retval = -EINVAL; if (is_subdir(new_dentry, old_dentry)) goto end_rename; if (new_inode) { if (new_dentry->d_count > 1) shrink_dcache_parent(new_dentry); retval = -EBUSY; if (new_dentry->d_count > 1) goto end_rename; retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; } retval = -EIO; dir_bh = sysv_file_bread(old_inode, 0, 0); if (!dir_bh) goto end_rename; if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) goto end_rename; retval = -EMLINK; if (!new_inode && new_dir->i_nlink >= new_dir->i_sb->sv_link_max) goto end_rename; } if (!new_bh) { retval = sysv_add_entry(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_bh, &new_de); if (retval) goto end_rename; } /* sanity checking before doing the rename - avoid races */ if (new_inode && (new_de->inode != new_inode->i_ino)) goto try_again; if (new_de->inode && !new_inode) goto try_again; if (old_de->inode != old_inode->i_ino) goto try_again; /* ok, that's it */ old_de->inode = 0; new_de->inode = old_inode->i_ino; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(old_dir); new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(new_dir); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; mark_inode_dirty(new_inode); } mark_buffer_dirty(old_bh, 1); mark_buffer_dirty(new_bh, 1); if (dir_bh) { PARENT_INO(dir_bh->b_data) = new_dir->i_ino; mark_buffer_dirty(dir_bh, 1); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; mark_inode_dirty(new_dir); } } d_move(old_dentry, new_dentry); retval = 0; end_rename: brelse(dir_bh); brelse(old_bh); brelse(new_bh); return retval; }
/* * This is called every time the dcache has a lookup hit, * and we should check whether we can really trust that * lookup. * * NOTE! The hit can be a negative hit too, don't assume * we have an inode! * * If the parent directory is seen to have changed, we throw out the * cached dentry and do a new lookup. */ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) { struct inode *dir; struct inode *inode; struct dentry *parent; int error; struct nfs_fh fhandle; struct nfs_fattr fattr; unsigned long verifier; int isopen = 0; parent = dget_parent(dentry); lock_kernel(); dir = parent->d_inode; inode = dentry->d_inode; if (nd && !(nd->flags & LOOKUP_CONTINUE) && (nd->flags & LOOKUP_OPEN)) isopen = 1; if (!inode) { if (nfs_neg_need_reval(dir, dentry, nd)) goto out_bad; goto out_valid; } if (is_bad_inode(inode)) { dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n", dentry->d_parent->d_name.name, dentry->d_name.name); goto out_bad; } /* Revalidate parent directory attribute cache */ nfs_revalidate_inode(NFS_SERVER(dir), dir); /* Force a full look up iff the parent directory has changed */ if (nfs_check_verifier(dir, dentry)) { if (nfs_lookup_verify_inode(inode, isopen)) goto out_zap_parent; goto out_valid; } /* * Note: we're not holding inode->i_sem and so may be racing with * operations that change the directory. We therefore save the * change attribute *before* we do the RPC call. */ verifier = nfs_save_change_attribute(dir); error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr); if (!error) { if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) goto out_bad; if (nfs_lookup_verify_inode(inode, isopen)) goto out_zap_parent; goto out_valid_renew; } if (NFS_STALE(inode)) goto out_bad; error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); if (error) goto out_bad; if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) goto out_bad; if ((error = nfs_refresh_inode(inode, &fattr)) != 0) goto out_bad; out_valid_renew: nfs_renew_times(dentry); nfs_set_verifier(dentry, verifier); out_valid: unlock_kernel(); dput(parent); return 1; out_zap_parent: nfs_zap_caches(dir); out_bad: NFS_CACHEINV(dir); if (inode && S_ISDIR(inode->i_mode)) { /* Purge readdir caches. */ nfs_zap_caches(inode); /* If we have submounts, don't unhash ! */ if (have_submounts(dentry)) goto out_valid; shrink_dcache_parent(dentry); } d_drop(dentry); unlock_kernel(); dput(parent); return 0; }
/* * RENAME * FIXME: Some nfsds, like the Linux user space nfsd, may generate a * different file handle for the same inode after a rename (e.g. when * moving to a different directory). A fail-safe method to do so would * be to look up old_dir/old_name, create a link to new_dir/new_name and * rename the old file using the sillyrename stuff. This way, the original * file in old_dir will go away when the last process iput()s the inode. * * FIXED. * * It actually works quite well. One needs to have the possibility for * at least one ".nfs..." file in each directory the file ever gets * moved or linked to which happens automagically with the new * implementation that only depends on the dcache stuff instead of * using the inode layer * * Unfortunately, things are a little more complicated than indicated * above. For a cross-directory move, we want to make sure we can get * rid of the old inode after the operation. This means there must be * no pending writes (if it's a file), and the use count must be 1. * If these conditions are met, we can drop the dentries before doing * the rename. */ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; struct dentry *dentry = NULL, *rehash = NULL; int error = -EBUSY; /* * To prevent any new references to the target during the rename, * we unhash the dentry and free the inode in advance. */ lock_kernel(); if (!d_unhashed(new_dentry)) { d_drop(new_dentry); rehash = new_dentry; } dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n", old_dentry->d_parent->d_name.name, old_dentry->d_name.name, new_dentry->d_parent->d_name.name, new_dentry->d_name.name, atomic_read(&new_dentry->d_count)); /* * First check whether the target is busy ... we can't * safely do _any_ rename if the target is in use. * * For files, make a copy of the dentry and then do a * silly-rename. If the silly-rename succeeds, the * copied dentry is hashed and becomes the new target. */ if (!new_inode) goto go_ahead; if (S_ISDIR(new_inode->i_mode)) goto out; else if (atomic_read(&new_dentry->d_count) > 1) { int err; /* copy the target dentry's name */ dentry = d_alloc(new_dentry->d_parent, &new_dentry->d_name); if (!dentry) goto out; /* silly-rename the existing target ... */ err = nfs_sillyrename(new_dir, new_dentry); if (!err) { new_dentry = rehash = dentry; new_inode = NULL; /* instantiate the replacement target */ d_instantiate(new_dentry, NULL); } /* dentry still busy? */ if (atomic_read(&new_dentry->d_count) > 1) { #ifdef NFS_PARANOIA printk("nfs_rename: target %s/%s busy, d_count=%d\n", new_dentry->d_parent->d_name.name, new_dentry->d_name.name, atomic_read(&new_dentry->d_count)); #endif goto out; } } go_ahead: /* * ... prune child dentries and writebacks if needed. */ if (atomic_read(&old_dentry->d_count) > 1) { nfs_wb_all(old_inode); shrink_dcache_parent(old_dentry); } if (new_inode) d_delete(new_dentry); nfs_begin_data_update(old_dir); nfs_begin_data_update(new_dir); nfs_begin_data_update(old_inode); error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); nfs_end_data_update(old_inode); nfs_end_data_update(new_dir); nfs_end_data_update(old_dir); out: if (rehash) d_rehash(rehash); if (!error) { if (!S_ISDIR(old_inode->i_mode)) d_move(old_dentry, new_dentry); nfs_renew_times(new_dentry); nfs_set_verifier(new_dentry, nfs_save_change_attribute(new_dir)); } /* new dentry created? */ if (dentry) dput(dentry); unlock_kernel(); return error; }
/* * check that a dentry lookup hit has found a valid entry * - NOTE! the hit can be a negative hit too, so we can't assume we have an inode * (derived from nfs_lookup_revalidate) */ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { struct afs_dir_lookup_cookie cookie; struct dentry *parent; struct inode *inode, *dir; unsigned fpos; int ret; _enter("{sb=%p n=%s},",dentry->d_sb,dentry->d_name.name); /* lock down the parent dentry so we can peer at it */ parent = dget_parent(dentry->d_parent); dir = parent->d_inode; inode = dentry->d_inode; /* handle a negative inode */ if (!inode) goto out_bad; /* handle a bad inode */ if (is_bad_inode(inode)) { printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", dentry->d_parent->d_name.name,dentry->d_name.name); goto out_bad; } /* force a full look up if the parent directory changed since last the server was consulted * - otherwise this inode must still exist, even if the inode details themselves have * changed */ if (AFS_FS_I(dir)->flags & AFS_VNODE_CHANGED) afs_vnode_fetch_status(AFS_FS_I(dir)); if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) { _debug("%s: parent dir deleted",dentry->d_name.name); goto out_bad; } if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) { _debug("%s: file already deleted",dentry->d_name.name); goto out_bad; } if ((unsigned long)dentry->d_fsdata != (unsigned long)AFS_FS_I(dir)->status.version) { _debug("%s: parent changed %lu -> %u", dentry->d_name.name, (unsigned long)dentry->d_fsdata, (unsigned)AFS_FS_I(dir)->status.version); /* search the directory for this vnode */ cookie.name = dentry->d_name.name; cookie.nlen = dentry->d_name.len; cookie.fid.vid = AFS_FS_I(inode)->volume->vid; cookie.found = 0; fpos = 0; ret = afs_dir_iterate(dir,&fpos,&cookie,afs_dir_lookup_filldir); if (ret<0) { _debug("failed to iterate dir %s: %d",parent->d_name.name,ret); goto out_bad; } if (!cookie.found) { _debug("%s: dirent not found",dentry->d_name.name); goto not_found; } /* if the vnode ID has changed, then the dirent points to a different file */ if (cookie.fid.vnode!=AFS_FS_I(inode)->fid.vnode) { _debug("%s: dirent changed",dentry->d_name.name); goto not_found; } /* if the vnode ID uniqifier has changed, then the file has been deleted */ if (cookie.fid.unique!=AFS_FS_I(inode)->fid.unique) { _debug("%s: file deleted (uq %u -> %u I:%lu)", dentry->d_name.name, cookie.fid.unique, AFS_FS_I(inode)->fid.unique, inode->i_version); spin_lock(&AFS_FS_I(inode)->lock); AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED; spin_unlock(&AFS_FS_I(inode)->lock); invalidate_remote_inode(inode); goto out_bad; } dentry->d_fsdata = (void*) (unsigned long) AFS_FS_I(dir)->status.version; } out_valid: dput(parent); _leave(" = 1 [valid]"); return 1; /* the dirent, if it exists, now points to a different vnode */ not_found: dentry->d_flags |= DCACHE_NFSFS_RENAMED; out_bad: if (inode) { /* don't unhash if we have submounts */ if (have_submounts(dentry)) goto out_valid; } shrink_dcache_parent(dentry); _debug("dropping dentry %s/%s",dentry->d_parent->d_name.name,dentry->d_name.name); d_drop(dentry); dput(parent); _leave(" = 0 [bad]"); return 0; } /* end afs_d_revalidate() */