📄 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/time.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>#include <linux/pagevec.h>#include <linux/namei.h>#include <linux/mount.h>#include <linux/sched.h>#include "nfs4_fs.h"#include "delegation.h"#include "iostat.h"#include "internal.h"/* #define NFS_DEBUG_VERBOSE 1 */static int nfs_opendir(struct inode *, struct file *);static int nfs_readdir(struct file *, void *, filldir_t);static struct dentry *nfs_lookup(struct inode *, struct dentry *, struct nameidata *);static int nfs_create(struct inode *, struct dentry *, int, struct nameidata *);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, dev_t);static int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);static int nfs_fsync_dir(struct file *, struct dentry *, int);static loff_t nfs_llseek_dir(struct file *, loff_t, int);const struct file_operations nfs_dir_operations = { .llseek = nfs_llseek_dir, .read = generic_read_dir, .readdir = nfs_readdir, .open = nfs_opendir, .release = nfs_release, .fsync = nfs_fsync_dir,};const 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, .getattr = nfs_getattr, .setattr = nfs_setattr,};#ifdef CONFIG_NFS_V3const struct inode_operations nfs3_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, .getattr = nfs_getattr, .setattr = nfs_setattr, .listxattr = nfs3_listxattr, .getxattr = nfs3_getxattr, .setxattr = nfs3_setxattr, .removexattr = nfs3_removexattr,};#endif /* CONFIG_NFS_V3 */#ifdef CONFIG_NFS_V4static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *);const struct inode_operations nfs4_dir_inode_operations = { .create = nfs_create, .lookup = nfs_atomic_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, .getattr = nfs_getattr, .setattr = nfs_setattr, .getxattr = nfs4_getxattr, .setxattr = nfs4_setxattr, .listxattr = nfs4_listxattr,};#endif /* CONFIG_NFS_V4 *//* * Open file */static intnfs_opendir(struct inode *inode, struct file *filp){ int res; dfprintk(VFS, "NFS: opendir(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); lock_kernel(); /* Call generic open code in order to cache credentials */ res = nfs_open(inode, filp); unlock_kernel(); return res;}typedef __be32 * (*decode_dirent_t)(__be32 *, struct nfs_entry *, int);typedef struct { struct file *file; struct page *page; unsigned long page_index; __be32 *ptr; u64 *dir_cookie; loff_t current_index; struct nfs_entry *entry; decode_dirent_t decode; int plus; int error; unsigned long timestamp; int timestamp_valid;} 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_path.dentry->d_inode; struct rpc_cred *cred = nfs_file_cred(file); unsigned long timestamp; int error; dfprintk(DIRCACHE, "NFS: %s: reading cookie %Lu into page %lu\n", __FUNCTION__, (long long)desc->entry->cookie, page->index); again: timestamp = jiffies; error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, desc->entry->cookie, page, NFS_SERVER(inode)->dtsize, desc->plus); if (error < 0) { /* We requested READDIRPLUS, but the server doesn't grok it */ if (error == -ENOTSUPP && desc->plus) { NFS_SERVER(inode)->caps &= ~NFS_CAP_READDIRPLUS; clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode)); desc->plus = 0; goto again; } goto error; } desc->timestamp = timestamp; desc->timestamp_valid = 1; SetPageUptodate(page); /* Ensure consistent page alignment of the data. * Note: assumes we have exclusive access to this mapping either * through inode->i_mutex or some other mechanism. */ if (page->index == 0 && invalidate_inode_pages2_range(inode->i_mapping, PAGE_CACHE_SIZE, -1) < 0) { /* Should never happen */ nfs_zap_mapping(inode, inode->i_mapping); } unlock_page(page); return 0; error: unlock_page(page); desc->error = error; return -EIO;}static inlineint dir_decode(nfs_readdir_descriptor_t *desc){ __be32 *p = desc->ptr; p = desc->decode(p, desc->entry, desc->plus); if (IS_ERR(p)) return PTR_ERR(p); desc->ptr = p; if (desc->timestamp_valid) desc->entry->fattr->time_start = desc->timestamp; else desc->entry->fattr->valid &= ~NFS_ATTR_FATTR; 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 with cookie '*desc->dir_cookie'. * * 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 nfs_entry *entry = desc->entry; int loop_count = 0, status; while((status = dir_decode(desc)) == 0) { dfprintk(DIRCACHE, "NFS: %s: examining cookie %Lu\n", __FUNCTION__, (unsigned long long)entry->cookie); if (entry->prev_cookie == *desc->dir_cookie) break; if (loop_count++ > 200) { loop_count = 0; schedule(); } } return status;}/* * Given a pointer to a buffer that has already been filled by a call * to readdir, find the entry at offset 'desc->file->f_pos'. * * 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_index(nfs_readdir_descriptor_t *desc){ struct nfs_entry *entry = desc->entry; int loop_count = 0, status; for(;;) { status = dir_decode(desc); if (status) break; dfprintk(DIRCACHE, "NFS: found cookie %Lu at index %Ld\n", (unsigned long long)entry->cookie, desc->current_index); if (desc->file->f_pos == desc->current_index) { *desc->dir_cookie = entry->cookie; break; } desc->current_index++; if (loop_count++ > 200) { loop_count = 0; schedule(); } } return status;}/* * Find the given page, and call find_dirent() or find_dirent_index 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_path.dentry->d_inode; struct page *page; int status; dfprintk(DIRCACHE, "NFS: %s: searching page %ld for target %Lu\n", __FUNCTION__, desc->page_index, (long long) *desc->dir_cookie); /* If we find the page in the page_cache, we cannot be sure * how fresh the data is, so we will ignore readdir_plus attributes. */ desc->timestamp_valid = 0; page = read_cache_page(inode->i_mapping, desc->page_index, (filler_t *)nfs_readdir_filler, desc); if (IS_ERR(page)) { status = PTR_ERR(page); goto out; } /* NOTE: Someone else may have changed the READDIRPLUS flag */ desc->page = page; desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ if (*desc->dir_cookie != 0) status = find_dirent(desc); else status = find_dirent_index(desc); if (status < 0) dir_page_release(desc); out: dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __FUNCTION__, status); return status;}/* * 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->dir_cookie' if non-0, * 'desc->file->f_pos' otherwise */static inlineint readdir_search_pagecache(nfs_readdir_descriptor_t *desc){ int loop_count = 0; int res; /* Always search-by-index from the beginning of the cache */ if (*desc->dir_cookie == 0) { dfprintk(DIRCACHE, "NFS: readdir_search_pagecache() searching for offset %Ld\n", (long long)desc->file->f_pos); desc->page_index = 0; desc->entry->cookie = desc->entry->prev_cookie = 0; desc->entry->eof = 0; desc->current_index = 0; } else dfprintk(DIRCACHE, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (unsigned long long)*desc->dir_cookie); 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(DIRCACHE, "NFS: %s: returns %d\n", __FUNCTION__, res); return res;}static inline unsigned int dt_type(struct inode *inode){ return (inode->i_mode >> 12) & 15;}static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc);/* * 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; struct dentry *dentry = NULL; u64 fileid; int loop_count = 0, res; dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (unsigned long long)entry->cookie); for(;;) { unsigned d_type = DT_UNKNOWN; /* Note: entry->prev_cookie contains the cookie for * retrieving the current dirent on the server */ fileid = entry->ino; /* Get a dentry if we have one */ if (dentry != NULL) dput(dentry); dentry = nfs_readdir_lookup(desc); /* Use readdirplus info */ if (dentry != NULL && dentry->d_inode != NULL) { d_type = dt_type(dentry->d_inode); fileid = NFS_FILEID(dentry->d_inode); } res = filldir(dirent, entry->name, entry->len, file->f_pos, nfs_compat_user_ino64(fileid), d_type); if (res < 0) break; file->f_pos++; *desc->dir_cookie = entry->cookie; if (dir_decode(desc) != 0) { desc->page_index ++; break; } if (loop_count++ > 200) { loop_count = 0; schedule(); } } dir_page_release(desc); if (dentry != NULL) dput(dentry); dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (unsigned long long)*desc->dir_cookie, 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_path.dentry->d_inode; struct rpc_cred *cred = nfs_file_cred(file); struct page *page = NULL; int status; unsigned long timestamp; dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n", (unsigned long long)*desc->dir_cookie); page = alloc_page(GFP_HIGHUSER); if (!page) { status = -ENOMEM; goto out; } timestamp = jiffies; desc->error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, *desc->dir_cookie, page, NFS_SERVER(inode)->dtsize, desc->plus); desc->page = page;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -