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

📄 nfsfh.c

📁 嵌入式系统设计与实例开发实验教材二源码 多线程应用程序设计 串行端口程序设计 AD接口实验 CAN总线通信实验 GPS通信实验 Linux内核移植与编译实验 IC卡读写实验 SD驱动使
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * linux/fs/nfsd/nfsfh.c * * NFS server file handle treatment. * * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org> * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999 */#include <linux/sched.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/unistd.h>#include <linux/string.h>#include <linux/stat.h>#include <linux/dcache.h>#include <asm/pgtable.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#define NFSDDBG_FACILITY		NFSDDBG_FH#define NFSD_PARANOIA 1/* #define NFSD_DEBUG_VERBOSE 1 */static int nfsd_nr_verified;static int nfsd_nr_put;struct nfsd_getdents_callback {	char *name;		/* name that was found. It already points to a buffer NAME_MAX+1 is size */	unsigned long ino;	/* the inum we are looking for */	int found;		/* inode matched? */	int sequence;		/* sequence counter */};/* * A rather strange filldir function to capture * the name matching the specified inode number. */static int filldir_one(void * __buf, const char * name, int len,			loff_t pos, ino_t ino, unsigned int d_type){	struct nfsd_getdents_callback *buf = __buf;	int result = 0;	buf->sequence++;#ifdef NFSD_DEBUG_VERBOSEdprintk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);#endif	if (buf->ino == ino) {		memcpy(buf->name, name, len);		buf->name[len] = '\0';		buf->found = 1;		result = -1;	}	return result;}/** * nfsd_get_name - default nfsd_operations->get_name function * @dentry: the directory in which to find a name * @name:   a pointer to a %NAME_MAX+1 char buffer to store the name * @child:  the dentry for the child directory. * * calls readdir on the parent until it finds an entry with * the same inode number as the child, and returns that. */static int nfsd_get_name(struct dentry *dentry, char *name,			struct dentry *child){	struct inode *dir = dentry->d_inode;	int error;	struct file file;	struct nfsd_getdents_callback buffer;	error = -ENOTDIR;	if (!dir || !S_ISDIR(dir->i_mode))		goto out;	error = -EINVAL;	if (!dir->i_fop)		goto out;	/*	 * Open the directory ...	 */	error = init_private_file(&file, dentry, FMODE_READ);	if (error)		goto out;	error = -EINVAL;	if (!file.f_op->readdir)		goto out_close;	buffer.name = name;	buffer.ino = child->d_inode->i_ino;	buffer.found = 0;	buffer.sequence = 0;	while (1) {		int old_seq = buffer.sequence;		error = vfs_readdir(&file, filldir_one, &buffer);		if (error < 0)			break;		error = 0;		if (buffer.found)			break;		error = -ENOENT;		if (old_seq == buffer.sequence)			break;	}out_close:	if (file.f_op->release)		file.f_op->release(dir, &file);out:	return error;}/* this should be provided by each filesystem in an nfsd_operations interface as * iget isn't really the right interface */static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation){	/* iget isn't really right if the inode is currently unallocated!!	 * This should really all be done inside each filesystem	 *	 * ext2fs' read_inode has been strengthed to return a bad_inode if the inode	 *   had been deleted.	 *	 * Currently we don't know the generation for parent directory, so a generation	 * of 0 means "accept any"	 */	struct inode *inode;	struct list_head *lp;	struct dentry *result;	if (ino == 0)		return ERR_PTR(-ESTALE);	inode = iget(sb, ino);	if (inode == NULL)		return ERR_PTR(-ENOMEM);	if (is_bad_inode(inode)	    || (generation && inode->i_generation != generation)		) {		/* we didn't find the right inode.. */		dprintk("fh_verify: Inode %lu, Bad count: %d %d or version  %u %u\n",			inode->i_ino,			inode->i_nlink, atomic_read(&inode->i_count),			inode->i_generation,			generation);		iput(inode);		return ERR_PTR(-ESTALE);	}	/* now to find a dentry.	 * If possible, get a well-connected one	 */	spin_lock(&dcache_lock);	for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {		result = list_entry(lp,struct dentry, d_alias);		if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {			dget_locked(result);			result->d_vfs_flags |= DCACHE_REFERENCED;			spin_unlock(&dcache_lock);			iput(inode);			return result;		}	}	spin_unlock(&dcache_lock);	result = d_alloc_root(inode);	if (result == NULL) {		iput(inode);		return ERR_PTR(-ENOMEM);	}	result->d_flags |= DCACHE_NFSD_DISCONNECTED;	return result;}static struct dentry *nfsd_get_dentry(struct super_block *sb, __u32 *fh,					     int len, int fhtype, int parent){	if (sb->s_op->fh_to_dentry)		return sb->s_op->fh_to_dentry(sb, fh, len, fhtype, parent);	switch (fhtype) {	case 1:		if (len < 2)			break;		if (parent)			break;		return nfsd_iget(sb, fh[0], fh[1]);	case 2:		if (len < 3)			break;		if (parent)			return nfsd_iget(sb,fh[2],0);		return nfsd_iget(sb,fh[0],fh[1]);	default: break;	}	return ERR_PTR(-EINVAL);}/* this routine links an IS_ROOT dentry into the dcache tree.  It gains "parent" * as a parent and "name" as a name * It should possibly go in dcache.c */int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name){	struct dentry *tdentry;#ifdef NFSD_PARANOIA	if (!IS_ROOT(target))		printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name);	if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED))		printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name);#endif	tdentry = d_alloc(parent, name);	if (tdentry == NULL)		return -ENOMEM;	d_move(target, tdentry);	/* tdentry will have been made a "child" of target (the parent of target)	 * make it an IS_ROOT instead	 */	spin_lock(&dcache_lock);	list_del_init(&tdentry->d_child);	tdentry->d_parent = tdentry;	spin_unlock(&dcache_lock);	d_rehash(target);	dput(tdentry);	/* if parent is properly connected, then we can assert that	 * the children are connected, but it must be a singluar (non-forking)	 * branch	 */	if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) {		while (target) {			target->d_flags &= ~DCACHE_NFSD_DISCONNECTED;			parent = target;			spin_lock(&dcache_lock);			if (list_empty(&parent->d_subdirs))				target = NULL;			else {				target = list_entry(parent->d_subdirs.next, struct dentry, d_child);#ifdef NFSD_PARANOIA				/* must be only child */				if (target->d_child.next != &parent->d_subdirs				    || target->d_child.prev != &parent->d_subdirs)					printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n",					       parent->d_name.name, target->d_name.name);#endif			}			spin_unlock(&dcache_lock);		}	}	return 0;}/* this routine finds the dentry of the parent of a given directory * it should be in the filesystem accessed by nfsd_operations * it assumes lookup("..") works. */struct dentry *nfsd_findparent(struct dentry *child){	struct dentry *tdentry, *pdentry;	tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0});	if (!tdentry)		return ERR_PTR(-ENOMEM);	/* I'm going to assume that if the returned dentry is different, then	 * it is well connected.  But nobody returns different dentrys do they?	 */	pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry);	d_drop(tdentry); /* we never want ".." hashed */	if (!pdentry && tdentry->d_inode == NULL) {		/* File system cannot find ".." ... sad but possible */		pdentry = ERR_PTR(-EINVAL);	}	if (!pdentry) {		/* I don't want to return a ".." dentry.		 * I would prefer to return an unconnected "IS_ROOT" dentry,		 * though a properly connected dentry is even better		 */		/* if first or last of alias list is not tdentry, use that		 * else make a root dentry		 */		struct list_head *aliases = &tdentry->d_inode->i_dentry;		spin_lock(&dcache_lock);		if (aliases->next != aliases) {			pdentry = list_entry(aliases->next, struct dentry, d_alias);			if (pdentry == tdentry)				pdentry = list_entry(aliases->prev, struct dentry, d_alias);			if (pdentry == tdentry)				pdentry = NULL;			if (pdentry) dget_locked(pdentry);		}		spin_unlock(&dcache_lock);		if (pdentry == NULL) {			pdentry = d_alloc_root(tdentry->d_inode);			if (pdentry) {				igrab(tdentry->d_inode);				pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED;			}		}		if (pdentry == NULL)			pdentry = ERR_PTR(-ENOMEM);	}	dput(tdentry); /* it is not hashed, it will be discarded */	return pdentry;}static struct dentry *splice(struct dentry *child, struct dentry *parent){	int err = 0, nerr;	struct qstr qs;	char namebuf[256];	struct list_head *lp;	/* child is an IS_ROOT (anonymous) dentry, but it is hypothesised that	 * it should be a child of parent.	 * We see if we can find a name and, if we can - splice it in.	 * We lookup the name before locking (i_sem) the directory as namelookup	 * also claims i_sem.  If the name gets changed then we will loop around	 * and try again in find_fh_dentry.	 */	nerr = nfsd_get_name(parent, namebuf, child);	/*	 * We now claim the parent i_sem so that no-one else tries to create	 * a dentry in the parent while we are.	 */		down(&parent->d_inode->i_sem);	/* Now, things might have changed while we waited.	 * Possibly a friendly filesystem found child and spliced it in in response	 * to a lookup (though nobody does this yet).  In this case, just succeed.	 */	if (child->d_parent == parent) goto out;		/* Possibly a new dentry has been made for this child->d_inode in	 * parent by a lookup.  In this case return that dentry. Caller must	 * notice and act accordingly	 */	spin_lock(&dcache_lock);	list_for_each(lp, &child->d_inode->i_dentry) {		struct dentry *tmp = list_entry(lp,struct dentry, d_alias);		if (!list_empty(&tmp->d_hash) &&		    tmp->d_parent == parent) {			child = dget_locked(tmp);			spin_unlock(&dcache_lock);			goto out;		}	}	spin_unlock(&dcache_lock);	/* now we need that name.  If there was an error getting it, now is th	 * time to bail out.	 */	if ((err = nerr))		goto out;	qs.name = namebuf;	qs.len = strlen(namebuf);	if (find_inode_number(parent, &qs) != 0) {		/* Now that IS odd.  I wonder what it means... */		err = -EEXIST;		printk("nfsd-fh: found a name that I didn't expect: %s/%s\n", parent->d_name.name, qs.name);		goto out;	}	err = d_splice(child, parent, &qs);	dprintk("nfsd_fh: found name %s for ino %ld\n", child->d_name.name, child->d_inode->i_ino); out:	up(&parent->d_inode->i_sem);	if (err)		return ERR_PTR(err);	else		return child;}/* * This is the basic lookup mechanism for turning an NFS file handle * into a dentry. * We use nfsd_iget and if that doesn't return a suitably connected dentry, * we try to find the parent, and the parent of that and so-on until a * connection if made. */static struct dentry *find_fh_dentry(struct super_block *sb, __u32 *datap, int len, int fhtype, int needpath){	struct dentry *dentry, *result = NULL;	struct dentry *tmp;	int err = -ESTALE;	/* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free)	 * dcache path ever exists, as otherwise two partial paths might get	 * joined together, which would be very confusing.	 * If there is ever an unconnected non-root directory, then this lock	 * must be held.	 */	nfsdstats.fh_lookup++;	/*	 * Attempt to find the inode.	 */ retry:	down(&sb->s_nfsd_free_path_sem);	result = nfsd_get_dentry(sb, datap, len, fhtype, 0);	if (IS_ERR(result)	    || !(result->d_flags & DCACHE_NFSD_DISCONNECTED)	    || (!S_ISDIR(result->d_inode->i_mode) && ! needpath)) {		up(&sb->s_nfsd_free_path_sem);	    		err = PTR_ERR(result);		if (IS_ERR(result))			goto err_out;		if ((result->d_flags & DCACHE_NFSD_DISCONNECTED))			nfsdstats.fh_anon++;		return result;	}	/* It's a directory, or we are required to confirm the file's	 * location in the tree.	 */	dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,datap[0]);	if (!S_ISDIR(result->d_inode->i_mode)) {		nfsdstats.fh_nocache_nondir++;			/* need to iget dirino and make sure this inode is in that directory */			dentry = nfsd_get_dentry(sb, datap, len, fhtype, 1);			err = PTR_ERR(dentry);			if (IS_ERR(dentry))				goto err_result;			err = -ESTALE;			if (!dentry->d_inode			    || !S_ISDIR(dentry->d_inode->i_mode)) {				goto err_dentry;			}			tmp = splice(result, dentry);			err = PTR_ERR(tmp);			if (IS_ERR(tmp))				goto err_dentry;			if (tmp != result) {				/* it is safe to just use tmp instead, but we must discard result first */				d_drop(result);				dput(result);				result = tmp;			}	} else {		nfsdstats.fh_nocache_dir++;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -