📄 dir.c
字号:
desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ if (desc->error >= 0) { desc->timestamp = timestamp; desc->timestamp_valid = 1; if ((status = dir_decode(desc)) == 0) desc->entry->prev_cookie = *desc->dir_cookie; } else status = -EIO; if (status < 0) goto out_release; status = nfs_do_filldir(desc, dirent, filldir); /* Reset read descriptor so it searches the page cache from * the start upon the next call to readdir_search_pagecache() */ desc->page_index = 0; desc->entry->cookie = desc->entry->prev_cookie = 0; desc->entry->eof = 0; out: dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __FUNCTION__, status); return status; out_release: dir_page_release(desc); goto out;}/* The file offset position represents the dirent entry number. A last cookie cache takes care of the common case of reading the whole directory. */static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir){ struct dentry *dentry = filp->f_path.dentry; struct inode *inode = dentry->d_inode; nfs_readdir_descriptor_t my_desc, *desc = &my_desc; struct nfs_entry my_entry; struct nfs_fh fh; struct nfs_fattr fattr; long res; dfprintk(VFS, "NFS: readdir(%s/%s) starting at cookie %Lu\n", dentry->d_parent->d_name.name, dentry->d_name.name, (long long)filp->f_pos); nfs_inc_stats(inode, NFSIOS_VFSGETDENTS); lock_kernel(); res = nfs_revalidate_mapping_nolock(inode, filp->f_mapping); if (res < 0) { unlock_kernel(); return res; } /* * filp->f_pos points to the dirent entry number. * *desc->dir_cookie has the cookie for the next entry. We have * to either find the entry with the appropriate number or * revalidate the cookie. */ memset(desc, 0, sizeof(*desc)); desc->file = filp; desc->dir_cookie = &nfs_file_open_context(filp)->dir_cookie; desc->decode = NFS_PROTO(inode)->decode_dirent; desc->plus = NFS_USE_READDIRPLUS(inode); my_entry.cookie = my_entry.prev_cookie = 0; my_entry.eof = 0; my_entry.fh = &fh; my_entry.fattr = &fattr; nfs_fattr_init(&fattr); desc->entry = &my_entry; nfs_block_sillyrename(dentry); while(!desc->entry->eof) { res = readdir_search_pagecache(desc); if (res == -EBADCOOKIE) { /* This means either end of directory */ if (*desc->dir_cookie && desc->entry->cookie != *desc->dir_cookie) { /* Or that the server has 'lost' a cookie */ res = uncached_readdir(desc, dirent, filldir); if (res >= 0) continue; } res = 0; break; } if (res == -ETOOSMALL && desc->plus) { clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode)); nfs_zap_caches(inode); desc->plus = 0; desc->entry->eof = 0; continue; } if (res < 0) break; res = nfs_do_filldir(desc, dirent, filldir); if (res < 0) { res = 0; break; } } nfs_unblock_sillyrename(dentry); unlock_kernel(); if (res > 0) res = 0; dfprintk(VFS, "NFS: readdir(%s/%s) returns %ld\n", dentry->d_parent->d_name.name, dentry->d_name.name, res); return res;}static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin){ mutex_lock(&filp->f_path.dentry->d_inode->i_mutex); switch (origin) { case 1: offset += filp->f_pos; case 0: if (offset >= 0) break; default: offset = -EINVAL; goto out; } if (offset != filp->f_pos) { filp->f_pos = offset; nfs_file_open_context(filp)->dir_cookie = 0; }out: mutex_unlock(&filp->f_path.dentry->d_inode->i_mutex); return offset;}/* * All directory operations under NFS are synchronous, so fsync() * is a dummy operation. */static int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync){ dfprintk(VFS, "NFS: fsync_dir(%s/%s) datasync %d\n", dentry->d_parent->d_name.name, dentry->d_name.name, datasync); return 0;}/* * A check for whether or not the parent directory has changed. * In the case it has, we assume that the dentries are untrustworthy * and may need to be looked up again. */static int nfs_check_verifier(struct inode *dir, struct dentry *dentry){ if (IS_ROOT(dentry)) return 1; if (!nfs_verify_change_attribute(dir, dentry->d_time)) return 0; /* Revalidate nfsi->cache_change_attribute before we declare a match */ if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0) return 0; if (!nfs_verify_change_attribute(dir, dentry->d_time)) return 0; return 1;}/* * Return the intent data that applies to this particular path component * * Note that the current set of intents only apply to the very last * component of the path. * We check for this using LOOKUP_CONTINUE and LOOKUP_PARENT. */static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, unsigned int mask){ if (nd->flags & (LOOKUP_CONTINUE|LOOKUP_PARENT)) return 0; return nd->flags & mask;}/* * Use intent information to check whether or not we're going to do * an O_EXCL create using this path component. */static int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd){ if (NFS_PROTO(dir)->version == 2) return 0; if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_CREATE) == 0) return 0; return (nd->intent.open.flags & O_EXCL) != 0;}/* * Inode and filehandle revalidation for lookups. * * We force revalidation in the cases where the VFS sets LOOKUP_REVAL, * or if the intent information indicates that we're about to open this * particular file and the "nocto" mount flag is not set. * */static inlineint nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd){ struct nfs_server *server = NFS_SERVER(inode); if (nd != NULL) { /* VFS wants an on-the-wire revalidation */ if (nd->flags & LOOKUP_REVAL) goto out_force; /* This is an open(2) */ if (nfs_lookup_check_intent(nd, LOOKUP_OPEN) != 0 && !(server->flags & NFS_MOUNT_NOCTO) && (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) goto out_force; return 0; } return nfs_revalidate_inode(server, inode);out_force: return __nfs_revalidate_inode(server, inode);}/* * We judge how long we want to trust negative * dentries by looking at the parent inode mtime. * * If parent mtime has changed, we revalidate, else we wait for a * period corresponding to the parent's attribute cache timeout value. */static inlineint nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, struct nameidata *nd){ /* Don't revalidate a negative dentry if we're creating a new file */ if (nd != NULL && nfs_lookup_check_intent(nd, LOOKUP_CREATE) != 0) return 0; return !nfs_check_verifier(dir, 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, struct nameidata *nd){ struct inode *dir; struct inode *inode; struct dentry *parent; int error; struct nfs_fh fhandle; struct nfs_fattr fattr; parent = dget_parent(dentry); lock_kernel(); dir = parent->d_inode; nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE); inode = dentry->d_inode; if (!inode) { if (nfs_neg_need_reval(dir, dentry, nd)) goto out_bad; goto out_valid; } if (is_bad_inode(inode)) { dfprintk(LOOKUPCACHE, "%s: %s/%s has dud inode\n", __FUNCTION__, 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_is_exclusive_create(dir, nd) && nfs_check_verifier(dir, dentry)) { if (nfs_lookup_verify_inode(inode, nd)) goto out_zap_parent; 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 (nfs_compare_fh(NFS_FH(inode), &fhandle)) goto out_bad; if ((error = nfs_refresh_inode(inode, &fattr)) != 0) goto out_bad; nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); out_valid: unlock_kernel(); dput(parent); dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n", __FUNCTION__, dentry->d_parent->d_name.name, dentry->d_name.name); return 1;out_zap_parent: nfs_zap_caches(dir); out_bad: nfs_mark_for_revalidate(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); dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n", __FUNCTION__, dentry->d_parent->d_name.name, dentry->d_name.name); return 0;}/* * This is called from dput() when d_count is going to 0. */static int nfs_dentry_delete(struct dentry *dentry){ dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_flags); if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { /* Unhash it, so that ->d_iput() would be called */ return 1; } if (!(dentry->d_sb->s_flags & MS_ACTIVE)) { /* Unhash it, so that ancestors of killed async unlink * files will be cleaned up during umount */ return 1; } return 0;}/* * Called when the dentry loses inode. * We use it to clean up silly-renamed files. */static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode){ nfs_inode_return_delegation(inode); if (S_ISDIR(inode->i_mode)) /* drop any readdir cache as it could easily be old */ NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA; if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { lock_kernel(); drop_nlink(inode); nfs_complete_unlink(dentry, inode); unlock_kernel(); } iput(inode);}struct dentry_operations nfs_dentry_operations = { .d_revalidate = nfs_lookup_revalidate, .d_delete = nfs_dentry_delete, .d_iput = nfs_dentry_iput,};static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd){ struct dentry *res; struct dentry *parent; struct inode *inode = NULL; int error; struct nfs_fh fhandle; struct nfs_fattr fattr; dfprintk(VFS, "NFS: lookup(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); nfs_inc_stats(dir, NFSIOS_VFSLOOKUP); res = ERR_PTR(-ENAMETOOLONG); if (dentry->d_name.len > NFS_SERVER(dir)->namelen) goto out; res = ERR_PTR(-ENOMEM); dentry->d_op = NFS_PROTO(dir)->dentry_ops; lock_kernel(); /* * If we're doing an exclusive create, optimize away the lookup * but don't hash the dentry. */ if (nfs_is_exclusive_create(dir, nd)) { d_instantiate(dentry, NULL); res = NULL; goto out_unlock; } parent = dentry->d_parent; /* Protect against concurrent sillydeletes */ nfs_block_sillyrename(parent); error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); if (error == -ENOENT) goto no_entry; if (error < 0) { res = ERR_PTR(error); goto out_unblock_sillyrename; } inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr); res = (struct dentry *)inode; if (IS_ERR(res)) goto out_unblock_sillyrename;no_entry: res = d_materialise_unique(dentry, inode); if (res != NULL) { if (IS_ERR(res)) goto out_unblock_sillyrename; dentry = res; } nfs_set_verifier(dentry, nfs_save_change_attribute(dir));out_unblock_sillyrename: nfs_unblock_sillyrename(parent);out_unlock: unlock_kernel();out: return res;}#ifdef CONFIG_NFS_V4static int nfs_open_revalidate(struct dentry *, struct nameidata *);struct dentry_operations nfs4_dentry_operations = { .d_revalidate = nfs_open_revalidate, .d_delete = nfs_dentry_delete, .d_iput = nfs_dentry_iput,};/* * Use intent information to determine whether we need to substitute * the NFSv4-style stateful OPEN for the LOOKUP call */static int is_atomic_open(struct inode *dir, struct nameidata *nd){ if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_OPEN) == 0) return 0; /* NFS does not (yet) have a stateful open for directories */ if (nd->flags & LOOKUP_DIRECTORY) return 0; /* Are we trying to write to a read only partition? */ if (IS_RDONLY(dir) && (nd->intent.open.flags & (O_CREAT|O_TRUNC|FMODE_WRITE))) return 0; return 1;}static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){ struct dentry *res = NULL; int error; dfprintk(VFS, "NFS: atomic_lookup(%s/%ld), %s\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); /* Check that we are indeed trying to open this file */ if (!is_atomic_open(dir, nd)) goto no_open; if (dentry->d_name.len > NFS_SERVER(dir)->namelen) { res = ERR_PTR(-ENAMETOOLONG); goto out; } dentry->d_op = NFS_PROTO(dir)->dentry_ops; /* Let vfs_create() deal with O_EXCL. Instantiate, but don't hash * the dentry. */ if (nd->intent.open.flags & O_EXCL) { d_instantiate(dentry, NULL); goto out;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -