⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dir.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  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 + -