📄 dir.c
字号:
/* * linux/fs/nfs/dir.c * * Copyright (C) 1992 Rick Sladkey * * nfs directory handling functions * * 10 Apr 1996 Added silly rename for unlink --okir * 28 Sep 1996 Improved directory cache --okir * 23 Aug 1997 Claus Heine claus@momo.math.rwth-aachen.de * Re-implemented silly rename for unlink, newly implemented * silly rename for nfs_rename() following the suggestions * of Olaf Kirch (okir) found in this file. * Following Linus comments on my original hack, this version * depends only on the dcache stuff and doesn't touch the inode * layer (iput() and friends). * 6 Jun 1999 Cache readdir lookups in the page cache. -DaveM */#include <linux/sched.h>#include <linux/errno.h>#include <linux/stat.h>#include <linux/fcntl.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/sunrpc/clnt.h>#include <linux/nfs_fs.h>#include <linux/nfs_mount.h>#include <linux/pagemap.h>#include <linux/smp_lock.h>#define NFS_PARANOIA 1/* #define NFS_DEBUG_VERBOSE 1 */static int nfs_readdir(struct file *, void *, filldir_t);static struct dentry *nfs_lookup(struct inode *, struct dentry *);static int nfs_create(struct inode *, struct dentry *, int);static int nfs_mkdir(struct inode *, struct dentry *, int);static int nfs_rmdir(struct inode *, struct dentry *);static int nfs_unlink(struct inode *, struct dentry *);static int nfs_symlink(struct inode *, struct dentry *, const char *);static int nfs_link(struct dentry *, struct inode *, struct dentry *);static int nfs_mknod(struct inode *, struct dentry *, int, int);static int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);struct file_operations nfs_dir_operations = { read: generic_read_dir, readdir: nfs_readdir, open: nfs_open, release: nfs_release,};struct inode_operations nfs_dir_inode_operations = { create: nfs_create, lookup: nfs_lookup, link: nfs_link, unlink: nfs_unlink, symlink: nfs_symlink, mkdir: nfs_mkdir, rmdir: nfs_rmdir, mknod: nfs_mknod, rename: nfs_rename, permission: nfs_permission, revalidate: nfs_revalidate, setattr: nfs_notify_change,};typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);typedef struct { struct file *file; struct page *page; unsigned long page_index; u32 *ptr; u64 target; struct nfs_entry *entry; decode_dirent_t decode; int plus; int error;} nfs_readdir_descriptor_t;/* Now we cache directories properly, by stuffing the dirent * data directly in the page cache. * * Inode invalidation due to refresh etc. takes care of * _everything_, no sloppy entry flushing logic, no extraneous * copying, network direct to page cache, the way it was meant * to be. * * NOTE: Dirent information verification is done always by the * page-in of the RPC reply, nowhere else, this simplies * things substantially. */staticint nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page){ struct file *file = desc->file; struct inode *inode = file->f_dentry->d_inode; struct rpc_cred *cred = nfs_file_cred(file); void *buffer = kmap(page); int error; dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index); again: error = NFS_PROTO(inode)->readdir(inode, cred, desc->entry->cookie, buffer, NFS_SERVER(inode)->dtsize, desc->plus); /* We requested READDIRPLUS, but the server doesn't grok it */ if (desc->plus && error == -ENOTSUPP) { NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS; desc->plus = 0; goto again; } if (error < 0) goto error; SetPageUptodate(page); kunmap(page); /* Ensure consistent page alignment of the data. * Note: assumes we have exclusive access to this mapping either * throught inode->i_sem or some other mechanism. */ if (page->index == 0) invalidate_inode_pages(inode); UnlockPage(page); return 0; error: SetPageError(page); kunmap(page); UnlockPage(page); invalidate_inode_pages(inode); desc->error = error; return -EIO;}static inlineint dir_decode(nfs_readdir_descriptor_t *desc){ u32 *p = desc->ptr; p = desc->decode(p, desc->entry, desc->plus); if (IS_ERR(p)) return PTR_ERR(p); desc->ptr = p; return 0;}static inlinevoid dir_page_release(nfs_readdir_descriptor_t *desc){ kunmap(desc->page); page_cache_release(desc->page); desc->page = NULL; desc->ptr = NULL;}/* * Given a pointer to a buffer that has already been filled by a call * to readdir, find the next entry. * * If the end of the buffer has been reached, return -EAGAIN, if not, * return the offset within the buffer of the next entry to be * read. */static inlineint find_dirent(nfs_readdir_descriptor_t *desc, struct page *page){ struct nfs_entry *entry = desc->entry; int loop_count = 0, status; while((status = dir_decode(desc)) == 0) { dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie); if (entry->prev_cookie == desc->target) break; if (loop_count++ > 200) { loop_count = 0; schedule(); } } dfprintk(VFS, "NFS: find_dirent() returns %d\n", status); return status;}/* * Find the given page, and call find_dirent() in order to try to * return the next entry. */static inlineint find_dirent_page(nfs_readdir_descriptor_t *desc){ struct inode *inode = desc->file->f_dentry->d_inode; struct page *page; int status; dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index); desc->plus = NFS_USE_READDIRPLUS(inode); page = read_cache_page(&inode->i_data, desc->page_index, (filler_t *)nfs_readdir_filler, desc); if (IS_ERR(page)) { status = PTR_ERR(page); goto out; } if (!Page_Uptodate(page)) goto read_error; /* NOTE: Someone else may have changed the READDIRPLUS flag */ desc->page = page; desc->ptr = kmap(page); status = find_dirent(desc, page); if (status < 0) dir_page_release(desc); out: dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status); return status; read_error: page_cache_release(page); return -EIO;}/* * Recurse through the page cache pages, and return a * filled nfs_entry structure of the next directory entry if possible. * * The target for the search is 'desc->target'. */static inlineint readdir_search_pagecache(nfs_readdir_descriptor_t *desc){ int loop_count = 0; int res; dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target); for (;;) { res = find_dirent_page(desc); if (res != -EAGAIN) break; /* Align to beginning of next page */ desc->page_index ++; if (loop_count++ > 200) { loop_count = 0; schedule(); } } dfprintk(VFS, "NFS: readdir_search_pagecache() returned %d\n", res); return res;}/* * Once we've found the start of the dirent within a page: fill 'er up... */static int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, filldir_t filldir){ struct file *file = desc->file; struct nfs_entry *entry = desc->entry; unsigned long fileid; int loop_count = 0, res; dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target); for(;;) { /* Note: entry->prev_cookie contains the cookie for * retrieving the current dirent on the server */ fileid = nfs_fileid_to_ino_t(entry->ino); res = filldir(dirent, entry->name, entry->len, entry->prev_cookie, fileid, DT_UNKNOWN); if (res < 0) break; file->f_pos = desc->target = entry->cookie; if (dir_decode(desc) != 0) { desc->page_index ++; break; } if (loop_count++ > 200) { loop_count = 0; schedule(); } } dir_page_release(desc); dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res); return res;}/* * If we cannot find a cookie in our cache, we suspect that this is * because it points to a deleted file, so we ask the server to return * whatever it thinks is the next entry. We then feed this to filldir. * If all goes well, we should then be able to find our way round the * cache on the next call to readdir_search_pagecache(); * * NOTE: we cannot add the anonymous page to the pagecache because * the data it contains might not be page aligned. Besides, * we should already have a complete representation of the * directory in the page cache by the time we get here. */static inlineint uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, filldir_t filldir){ struct file *file = desc->file; struct inode *inode = file->f_dentry->d_inode; struct rpc_cred *cred = nfs_file_cred(file); struct page *page = NULL; int status; dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target); page = alloc_page(GFP_HIGHUSER); if (!page) { status = -ENOMEM; goto out; } desc->page = page; desc->ptr = kmap(page); desc->error = NFS_PROTO(inode)->readdir(inode, cred, desc->target, desc->ptr, NFS_SERVER(inode)->dtsize, desc->plus); if (desc->error >= 0) { if ((status = dir_decode(desc)) == 0) desc->entry->prev_cookie = desc->target; } 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; memset(desc->entry, 0, sizeof(*desc->entry)); out: dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status); return status; out_release: dir_page_release(desc); goto out;}/* The file offset position is now represented as a true offset into the * page cache as is the case in most of the other filesystems. */static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir){ struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; nfs_readdir_descriptor_t my_desc, *desc = &my_desc; struct nfs_entry my_entry; long res; res = nfs_revalidate(dentry); if (res < 0) return res; /* * filp->f_pos points to the file offset in the page cache. * but if the cache has meanwhile been zapped, we need to * read from the last dirent to revalidate f_pos * itself. */ memset(desc, 0, sizeof(*desc)); memset(&my_entry, 0, sizeof(my_entry)); desc->file = filp; desc->target = filp->f_pos; desc->entry = &my_entry; desc->decode = NFS_PROTO(inode)->decode_dirent; while(!desc->entry->eof) { res = readdir_search_pagecache(desc); if (res == -EBADCOOKIE) { /* This means either end of directory */ if (desc->entry->cookie != desc->target) { /* Or that the server has 'lost' a cookie */ res = uncached_readdir(desc, dirent, filldir); if (res >= 0) continue; } res = 0; break; } else if (res < 0) break; res = nfs_do_filldir(desc, dirent, filldir); if (res < 0) { res = 0; break; } } if (desc->error < 0) return desc->error; if (res < 0) return res; return 0;}/* * Whenever an NFS operation succeeds, we know that the dentry * is valid, so we update the revalidation timestamp. */static inline void nfs_renew_times(struct dentry * dentry){ dentry->d_time = jiffies;}static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags){ struct inode *inode = dentry->d_inode; unsigned long timeout = NFS_ATTRTIMEO(inode); /* * If it's the last lookup in a series, we use a stricter * cache consistency check by looking at the parent mtime. * * If it's been modified in the last hour, be really strict. * (This still means that we can avoid doing unnecessary * work on directories like /usr/share/bin etc which basically * never change). */ if (!(flags & LOOKUP_CONTINUE)) { long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime; if (diff < 15*60) timeout = 0; } return time_after(jiffies,dentry->d_time + timeout);}/* * We judge how long we want to trust negative * dentries by looking at the parent inode mtime. * * If mtime is close to present time, we revalidate * more often. */#define NFS_REVALIDATE_NEGATIVE (1 * HZ)static inline int nfs_neg_need_reval(struct dentry *dentry){ struct inode *dir = dentry->d_parent->d_inode; unsigned long timeout = NFS_ATTRTIMEO(dir); long diff = CURRENT_TIME - dir->i_mtime; if (diff < 5*60 && timeout > NFS_REVALIDATE_NEGATIVE) timeout = NFS_REVALIDATE_NEGATIVE; return time_after(jiffies, dentry->d_time + timeout);}/* * 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 dentry is older than the revalidation interval, * we do a new lookup and verify that the dentry is still * correct. */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 we don't have an inode, let's look at the parent * directory mtime to get a hint about how often we * should validate things.. */ if (!inode) { if (nfs_neg_need_reval(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; } if (!nfs_dentry_force_reval(dentry, flags)) goto out_valid; if (IS_ROOT(dentry)) { __nfs_revalidate_inode(NFS_SERVER(inode), inode); goto out_valid_renew; } /* * Do a new lookup and check the dentry attributes. */ error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); if (error) goto out_bad; /* Inode number matches? */ if (!(fattr.valid & NFS_ATTR_FATTR) || NFS_FSID(inode) != fattr.fsid || NFS_FILEID(inode) != fattr.fileid) goto out_bad; /* Ok, remember that we successfully checked it.. */ nfs_refresh_inode(inode, &fattr); if (nfs_inode_is_stale(inode, &fhandle, &fattr)) goto out_bad; out_valid_renew: nfs_renew_times(dentry);out_valid: unlock_kernel(); return 1;out_bad: shrink_dcache_parent(dentry); /* If we have submounts, don't unhash ! */ if (have_submounts(dentry)) goto out_valid; d_drop(dentry); /* Purge readdir caches. */ nfs_zap_caches(dir); if (inode && S_ISDIR(inode->i_mode)) nfs_zap_caches(inode); unlock_kernel(); 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; } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -