📄 vfs.c
字号:
#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/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/splice.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/delay.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/fsnotify.h>#include <linux/posix_acl.h>#include <linux/posix_acl_xattr.h>#include <linux/xattr.h>#ifdef CONFIG_NFSD_V4#include <linux/nfs4.h>#include <linux/nfs4_acl.h>#include <linux/nfsd_idmap.h>#include <linux/security.h>#endif /* CONFIG_NFSD_V4 */#include <linux/jhash.h>#include <asm/uaccess.h>#define NFSDDBG_FACILITY NFSDDBG_FILEOP/* * 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; unsigned int p_hindex;};struct raparm_hbucket { struct raparms *pb_head; spinlock_t pb_lock;} ____cacheline_aligned_in_smp;static struct raparms * raparml;#define RAPARM_HASH_BITS 4#define RAPARM_HASH_SIZE (1<<RAPARM_HASH_BITS)#define RAPARM_HASH_MASK (RAPARM_HASH_SIZE-1)static struct raparm_hbucket raparm_hash[RAPARM_HASH_SIZE];/* * Called from nfsd_lookup and encode_dirent. Check if we have crossed * a mount point. * Returns -EAGAIN or -ETIMEDOUT 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 = 0; while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)); exp2 = rqst_exp_get_by_name(rqstp, mnt, mounts); if (IS_ERR(exp2)) { if (PTR_ERR(exp2) != -ENOENT) err = PTR_ERR(exp2); dput(mounts); mntput(mnt); goto out; } if ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) { /* successfully crossed mount point */ exp_put(exp); *expp = exp2; dput(dentry); *dpp = mounts; } else { exp_put(exp2); dput(mounts); } mntput(mnt);out: return err;}__be32nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, int len, struct svc_export **exp_ret, struct dentry **dentry_ret){ struct svc_export *exp; struct dentry *dparent; struct dentry *dentry; __be32 err; int host_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); /* 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 = rqst_exp_parent(rqstp, mnt, dentry); if (PTR_ERR(exp2) == -ENOENT) { dput(dentry); dentry = dget(dparent); } else if (IS_ERR(exp2)) { host_err = PTR_ERR(exp2); dput(dentry); mntput(mnt); goto out_nfserr; } else { exp_put(exp); exp = exp2; } mntput(mnt); } } else { fh_lock(fhp); dentry = lookup_one_len(name, dparent, len); host_err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_nfserr; /* * check if we have crossed a mount point ... */ if (d_mountpoint(dentry)) { if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) { dput(dentry); goto out_nfserr; } } } *dentry_ret = dentry; *exp_ret = exp; return 0;out_nfserr: exp_put(exp); return nfserrno(host_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> */__be32nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, int len, struct svc_fh *resfh){ struct svc_export *exp; struct dentry *dentry; __be32 err; err = nfsd_lookup_dentry(rqstp, fhp, name, len, &exp, &dentry); if (err) return err; err = check_nfsd_access(exp, rqstp); if (err) goto out; /* * 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;out: dput(dentry); exp_put(exp); return err;}/* * Set various file attributes. * N.B. After this call fhp needs an fh_put */__be32nfsd_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; __be32 err; int host_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) goto out; dentry = fhp->fh_dentry; inode = dentry->d_inode; /* Ignore any mode updates on symlinks */ if (S_ISLNK(inode->i_mode)) iap->ia_valid &= ~ATTR_MODE; if (!iap->ia_valid) goto out; /* * 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(rqstp, 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. */ host_err = break_lease(inode, FMODE_WRITE | O_NONBLOCK); if (host_err == -EWOULDBLOCK) host_err = -ETIMEDOUT; if (host_err) /* ENOMEM or EWOULDBLOCK */ goto out_nfserr; host_err = get_write_access(inode); if (host_err) goto out_nfserr; size_change = 1; host_err = locks_verify_truncate(inode, NULL, iap->ia_size); if (host_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); /* if changing uid/gid revoke setuid/setgid in mode */ if ((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) { iap->ia_valid |= ATTR_KILL_PRIV; iap->ia_mode &= ~S_ISUID; } if ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid) iap->ia_mode &= ~S_ISGID; } else { /* * 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 | ATTR_KILL_PRIV; 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); host_err = notify_change(dentry, iap); err = nfserrno(host_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(host_err); goto out;}#if defined(CONFIG_NFSD_V2_ACL) || \ defined(CONFIG_NFSD_V3_ACL) || \ defined(CONFIG_NFSD_V4)static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf){ ssize_t buflen; buflen = vfs_getxattr(dentry, key, NULL, 0); if (buflen <= 0) return buflen; *buf = kmalloc(buflen, GFP_KERNEL); if (!*buf) return -ENOMEM; return vfs_getxattr(dentry, key, *buf, buflen);}#endif#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; 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 = vfs_setxattr(dentry, key, buf, len, 0);out: kfree(buf); return error;}__be32nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_acl *acl){ __be32 error; int host_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) return error; dentry = fhp->fh_dentry; inode = dentry->d_inode; if (S_ISDIR(inode->i_mode)) flags = NFS4_ACL_DIR; host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags); if (host_error == -EINVAL) { return nfserr_attrnotsupp; } else if (host_error < 0) goto out_nfserr; host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS); if (host_error < 0) goto out_release; if (S_ISDIR(inode->i_mode)) host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT);out_release: posix_acl_release(pacl); posix_acl_release(dpacl);out_nfserr: if (host_error == -EOPNOTSUPP) return nfserr_attrnotsupp; else return nfserrno(host_error);}static struct posix_acl *_get_posix_acl(struct dentry *dentry, char *key){ void *buf = NULL; struct posix_acl *pacl = NULL; int buflen; buflen = nfsd_getxattr(dentry, key, &buf); if (!buflen) buflen = -ENODATA; if (buflen <= 0) return ERR_PTR(buflen); pacl = posix_acl_from_xattr(buf, buflen); kfree(buf); return pacl;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -