expfs.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 543 行

C
543
字号
#include <linux/fs.h>#include <linux/file.h>#include <linux/module.h>#include <linux/smp_lock.h>#include <linux/namei.h>struct export_operations export_op_default;#define	CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun)#define dprintk(fmt, args...) do{}while(0)/** * find_exported_dentry - helper routine to implement export_operations->decode_fh * @sb:		The &super_block identifying the filesystem * @obj:	An opaque identifier of the object to be found - passed to *		get_inode * @parent:	An optional opqaue identifier of the parent of the object. * @acceptable:	A function used to test possible &dentries to see if they are *		acceptable * @context:	A parameter to @acceptable so that it knows on what basis to *		judge. * * find_exported_dentry is the central helper routine to enable file systems * to provide the decode_fh() export_operation.  It's main task is to take * an &inode, find or create an appropriate &dentry structure, and possibly * splice this into the dcache in the correct place. * * The decode_fh() operation provided by the filesystem should call * find_exported_dentry() with the same parameters that it received except * that instead of the file handle fragment, pointers to opaque identifiers * for the object and optionally its parent are passed.  The default decode_fh * routine passes one pointer to the start of the filehandle fragment, and * one 8 bytes into the fragment.  It is expected that most filesystems will * take this approach, though the offset to the parent identifier may well be * different. * * find_exported_dentry() will call get_dentry to get an dentry pointer from * the file system.  If any &dentry in the d_alias list is acceptable, it will * be returned.  Otherwise find_exported_dentry() will attempt to splice a new * &dentry into the dcache using get_name() and get_parent() to find the * appropriate place. */struct dentry *find_exported_dentry(struct super_block *sb, void *obj, void *parent,		     int (*acceptable)(void *context, struct dentry *de),		     void *context){	struct dentry *result = NULL;	struct dentry *target_dir;	int err;	struct export_operations *nops = sb->s_export_op;	struct list_head *le, *head;	struct dentry *toput = NULL;	int noprogress;	/*	 * Attempt to find the inode.	 */	result = CALL(sb->s_export_op,get_dentry)(sb,obj);	err = -ESTALE;	if (result == NULL)		goto err_out;	if (IS_ERR(result)) {		err = PTR_ERR(result);		goto err_out;	}	if (S_ISDIR(result->d_inode->i_mode) &&	    (result->d_flags & DCACHE_DISCONNECTED)) {		/* it is an unconnected directory, we must connect it */		;	} else {		if (acceptable(context, result))			return result;		if (S_ISDIR(result->d_inode->i_mode)) {			/* there is no other dentry, so fail */			goto err_result;		}		/* try any other aliases */		spin_lock(&dcache_lock);		head = &result->d_inode->i_dentry;		list_for_each(le, head) {			struct dentry *dentry = list_entry(le, struct dentry, d_alias);			dget_locked(dentry);			spin_unlock(&dcache_lock);			if (toput)				dput(toput);			toput = NULL;			if (dentry != result &&			    acceptable(context, dentry)) {				dput(result);				return dentry;			}			spin_lock(&dcache_lock);			toput = dentry;		}		spin_unlock(&dcache_lock);		if (toput)			dput(toput);	}				/* It's a directory, or we are required to confirm the file's	 * location in the tree based on the parent information 	 */	dprintk("find_exported_dentry: need to look harder for %s/%d\n",sb->s_id,*(int*)obj);	if (S_ISDIR(result->d_inode->i_mode))		target_dir = dget(result);	else {		if (parent == NULL)			goto err_result;		target_dir = CALL(sb->s_export_op,get_dentry)(sb,parent);		if (IS_ERR(target_dir))			err = PTR_ERR(target_dir);		if (target_dir == NULL || IS_ERR(target_dir))			goto err_result;	}	/*	 * Now we need to make sure that target_dir is properly connected.	 * It may already be, as the flag isn't always updated when connection	 * happens.	 * So, we walk up parent links until we find a connected directory,	 * or we run out of directories.  Then we find the parent, find	 * the name of the child in that parent, and do a lookup.	 * This should connect the child into the parent	 * We then repeat.	 */	/* it is possible that a confused file system might not let us complete 	 * the path to the root.  For example, if get_parent returns a directory	 * in which we cannot find a name for the child.  While this implies a	 * very sick filesystem we don't want it to cause knfsd to spin.  Hence	 * the noprogress counter.  If we go through the loop 10 times (2 is	 * probably enough) without getting anywhere, we just give up	 */	noprogress= 0;	while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) {		struct dentry *pd = target_dir;		dget(pd);		spin_lock(&pd->d_lock);		while (!IS_ROOT(pd) &&				(pd->d_parent->d_flags&DCACHE_DISCONNECTED)) {			struct dentry *parent = pd->d_parent;			dget(parent);			spin_unlock(&pd->d_lock);			dput(pd);			pd = parent;			spin_lock(&pd->d_lock);		}		spin_unlock(&pd->d_lock);		if (!IS_ROOT(pd)) {			/* must have found a connected parent - great */			spin_lock(&pd->d_lock);			pd->d_flags &= ~DCACHE_DISCONNECTED;			spin_unlock(&pd->d_lock);			noprogress = 0;		} else if (pd == sb->s_root) {			printk(KERN_ERR "export: Eeek filesystem root is not connected, impossible\n");			spin_lock(&pd->d_lock);			pd->d_flags &= ~DCACHE_DISCONNECTED;			spin_unlock(&pd->d_lock);			noprogress = 0;		} else {			/* we have hit the top of a disconnected path.  Try			 * to find parent and connect			 * note: racing with some other process renaming a			 * directory isn't much of a problem here.  If someone			 * renames the directory, it will end up properly			 * connected, which is what we want			 */			struct dentry *ppd;			struct dentry *npd;			char nbuf[NAME_MAX+1];			down(&pd->d_inode->i_sem);			ppd = CALL(nops,get_parent)(pd);			up(&pd->d_inode->i_sem);			if (IS_ERR(ppd)) {				err = PTR_ERR(ppd);				dprintk("find_exported_dentry: get_parent of %ld failed, err %d\n",					pd->d_inode->i_ino, err);				dput(pd);				break;			}			dprintk("find_exported_dentry: find name of %lu in %lu\n", pd->d_inode->i_ino, ppd->d_inode->i_ino);			err = CALL(nops,get_name)(ppd, nbuf, pd);			if (err) {				dput(ppd);				dput(pd);				if (err == -ENOENT)					/* some race between get_parent and					 * get_name?  just try again					 */					continue;				break;			}			dprintk("find_exported_dentry: found name: %s\n", nbuf);			down(&ppd->d_inode->i_sem);			npd = lookup_one_len(nbuf, ppd, strlen(nbuf));			up(&ppd->d_inode->i_sem);			if (IS_ERR(npd)) {				err = PTR_ERR(npd);				dprintk("find_exported_dentry: lookup failed: %d\n", err);				dput(ppd);				dput(pd);				break;			}			/* we didn't really want npd, we really wanted			 * a side-effect of the lookup.			 * hopefully, npd == pd, though it isn't really			 * a problem if it isn't			 */			if (npd == pd)				noprogress = 0;			else				printk("find_exported_dentry: npd != pd\n");			dput(npd);			dput(ppd);			if (IS_ROOT(pd)) {				/* something went wrong, we have to give up */				dput(pd);				break;			}		}		dput(pd);	}	if (target_dir->d_flags & DCACHE_DISCONNECTED) {		/* something went wrong - oh-well */		if (!err)			err = -ESTALE;		goto err_target;	}	/* if we weren't after a directory, have one more step to go */	if (result != target_dir) {		struct dentry *nresult;		char nbuf[NAME_MAX+1];		err = CALL(nops,get_name)(target_dir, nbuf, result);		if (!err) {			down(&target_dir->d_inode->i_sem);			nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf));			up(&target_dir->d_inode->i_sem);			if (!IS_ERR(nresult)) {				if (nresult->d_inode) {					dput(result);					result = nresult;				} else					dput(nresult);			}		}	}	dput(target_dir);	/* now result is properly connected, it is our best bet */	if (acceptable(context, result))		return result;	/* one last try of the aliases.. */	spin_lock(&dcache_lock);	toput = NULL;	head = &result->d_inode->i_dentry;	list_for_each(le, head) {		struct dentry *dentry = list_entry(le, struct dentry, d_alias);		dget_locked(dentry);		spin_unlock(&dcache_lock);		if (toput) dput(toput);		if (dentry != result &&		    acceptable(context, dentry)) {			dput(result);			return dentry;		}		spin_lock(&dcache_lock);		toput = dentry;	}	spin_unlock(&dcache_lock);	if (toput)		dput(toput);	/* drat - I just cannot find anything acceptable */	dput(result);	/* It might be justifiable to return ESTALE here,	 * but the filehandle at-least looks reasonable good	 * and it just be a permission problem, so returning	 * -EACCESS is safer	 */	return ERR_PTR(-EACCES); err_target:	dput(target_dir); err_result:	dput(result); err_out:	return ERR_PTR(err);}static struct dentry *get_parent(struct dentry *child){	/* get_parent cannot be supported generically, the locking	 * is too icky.	 * instead, we just return EACCES.  If server reboots or inodes	 * get flushed, you lose	 */	return ERR_PTR(-EACCES);}struct 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 getdents_callback *buf = __buf;	int result = 0;	buf->sequence++;	if (buf->ino == ino) {		memcpy(buf->name, name, len);		buf->name[len] = '\0';		buf->found = 1;		result = -1;	}	return result;}/** * get_name - default export_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 get_name(struct dentry *dentry, char *name,			struct dentry *child){	struct inode *dir = dentry->d_inode;	int error;	struct file *file;	struct 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 ...	 */	file = dentry_open(dget(dentry), NULL, O_RDONLY);	error = PTR_ERR(file);	if (IS_ERR(file))		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:	fput(file);out:	return error;}static struct dentry *export_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 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	 */	result = d_alloc_anon(inode);	if (!result) {		iput(inode);		return ERR_PTR(-ENOMEM);	}	return result;}static struct dentry *get_object(struct super_block *sb, void *vobjp){	__u32 *objp = vobjp;	unsigned long ino = objp[0];	__u32 generation = objp[1];	return export_iget(sb, ino, generation);}/** * export_encode_fh - default export_operations->encode_fh function * @dentry:  the dentry to encode * @fh:      where to store the file handle fragment * @max_len: maximum length to store there * @connectable: whether to store parent information * * This default encode_fh function assumes that the 32 inode number * is suitable for locating an inode, and that the generation number * can be used to check that it is still valid.  It places them in the * filehandle fragment where export_decode_fh expects to find them. */static int export_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,		   int connectable){	struct inode * inode = dentry->d_inode;	int len = *max_len;	int type = 1;		if (len < 2 || (connectable && len < 4))		return 255;	len = 2;	fh[0] = inode->i_ino;	fh[1] = inode->i_generation;	if (connectable && !S_ISDIR(inode->i_mode)) {		struct inode *parent;		spin_lock(&dentry->d_lock);		parent = dentry->d_parent->d_inode;		fh[2] = parent->i_ino;		fh[3] = parent->i_generation;		spin_unlock(&dentry->d_lock);		len = 4;		type = 2;	}	*max_len = len;	return type;}/** * export_decode_fh - default export_operations->decode_fh function * @sb:  The superblock * @fh:  pointer to the file handle fragment * @fh_len: length of file handle fragment * @acceptable: function for testing acceptability of dentrys * @context:   context for @acceptable * * This is the default decode_fh() function. * a fileid_type of 1 indicates that the filehandlefragment * just contains an object identifier understood by  get_dentry. * a fileid_type of 2 says that there is also a directory * identifier 8 bytes in to the filehandlefragement. */static struct dentry *export_decode_fh(struct super_block *sb, __u32 *fh, int fh_len,			      int fileid_type,			 int (*acceptable)(void *context, struct dentry *de),			 void *context){	__u32 parent[2];	parent[0] = parent[1] = 0;	if (fh_len < 2 || fileid_type > 2)		return NULL;	if (fileid_type == 2) {		if (fh_len > 2) parent[0] = fh[2];		if (fh_len > 3) parent[1] = fh[3];	}	return find_exported_dentry(sb, fh, parent,				   acceptable, context);}struct export_operations export_op_default = {	.decode_fh	= export_decode_fh,	.encode_fh	= export_encode_fh,	.get_name	= get_name,	.get_parent	= get_parent,	.get_dentry	= get_object,};EXPORT_SYMBOL(export_op_default);EXPORT_SYMBOL(find_exported_dentry);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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