/** * nilfs_try_to_shrink_tree() - 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 int nilfs_try_to_shrink_tree(struct dentry *root_dentry) { if (have_submounts(root_dentry)) return true; shrink_dcache_parent(root_dentry); return nilfs_tree_was_touched(root_dentry); }
/* * 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, int flags) { struct inode *dir; struct inode *inode; int error; struct nfs_fh fhandle; struct nfs_fattr fattr; lock_kernel(); dir = dentry->d_parent->d_inode; inode = dentry->d_inode; if (!inode) { if (nfs_neg_need_reval(dir, dentry)) 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; } /* Force a full look up iff the parent directory has changed */ if (nfs_check_verifier(dir, dentry)) { if (nfs_lookup_verify_inode(inode, flags)) goto out_bad; goto out_valid; } 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; nfs_renew_times(dentry); out_valid: unlock_kernel(); return 1; 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(); return 0; }
/* * Check if the given path is a mountpoint. * * If we are supplied with the file descriptor of an autofs * mount we're looking for a specific mount. In this case * the path is considered a mountpoint if it is itself a * mountpoint or contains a mount, such as a multi-mount * without a root mount. In this case we return 1 if the * path is a mount point and the super magic of the covering * mount if there is one or 0 if it isn't a mountpoint. * * If we aren't supplied with a file descriptor then we * lookup the nameidata of the path and check if it is the * root of a mount. If a type is given we are looking for * a particular autofs mount and if we don't find a match * we return fail. If the located nameidata path is the * root of a mount we return 1 along with the super magic * of the mount or 0 otherwise. * * In both cases the the device number (as returned by * new_encode_dev()) is also returned. */ static int autofs_dev_ioctl_ismountpoint(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { struct path path; const char *name; unsigned int type; unsigned int devid, magic; int err = -ENOENT; if (param->size <= sizeof(*param)) { err = -EINVAL; goto out; } name = param->path; type = param->ismountpoint.in.type; param->ismountpoint.out.devid = devid = 0; param->ismountpoint.out.magic = magic = 0; if (!fp || param->ioctlfd == -1) { if (autofs_type_any(type)) err = kern_path(name, LOOKUP_FOLLOW, &path); else err = find_autofs_mount(name, &path, test_by_type, &type); if (err) goto out; devid = new_encode_dev(path.mnt->mnt_sb->s_dev); err = 0; if (path.dentry->d_inode && path.mnt->mnt_root == path.dentry) { err = 1; magic = path.dentry->d_inode->i_sb->s_magic; } } else { dev_t dev = sbi->sb->s_dev; err = find_autofs_mount(name, &path, test_by_dev, &dev); if (err) goto out; devid = new_encode_dev(dev); err = have_submounts(path.dentry); if (path.mnt->mnt_mountpoint != path.mnt->mnt_root) { if (follow_down(&path)) magic = path.mnt->mnt_sb->s_magic; } } param->ismountpoint.out.devid = devid; param->ismountpoint.out.magic = magic; path_put(&path); out: return err; }
int have_submounts_init(void) { struct dentry *dentry_parent = current->fs->pwd.dentry->d_parent; int data = have_submounts(dentry_parent); if(data==0) printk("<0> Current dentry has not munt.\n"); else printk("<0> Current dentry has munt.\n"); return 0; }
static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd) { struct sysfs_dirent *sd = dentry->d_fsdata; int is_dir; mutex_lock(&sysfs_mutex); /* The sysfs dirent has been deleted */ if (sd->s_flags & SYSFS_FLAG_REMOVED) goto out_bad; /* The sysfs dirent has been moved? */ if (dentry->d_parent->d_fsdata != sd->s_parent) goto out_bad; /* The sysfs dirent has been renamed */ if (strcmp(dentry->d_name.name, sd->s_name) != 0) goto out_bad; mutex_unlock(&sysfs_mutex); out_valid: return 1; out_bad: /* Remove the dentry from the dcache hashes. * If this is a deleted dentry we use d_drop instead of d_delete * so sysfs doesn't need to cope with negative dentries. * * If this is a dentry that has simply been renamed we * use d_drop to remove it from the dcache lookup on its * old parent. If this dentry persists later when a lookup * is performed at its new name the dentry will be readded * to the dcache hashes. */ is_dir = (sysfs_type(sd) == SYSFS_DIR); mutex_unlock(&sysfs_mutex); if (is_dir) { /* If we have submounts we must allow the vfs caches * to lie about the state of the filesystem to prevent * leaks and other nasty things. */ if (have_submounts(dentry)) goto out_valid; shrink_dcache_parent(dentry); } d_drop(dentry); return 0; }
/* * 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 */ 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; if (nd->flags & LOOKUP_RCU) return -ECHILD; 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:%u)", dentry->d_name.name, fid.unique, vnode->fid.unique, dentry->d_inode->i_generation); 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; }
static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) { struct dentry *parent; struct gfs2_sbd *sdp; struct gfs2_inode *dip; struct inode *inode; struct gfs2_holder d_gh; struct gfs2_inode *ip = NULL; int error; int had_lock = 0; if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; parent = dget_parent(dentry); sdp = GFS2_SB(parent->d_inode); dip = GFS2_I(parent->d_inode); inode = dentry->d_inode; if (inode) { if (is_bad_inode(inode)) goto invalid; ip = GFS2_I(inode); } if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) 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; }
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; struct gfs2_inum_host inum; unsigned int type; int error; int had_lock=0; if (inode && is_bad_inode(inode)) goto invalid; if (sdp->sd_args.ar_localcaching) goto valid; had_lock = gfs2_glock_is_locked_by_me(dip->i_gl); if (!had_lock) { error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); if (error) goto fail; } error = gfs2_dir_search(parent->d_inode, &dentry->d_name, &inum, &type); switch (error) { case 0: if (!inode) goto invalid_gunlock; break; case -ENOENT: if (!inode) goto valid_gunlock; goto invalid_gunlock; default: goto fail_gunlock; } ip = GFS2_I(inode); if (!gfs2_inum_equal(&ip->i_num, &inum)) goto invalid_gunlock; if (IF2DT(ip->i_inode.i_mode) != type) { gfs2_consist_inode(dip); 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; }
/* * 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 dentry */ 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: spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NFSFS_RENAMED; spin_unlock(&dentry->d_lock); 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() */
/* * Check if we have a valid request. * Returns * 1 if the request should continue. * In this case we can return an autofs_wait_queue entry if one is * found or NULL to idicate a new wait needs to be created. * 0 or a negative errno if the request shouldn't continue. */ static int validate_request(struct autofs_wait_queue **wait, struct autofs_sb_info *sbi, struct qstr *qstr, struct dentry*dentry, enum autofs_notify notify) { struct autofs_wait_queue *wq; struct autofs_info *ino; /* Wait in progress, continue; */ wq = autofs4_find_wait(sbi, qstr); if (wq) { *wait = wq; return 1; } *wait = NULL; /* If we don't yet have any info this is a new request */ ino = autofs4_dentry_ino(dentry); if (!ino) return 1; /* * If we've been asked to wait on an existing expire (NFY_NONE) * but there is no wait in the queue ... */ if (notify == NFY_NONE) { /* * Either we've betean the pending expire to post it's * wait or it finished while we waited on the mutex. * So we need to wait till either, the wait appears * or the expire finishes. */ while (ino->flags & AUTOFS_INF_EXPIRING) { mutex_unlock(&sbi->wq_mutex); schedule_timeout_interruptible(HZ/10); if (mutex_lock_interruptible(&sbi->wq_mutex)) return -EINTR; wq = autofs4_find_wait(sbi, qstr); if (wq) { *wait = wq; return 1; } } /* * Not ideal but the status has already gone. Of the two * cases where we wait on NFY_NONE neither depend on the * return status of the wait. */ return 0; } /* * If we've been asked to trigger a mount and the request * completed while we waited on the mutex ... */ if (notify == NFY_MOUNT) { /* * If the dentry was successfully mounted while we slept * on the wait queue mutex we can return success. If it * isn't mounted (doesn't have submounts for the case of * a multi-mount with no mount at it's base) we can * continue on and create a new request. */ if (have_submounts(dentry)) return 0; } return 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; }
/* * Check if the given path is a mountpoint. * * If we are supplied with the file descriptor of an autofs * mount we're looking for a specific mount. In this case * the path is considered a mountpoint if it is itself a * mountpoint or contains a mount, such as a multi-mount * without a root mount. In this case we return 1 if the * path is a mount point and the super magic of the covering * mount if there is one or 0 if it isn't a mountpoint. * * If we aren't supplied with a file descriptor then we * lookup the nameidata of the path and check if it is the * root of a mount. If a type is given we are looking for * a particular autofs mount and if we don't find a match * we return fail. If the located nameidata path is the * root of a mount we return 1 along with the super magic * of the mount or 0 otherwise. * * In both cases the the device number (as returned by * new_encode_dev()) is also returned. */ static int autofs_dev_ioctl_ismountpoint(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { struct nameidata nd; const char *path; unsigned int type; int err = -ENOENT; if (param->size <= sizeof(*param)) { err = -EINVAL; goto out; } path = param->path; type = param->arg1; param->arg1 = 0; param->arg2 = 0; if (!fp || param->ioctlfd == -1) { if (type == AUTOFS_TYPE_ANY) { struct super_block *sb; err = path_lookup(path, LOOKUP_FOLLOW, &nd); if (err) goto out; sb = nd.path.dentry->d_sb; param->arg1 = new_encode_dev(sb->s_dev); } else { struct autofs_info *ino; err = path_lookup(path, LOOKUP_PARENT, &nd); if (err) goto out; err = autofs_dev_ioctl_find_sbi_type(&nd, type); if (err) goto out_release; ino = autofs4_dentry_ino(nd.path.dentry); param->arg1 = autofs4_get_dev(ino->sbi); } err = 0; if (nd.path.dentry->d_inode && nd.path.mnt->mnt_root == nd.path.dentry) { err = 1; param->arg2 = nd.path.dentry->d_inode->i_sb->s_magic; } } else { dev_t devid = new_encode_dev(sbi->sb->s_dev); err = path_lookup(path, LOOKUP_PARENT, &nd); if (err) goto out; err = autofs_dev_ioctl_find_super(&nd, devid); if (err) goto out_release; param->arg1 = autofs4_get_dev(sbi); err = have_submounts(nd.path.dentry); if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { if (follow_down(&nd.path.mnt, &nd.path.dentry)) { struct inode *inode = nd.path.dentry->d_inode; param->arg2 = inode->i_sb->s_magic; } } } out_release: path_put(&nd.path); out: return err; }
/* * 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; }