📄 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 in ksyms.c, 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> */#include <linux/config.h>#include <linux/version.h>#include <linux/string.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/locks.h>#include <linux/fs.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/in.h>#define __NO_VERSION__#include <linux/module.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 <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; unsigned long p_reada, p_ramax, p_raend, p_ralen, p_rawin;};static struct raparms * raparml;static struct raparms * raparm_cache;/* * 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_CROSSMNT, 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) goto out; dparent = fhp->fh_dentry; exp = fhp->fh_export; err = nfserr_acces; /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { if (len==1) dentry = dget(dparent); else { /* must be ".." */ /* checking mountpoint crossing is very different when stepping up */ if (dparent == exp->ex_dentry) { if (!EX_CROSSMNT(exp)) dentry = dget(dparent); /* .. == . just like at / */ else { struct svc_export *exp2 = NULL; struct dentry *dp; struct vfsmount *mnt = mntget(exp->ex_mnt); dentry = dget(dparent); while(follow_up(&mnt, &dentry)) ; dp = dget(dentry->d_parent); dput(dentry); dentry = dp; for ( ; exp2 == NULL && dp->d_parent != dp; dp=dp->d_parent) exp2 = exp_get(exp->ex_client, dp->d_inode->i_dev, dp->d_inode->i_ino); if (exp2==NULL) { dput(dentry); dentry = dget(dparent); } else { exp = exp2; } mntput(mnt); } } else dentry = dget(dparent->d_parent); } } 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)) { struct svc_export *exp2 = NULL; struct vfsmount *mnt = mntget(exp->ex_mnt); struct dentry *mounts = dget(dentry); while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)) ; exp2 = exp_get(rqstp->rq_client, mounts->d_inode->i_dev, mounts->d_inode->i_ino); if (exp2 && EX_CROSSMNT(exp2)) { /* successfully crossed mount point */ exp = exp2; dput(dentry); dentry = mounts; } else dput(mounts); mntput(mnt); } } /* * 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: 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; err = inode_change_ok(inode, iap); /* could be a "touch" (utimes) request where the user is not the owner but does * have write permission. In this case the user should be allowed to set * both times to the current time. We could just assume any such SETATTR * is intended to set the times to "now", but we do a couple of simple tests * to increase our confidence. */#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)#define MAX_TOUCH_TIME_ERROR (30*60) if (err && (iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET && iap->ia_mtime == iap->ia_atime ) { /* looks good. now just make sure time is in the right ballpark. * solaris, at least, doesn't seem to care what the time request is */ time_t delta = iap->ia_atime - CURRENT_TIME; if (delta<0) delta = -delta; if (delta < MAX_TOUCH_TIME_ERROR) { /* 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; err = inode_change_ok(inode, iap); } } if (err) goto out_nfserr; /* 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 = get_lease(inode, FMODE_WRITE); if (err) goto out_nfserr; err = get_write_access(inode); if (err) goto out_nfserr; 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) && (imode & S_ISUID) && iap->ia_uid != inode->i_uid) { iap->ia_valid |= ATTR_MODE; iap->ia_mode = imode &= ~S_ISUID; } if ((iap->ia_valid & ATTR_GID) && (imode & S_ISGID) && iap->ia_gid != inode->i_gid) { iap->ia_valid |= ATTR_MODE; iap->ia_mode = imode &= ~S_ISGID; } /* Change the attributes. */ iap->ia_valid |= ATTR_CTIME; if (iap->ia_valid & ATTR_SIZE) { fh_lock(fhp); size_change = 1; } err = nfserr_notsync; if (!check_guard || guardtime == inode->i_ctime) { err = notify_change(dentry, iap); err = nfserrno(err); } if (size_change) { fh_unlock(fhp); 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;}#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 */ { NFS3_ACCESS_READ, MAY_READ }, { NFS3_ACCESS_EXECUTE, MAY_EXEC }, { NFS3_ACCESS_MODIFY, MAY_WRITE }, { NFS3_ACCESS_EXTEND, MAY_WRITE }, { 0, 0 }};intnfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access){ struct accessmap *map; struct svc_export *export; struct dentry *dentry; u32 query, result = 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; err2 = nfsd_permission(export, dentry, map->how); switch (err2) { case nfs_ok: result |= map->access; break; /* the following error codes just mean the access was not allowed, * rather than an error occurred */ case nfserr_rofs: case nfserr_acces: case nfserr_perm: /* simply don't "or" in the access bit. */ break; default: error = err2; goto out; } } } *access = result; out: return error;}#endif /* CONFIG_NFSD_V3 *//* * Open an existing file or directory. * The access argument indicates the type of open (read/write/lock) * N.B. After this call fhp needs an fh_put */intnfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access, struct file *filp){ struct dentry *dentry; struct inode *inode; int err; /* If we get here, then the client has already done an "open", and (hopefully) * checked permission - so allow OWNER_OVERRIDE in case a chmod has now revoked * permission */ err = fh_verify(rqstp, fhp, type, access | MAY_OWNER_OVERRIDE); if (err) goto out; dentry = fhp->fh_dentry; inode = dentry->d_inode; /* Disallow access to files with the append-only bit set or * with mandatory locking enabled */ err = nfserr_perm; if (IS_APPEND(inode) || IS_ISMNDLK(inode)) goto out; if (!inode->i_fop) goto out; /* * Check to see if there are any leases on this file. * This may block while leases are broken. */ err = get_lease(inode, (access & MAY_WRITE) ? FMODE_WRITE : 0); if (err) goto out_nfserr; if ((access & MAY_WRITE) && (err = get_write_access(inode)) != 0) goto out_nfserr; memset(filp, 0, sizeof(*filp)); filp->f_op = fops_get(inode->i_fop); atomic_set(&filp->f_count, 1); filp->f_dentry = dentry; filp->f_vfsmnt = fhp->fh_export->ex_mnt; if (access & MAY_WRITE) { filp->f_flags = O_WRONLY|O_LARGEFILE; filp->f_mode = FMODE_WRITE; DQUOT_INIT(inode); } else { filp->f_flags = O_RDONLY|O_LARGEFILE; filp->f_mode = FMODE_READ; } err = 0; if (filp->f_op && filp->f_op->open) { err = filp->f_op->open(inode, filp); if (err) { fops_put(filp->f_op); if (access & MAY_WRITE) put_write_access(inode); /* I nearly added put_filp() call here, but this filp * is really on callers stack frame. -DaveM */ atomic_dec(&filp->f_count); } }out_nfserr: if (err) err = nfserrno(err);out: return err;}/* * Close a file. */voidnfsd_close(struct file *filp){ struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; if (filp->f_op && filp->f_op->release) filp->f_op->release(inode, filp); fops_put(filp->f_op); if (filp->f_mode & FMODE_WRITE) put_write_access(inode);}/* * Sync a file * As this calls fsync (not fdatasync) there is no need for a write_inode * after it. */voidnfsd_sync(struct file *filp){ dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name); down(&filp->f_dentry->d_inode->i_sem); filp->f_op->fsync(filp, filp->f_dentry, 0); up(&filp->f_dentry->d_inode->i_sem);}voidnfsd_sync_dir(struct dentry *dp){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -