vfs.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,815 行 · 第 1/3 页

C
1,815
字号
#define MSNFS	/* HACK HACK *//* * linux/fs/nfsd/vfs.c * * File operations used by nfsd. Some of these have been ripped from * other parts of the kernel because they weren't exported, others * are partial duplicates with added or changed functionality. * * Note that several functions dget() the dentry upon which they want * to act, most notably those that create directory entries. Response * dentry's are dput()'d if necessary in the release callback. * So if you notice code paths that apparently fail to dput() the * dentry, don't worry--they have been taken care of. * * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de> * Zerocpy NFS support (C) 2002 Hirokazu Takahashi <taka@valinux.co.jp> */#include <linux/config.h>#include <linux/string.h>#include <linux/time.h>#include <linux/errno.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/mount.h>#include <linux/major.h>#include <linux/ext2_fs.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/fcntl.h>#include <linux/net.h>#include <linux/unistd.h>#include <linux/slab.h>#include <linux/pagemap.h>#include <linux/in.h>#include <linux/module.h>#include <linux/namei.h>#include <linux/vfs.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#ifdef CONFIG_NFSD_V3#include <linux/nfs3.h>#include <linux/nfsd/xdr3.h>#endif /* CONFIG_NFSD_V3 */#include <linux/nfsd/nfsfh.h>#include <linux/quotaops.h>#include <linux/dnotify.h>#ifdef CONFIG_NFSD_V4#include <linux/posix_acl.h>#include <linux/posix_acl_xattr.h>#include <linux/xattr_acl.h>#include <linux/xattr.h>#include <linux/nfs4.h>#include <linux/nfs4_acl.h>#include <linux/nfsd_idmap.h>#include <linux/security.h>#endif /* CONFIG_NFSD_V4 */#include <asm/uaccess.h>#define NFSDDBG_FACILITY		NFSDDBG_FILEOP#define NFSD_PARANOIA/* We must ignore files (but only files) which might have mandatory * locks on them because there is no way to know if the accesser has * the lock. */#define IS_ISMNDLK(i)	(S_ISREG((i)->i_mode) && MANDATORY_LOCK(i))/* * This is a cache of readahead params that help us choose the proper * readahead strategy. Initially, we set all readahead parameters to 0 * and let the VFS handle things. * If you increase the number of cached files very much, you'll need to * add a hash table here. */struct raparms {	struct raparms		*p_next;	unsigned int		p_count;	ino_t			p_ino;	dev_t			p_dev;	int			p_set;	struct file_ra_state	p_ra;};static struct raparms *		raparml;static struct raparms *		raparm_cache;/*  * Called from nfsd_lookup and encode_dirent. Check if we have crossed  * a mount point. * Returns -EAGAIN leaving *dpp and *expp unchanged,  *  or nfs_ok having possibly changed *dpp and *expp */intnfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, 		        struct svc_export **expp){	struct svc_export *exp = *expp, *exp2 = NULL;	struct dentry *dentry = *dpp;	struct vfsmount *mnt = mntget(exp->ex_mnt);	struct dentry *mounts = dget(dentry);	int err = nfs_ok;	while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts));	exp2 = exp_get_by_name(exp->ex_client, mnt, mounts, &rqstp->rq_chandle);	if (IS_ERR(exp2)) {		err = PTR_ERR(exp2);		dput(mounts);		mntput(mnt);		goto out;	}	if (exp2 && ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2))) {		/* successfully crossed mount point */		exp_put(exp);		*expp = exp2;		dput(dentry);		*dpp = mounts;	} else {		if (exp2) exp_put(exp2);		dput(mounts);	}	mntput(mnt);out:	return err;}/* * Look up one component of a pathname. * N.B. After this call _both_ fhp and resfh need an fh_put * * If the lookup would cross a mountpoint, and the mounted filesystem * is exported to the client with NFSEXP_NOHIDE, then the lookup is * accepted as it stands and the mounted directory is * returned. Otherwise the covered directory is returned. * NOTE: this mountpoint crossing is not supported properly by all *   clients and is explicitly disallowed for NFSv3 *      NeilBrown <neilb@cse.unsw.edu.au> */intnfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,					int len, struct svc_fh *resfh){	struct svc_export	*exp;	struct dentry		*dparent;	struct dentry		*dentry;	int			err;	dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name);	/* Obtain dentry and export. */	err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC);	if (err)		return err;	dparent = fhp->fh_dentry;	exp  = fhp->fh_export;	exp_get(exp);	err = nfserr_acces;	/* Lookup the name, but don't follow links */	if (isdotent(name, len)) {		if (len==1)			dentry = dget(dparent);		else if (dparent != exp->ex_dentry) {			dentry = dget_parent(dparent);		} else  if (!EX_NOHIDE(exp))			dentry = dget(dparent); /* .. == . just like at / */		else {			/* checking mountpoint crossing is very different when stepping up */			struct svc_export *exp2 = NULL;			struct dentry *dp;			struct vfsmount *mnt = mntget(exp->ex_mnt);			dentry = dget(dparent);			while(dentry == mnt->mnt_root && follow_up(&mnt, &dentry))				;			dp = dget_parent(dentry);			dput(dentry);			dentry = dp;			exp2 = exp_parent(exp->ex_client, mnt, dentry,					  &rqstp->rq_chandle);			if (IS_ERR(exp2)) {				err = PTR_ERR(exp2);				dput(dentry);				mntput(mnt);				goto out_nfserr;			}			if (!exp2) {				dput(dentry);				dentry = dget(dparent);			} else {				exp_put(exp);				exp = exp2;			}			mntput(mnt);		}	} else {		fh_lock(fhp);		dentry = lookup_one_len(name, dparent, len);		err = PTR_ERR(dentry);		if (IS_ERR(dentry))			goto out_nfserr;		/*		 * check if we have crossed a mount point ...		 */		if (d_mountpoint(dentry)) {			if ((err = nfsd_cross_mnt(rqstp, &dentry, &exp))) {				dput(dentry);				goto out_nfserr;			}		}	}	/*	 * Note: we compose the file handle now, but as the	 * dentry may be negative, it may need to be updated.	 */	err = fh_compose(resfh, exp, dentry, fhp);	if (!err && !dentry->d_inode)		err = nfserr_noent;	dput(dentry);out:	exp_put(exp);	return err;out_nfserr:	err = nfserrno(err);	goto out;}/* * Set various file attributes. * N.B. After this call fhp needs an fh_put */intnfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,	     int check_guard, time_t guardtime){	struct dentry	*dentry;	struct inode	*inode;	int		accmode = MAY_SATTR;	int		ftype = 0;	int		imode;	int		err;	int		size_change = 0;	if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))		accmode |= MAY_WRITE|MAY_OWNER_OVERRIDE;	if (iap->ia_valid & ATTR_SIZE)		ftype = S_IFREG;	/* Get inode */	err = fh_verify(rqstp, fhp, ftype, accmode);	if (err || !iap->ia_valid)		goto out;	dentry = fhp->fh_dentry;	inode = dentry->d_inode;	/* NFSv2 does not differentiate between "set-[ac]time-to-now"	 * which only requires access, and "set-[ac]time-to-X" which	 * requires ownership.	 * So if it looks like it might be "set both to the same time which	 * is close to now", and if inode_change_ok fails, then we	 * convert to "set to now" instead of "set to explicit time"	 *	 * We only call inode_change_ok as the last test as technically	 * it is not an interface that we should be using.  It is only	 * valid if the filesystem does not define it's own i_op->setattr.	 */#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)#define	MAX_TOUCH_TIME_ERROR (30*60)	if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET	    && iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec	    ) {	    /* Looks probable.  Now just make sure time is in the right ballpark.	     * Solaris, at least, doesn't seem to care what the time request is.	     * We require it be within 30 minutes of now.	     */	    time_t delta = iap->ia_atime.tv_sec - get_seconds();	    if (delta<0) delta = -delta;	    if (delta < MAX_TOUCH_TIME_ERROR &&		inode_change_ok(inode, iap) != 0) {		/* turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME		 * this will cause notify_change to set these times to "now"		 */		iap->ia_valid &= ~BOTH_TIME_SET;	    }	}	    	/* The size case is special. It changes the file as well as the attributes.  */	if (iap->ia_valid & ATTR_SIZE) {		if (iap->ia_size < inode->i_size) {			err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE);			if (err)				goto out;		}		/*		 * If we are changing the size of the file, then		 * we need to break all leases.		 */		err = break_lease(inode, FMODE_WRITE | O_NONBLOCK);		if (err) /* ENOMEM or EWOULDBLOCK */			goto out_nfserr;		err = get_write_access(inode);		if (err)			goto out_nfserr;		size_change = 1;		err = locks_verify_truncate(inode, NULL, iap->ia_size);		if (err) {			put_write_access(inode);			goto out_nfserr;		}		DQUOT_INIT(inode);	}	imode = inode->i_mode;	if (iap->ia_valid & ATTR_MODE) {		iap->ia_mode &= S_IALLUGO;		imode = iap->ia_mode |= (imode & ~S_IALLUGO);	}	/* Revoke setuid/setgid bit on chown/chgrp */	if ((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid)		iap->ia_valid |= ATTR_KILL_SUID;	if ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid)		iap->ia_valid |= ATTR_KILL_SGID;	/* Change the attributes. */	iap->ia_valid |= ATTR_CTIME;	err = nfserr_notsync;	if (!check_guard || guardtime == inode->i_ctime.tv_sec) {		fh_lock(fhp);		err = notify_change(dentry, iap);		err = nfserrno(err);		fh_unlock(fhp);	}	if (size_change)		put_write_access(inode);	if (!err)		if (EX_ISSYNC(fhp->fh_export))			write_inode_now(inode, 1);out:	return err;out_nfserr:	err = nfserrno(err);	goto out;}#if defined(CONFIG_NFSD_V4)static intset_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key){	int len;	size_t buflen;	char *buf = NULL;	int error = 0;	struct inode *inode = dentry->d_inode;	buflen = posix_acl_xattr_size(pacl->a_count);	buf = kmalloc(buflen, GFP_KERNEL);	error = -ENOMEM;	if (buf == NULL)		goto out;	len = posix_acl_to_xattr(pacl, buf, buflen);	if (len < 0) {		error = len;		goto out;	}	error = -EOPNOTSUPP;	if (inode->i_op && inode->i_op->setxattr) {		down(&inode->i_sem);		security_inode_setxattr(dentry, key, buf, len, 0);		error = inode->i_op->setxattr(dentry, key, buf, len, 0);		if (!error)			security_inode_post_setxattr(dentry, key, buf, len, 0);		up(&inode->i_sem);	}out:	kfree(buf);	return (error);}intnfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,    struct nfs4_acl *acl){	int error;	struct dentry *dentry;	struct inode *inode;	struct posix_acl *pacl = NULL, *dpacl = NULL;	unsigned int flags = 0;	/* Get inode */	error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR);	if (error)		goto out;	dentry = fhp->fh_dentry;	inode = dentry->d_inode;	if (S_ISDIR(inode->i_mode))		flags = NFS4_ACL_DIR;	error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);	if (error < 0)		goto out_nfserr;	if (pacl) {		error = set_nfsv4_acl_one(dentry, pacl, XATTR_NAME_ACL_ACCESS);		if (error < 0)			goto out_nfserr;	}	if (dpacl) {		error = set_nfsv4_acl_one(dentry, dpacl, XATTR_NAME_ACL_DEFAULT);		if (error < 0)			goto out_nfserr;	}	error = nfs_ok;out:	posix_acl_release(pacl);	posix_acl_release(dpacl);	return (error);out_nfserr:	error = nfserrno(error);	goto out;}static struct posix_acl *_get_posix_acl(struct dentry *dentry, char *key){	struct inode *inode = dentry->d_inode;	char *buf = NULL;	int buflen, error = 0;	struct posix_acl *pacl = NULL;	down(&inode->i_sem);	buflen = inode->i_op->getxattr(dentry, key, NULL, 0);	if (buflen <= 0) {		error = buflen < 0 ? buflen : -ENODATA;		goto out_sem;	}	buf = kmalloc(buflen, GFP_KERNEL);	if (buf == NULL) {		error = -ENOMEM;		goto out_sem;	}	error = -EOPNOTSUPP;	if (inode->i_op && inode->i_op->getxattr) {		error = security_inode_getxattr(dentry, key);		if (error)			goto out_sem;		error = inode->i_op->getxattr(dentry, key, buf, buflen);	}	if (error < 0)		goto out_sem;	error = 0;	up(&inode->i_sem);	pacl = posix_acl_from_xattr(buf, buflen); out:	kfree(buf);	return pacl; out_sem:	up(&inode->i_sem);	pacl = ERR_PTR(error);	goto out;}intnfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl){	struct inode *inode = dentry->d_inode;	int error = 0;	struct posix_acl *pacl = NULL, *dpacl = NULL;	unsigned int flags = 0;	pacl = _get_posix_acl(dentry, XATTR_NAME_ACL_ACCESS);	if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA)		pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);	if (IS_ERR(pacl)) {		error = PTR_ERR(pacl);		pacl = NULL;		goto out;	}	if (S_ISDIR(inode->i_mode)) {		dpacl = _get_posix_acl(dentry, XATTR_NAME_ACL_DEFAULT);		if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA)			dpacl = NULL;		else if (IS_ERR(dpacl)) {			error = PTR_ERR(dpacl);			dpacl = NULL;			goto out;		}		flags = NFS4_ACL_DIR;	}	*acl = nfs4_acl_posix_to_nfsv4(pacl, dpacl, flags);	if (IS_ERR(*acl)) {		error = PTR_ERR(*acl);		*acl = NULL;	} out:	posix_acl_release(pacl);	posix_acl_release(dpacl);	return error;}#endif /* defined(CONFIG_NFS_V4) */#ifdef CONFIG_NFSD_V3/* * Check server access rights to a file system object */struct accessmap {	u32		access;	int		how;};static struct accessmap	nfs3_regaccess[] = {    {	NFS3_ACCESS_READ,	MAY_READ			},    {	NFS3_ACCESS_EXECUTE,	MAY_EXEC			},    {	NFS3_ACCESS_MODIFY,	MAY_WRITE|MAY_TRUNC		},    {	NFS3_ACCESS_EXTEND,	MAY_WRITE			},    {	0,			0				}};static struct accessmap	nfs3_diraccess[] = {    {	NFS3_ACCESS_READ,	MAY_READ			},    {	NFS3_ACCESS_LOOKUP,	MAY_EXEC			},    {	NFS3_ACCESS_MODIFY,	MAY_EXEC|MAY_WRITE|MAY_TRUNC	},    {	NFS3_ACCESS_EXTEND,	MAY_EXEC|MAY_WRITE		},    {	NFS3_ACCESS_DELETE,	MAY_REMOVE			},    {	0,			0				}};static struct accessmap	nfs3_anyaccess[] = {	/* Some clients - Solaris 2.6 at least, make an access call	 * to the server to check for access for things like /dev/null	 * (which really, the server doesn't care about).  So	 * We provide simple access checking for them, looking	 * mainly at mode bits, and we make sure to ignore read-only	 * filesystem checks	 */    {	NFS3_ACCESS_READ,	MAY_READ			},    {	NFS3_ACCESS_EXECUTE,	MAY_EXEC			},    {	NFS3_ACCESS_MODIFY,	MAY_WRITE|MAY_LOCAL_ACCESS	},    {	NFS3_ACCESS_EXTEND,	MAY_WRITE|MAY_LOCAL_ACCESS	},    {	0,			0				}};intnfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *supported){	struct accessmap	*map;	struct svc_export	*export;	struct dentry		*dentry;	u32			query, result = 0, sresult = 0;	unsigned int		error;	error = fh_verify(rqstp, fhp, 0, MAY_NOP);	if (error)		goto out;	export = fhp->fh_export;	dentry = fhp->fh_dentry;	if (S_ISREG(dentry->d_inode->i_mode))		map = nfs3_regaccess;	else if (S_ISDIR(dentry->d_inode->i_mode))		map = nfs3_diraccess;	else		map = nfs3_anyaccess;	query = *access;	for  (; map->access; map++) {		if (map->access & query) {			unsigned int err2;			sresult |= map->access;			err2 = nfsd_permission(export, dentry, map->how);			switch (err2) {

⌨️ 快捷键说明

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