📄 vfs.c
字号:
out_nfserr: err = nfserrno(host_err); goto out;}/* * Create a hardlink * N.B. After this call _both_ ffhp and tfhp need an fh_put */__be32nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *name, int len, struct svc_fh *tfhp){ struct dentry *ddir, *dnew, *dold; struct inode *dirp, *dest; __be32 err; int host_err; err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE); if (err) goto out; err = fh_verify(rqstp, tfhp, -S_IFDIR, MAY_NOP); if (err) goto out; err = nfserr_perm; if (!len) goto out; err = nfserr_exist; if (isdotent(name, len)) goto out; fh_lock_nested(ffhp, I_MUTEX_PARENT); ddir = ffhp->fh_dentry; dirp = ddir->d_inode; dnew = lookup_one_len(name, ddir, len); host_err = PTR_ERR(dnew); if (IS_ERR(dnew)) goto out_nfserr; dold = tfhp->fh_dentry; dest = dold->d_inode; host_err = vfs_link(dold, dirp, dnew); if (!host_err) { if (EX_ISSYNC(ffhp->fh_export)) { err = nfserrno(nfsd_sync_dir(ddir)); write_inode_now(dest, 1); } err = 0; } else { if (host_err == -EXDEV && rqstp->rq_vers == 2) err = nfserr_acces; else err = nfserrno(host_err); } dput(dnew);out_unlock: fh_unlock(ffhp);out: return err;out_nfserr: err = nfserrno(host_err); goto out_unlock;}/* * Rename a file * N.B. After this call _both_ ffhp and tfhp need an fh_put */__be32nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, struct svc_fh *tfhp, char *tname, int tlen){ struct dentry *fdentry, *tdentry, *odentry, *ndentry, *trap; struct inode *fdir, *tdir; __be32 err; int host_err; err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE); if (err) goto out; err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE); if (err) goto out; fdentry = ffhp->fh_dentry; fdir = fdentry->d_inode; tdentry = tfhp->fh_dentry; tdir = tdentry->d_inode; err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev; if (ffhp->fh_export != tfhp->fh_export) goto out; err = nfserr_perm; if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen)) goto out; /* cannot use fh_lock as we need deadlock protective ordering * so do it by hand */ trap = lock_rename(tdentry, fdentry); ffhp->fh_locked = tfhp->fh_locked = 1; fill_pre_wcc(ffhp); fill_pre_wcc(tfhp); odentry = lookup_one_len(fname, fdentry, flen); host_err = PTR_ERR(odentry); if (IS_ERR(odentry)) goto out_nfserr; host_err = -ENOENT; if (!odentry->d_inode) goto out_dput_old; host_err = -EINVAL; if (odentry == trap) goto out_dput_old; ndentry = lookup_one_len(tname, tdentry, tlen); host_err = PTR_ERR(ndentry); if (IS_ERR(ndentry)) goto out_dput_old; host_err = -ENOTEMPTY; if (ndentry == trap) goto out_dput_new;#ifdef MSNFS if ((ffhp->fh_export->ex_flags & NFSEXP_MSNFS) && ((atomic_read(&odentry->d_count) > 1) || (atomic_read(&ndentry->d_count) > 1))) { host_err = -EPERM; } else#endif host_err = vfs_rename(fdir, odentry, tdir, ndentry); if (!host_err && EX_ISSYNC(tfhp->fh_export)) { host_err = nfsd_sync_dir(tdentry); if (!host_err) host_err = nfsd_sync_dir(fdentry); } out_dput_new: dput(ndentry); out_dput_old: dput(odentry); out_nfserr: err = nfserrno(host_err); /* we cannot reply on fh_unlock on the two filehandles, * as that would do the wrong thing if the two directories * were the same, so again we do it by hand */ fill_post_wcc(ffhp); fill_post_wcc(tfhp); unlock_rename(tdentry, fdentry); ffhp->fh_locked = tfhp->fh_locked = 0;out: return err;}/* * Unlink a file or directory * N.B. After this call fhp needs an fh_put */__be32nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, char *fname, int flen){ struct dentry *dentry, *rdentry; struct inode *dirp; __be32 err; int host_err; err = nfserr_acces; if (!flen || isdotent(fname, flen)) goto out; err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE); if (err) goto out; fh_lock_nested(fhp, I_MUTEX_PARENT); dentry = fhp->fh_dentry; dirp = dentry->d_inode; rdentry = lookup_one_len(fname, dentry, flen); host_err = PTR_ERR(rdentry); if (IS_ERR(rdentry)) goto out_nfserr; if (!rdentry->d_inode) { dput(rdentry); err = nfserr_noent; goto out; } if (!type) type = rdentry->d_inode->i_mode & S_IFMT; if (type != S_IFDIR) { /* It's UNLINK */#ifdef MSNFS if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && (atomic_read(&rdentry->d_count) > 1)) { host_err = -EPERM; } else#endif host_err = vfs_unlink(dirp, rdentry); } else { /* It's RMDIR */ host_err = vfs_rmdir(dirp, rdentry); } dput(rdentry); if (host_err) goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) host_err = nfsd_sync_dir(dentry);out_nfserr: err = nfserrno(host_err);out: return err;}/* * Read entries from a directory. * The NFSv3/4 verifier we ignore for now. */__be32nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, struct readdir_cd *cdp, filldir_t func){ __be32 err; int host_err; struct file *file; loff_t offset = *offsetp; err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file); if (err) goto out; offset = vfs_llseek(file, offset, 0); if (offset < 0) { err = nfserrno((int)offset); goto out_close; } /* * Read the directory entries. This silly loop is necessary because * readdir() is not guaranteed to fill up the entire buffer, but * may choose to do less. */ do { cdp->err = nfserr_eof; /* will be cleared on successful read */ host_err = vfs_readdir(file, func, cdp); } while (host_err >=0 && cdp->err == nfs_ok); if (host_err) err = nfserrno(host_err); else err = cdp->err; *offsetp = vfs_llseek(file, 0, 1); if (err == nfserr_eof || err == nfserr_toosmall) err = nfs_ok; /* can still be found in ->err */out_close: nfsd_close(file);out: return err;}/* * Get file system stats * N.B. After this call fhp needs an fh_put */__be32nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat){ __be32 err = fh_verify(rqstp, fhp, 0, MAY_NOP); if (!err && vfs_statfs(fhp->fh_dentry,stat)) err = nfserr_io; return err;}static int exp_rdonly(struct svc_rqst *rqstp, struct svc_export *exp){ return nfsexp_flags(rqstp, exp) & NFSEXP_READONLY;}/* * Check for a user's access permissions to this inode. */__be32nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, struct dentry *dentry, int acc){ struct inode *inode = dentry->d_inode; int err; if (acc == MAY_NOP) return 0;#if 0 dprintk("nfsd: permission 0x%x%s%s%s%s%s%s%s mode 0%o%s%s%s\n", acc, (acc & MAY_READ)? " read" : "", (acc & MAY_WRITE)? " write" : "", (acc & MAY_EXEC)? " exec" : "", (acc & MAY_SATTR)? " sattr" : "", (acc & MAY_TRUNC)? " trunc" : "", (acc & MAY_LOCK)? " lock" : "", (acc & MAY_OWNER_OVERRIDE)? " owneroverride" : "", inode->i_mode, IS_IMMUTABLE(inode)? " immut" : "", IS_APPEND(inode)? " append" : "", IS_RDONLY(inode)? " ro" : ""); dprintk(" owner %d/%d user %d/%d\n", inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);#endif /* Normally we reject any write/sattr etc access on a read-only file * system. But if it is IRIX doing check on write-access for a * device special file, we ignore rofs. */ if (!(acc & MAY_LOCAL_ACCESS)) if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { if (exp_rdonly(rqstp, exp) || IS_RDONLY(inode)) return nfserr_rofs; if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) return nfserr_perm; } if ((acc & MAY_TRUNC) && IS_APPEND(inode)) return nfserr_perm; if (acc & MAY_LOCK) { /* If we cannot rely on authentication in NLM requests, * just allow locks, otherwise require read permission, or * ownership */ if (exp->ex_flags & NFSEXP_NOAUTHNLM) return 0; else acc = MAY_READ | MAY_OWNER_OVERRIDE; } /* * The file owner always gets access permission for accesses that * would normally be checked at open time. This is to make * file access work even when the client has done a fchmod(fd, 0). * * However, `cp foo bar' should fail nevertheless when bar is * readonly. A sensible way to do this might be to reject all * attempts to truncate a read-only file, because a creat() call * always implies file truncation. * ... but this isn't really fair. A process may reasonably call * ftruncate on an open file descriptor on a file with perm 000. * We must trust the client to do permission checking - using "ACCESS" * with NFSv3. */ if ((acc & MAY_OWNER_OVERRIDE) && inode->i_uid == current->fsuid) return 0; err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), NULL); /* Allow read access to binaries even when mode 111 */ if (err == -EACCES && S_ISREG(inode->i_mode) && acc == (MAY_READ | MAY_OWNER_OVERRIDE)) err = permission(inode, MAY_EXEC, NULL); return err? nfserrno(err) : 0;}voidnfsd_racache_shutdown(void){ if (!raparml) return; dprintk("nfsd: freeing readahead buffers.\n"); kfree(raparml); raparml = NULL;}/* * Initialize readahead param cache */intnfsd_racache_init(int cache_size){ int i; int j = 0; int nperbucket; if (raparml) return 0; if (cache_size < 2*RAPARM_HASH_SIZE) cache_size = 2*RAPARM_HASH_SIZE; raparml = kcalloc(cache_size, sizeof(struct raparms), GFP_KERNEL); if (!raparml) { printk(KERN_WARNING "nfsd: Could not allocate memory read-ahead cache.\n"); return -ENOMEM; } dprintk("nfsd: allocating %d readahead buffers.\n", cache_size); for (i = 0 ; i < RAPARM_HASH_SIZE ; i++) { raparm_hash[i].pb_head = NULL; spin_lock_init(&raparm_hash[i].pb_lock); } nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE); for (i = 0; i < cache_size - 1; i++) { if (i % nperbucket == 0) raparm_hash[j++].pb_head = raparml + i; if (i % nperbucket < nperbucket-1) raparml[i].p_next = raparml + i + 1; } nfsdstats.ra_size = cache_size; return 0;}#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)struct posix_acl *nfsd_get_posix_acl(struct svc_fh *fhp, int type){ struct inode *inode = fhp->fh_dentry->d_inode; char *name; void *value = NULL; ssize_t size; struct posix_acl *acl; if (!IS_POSIXACL(inode)) return ERR_PTR(-EOPNOTSUPP); switch (type) { case ACL_TYPE_ACCESS: name = POSIX_ACL_XATTR_ACCESS; break; case ACL_TYPE_DEFAULT: name = POSIX_ACL_XATTR_DEFAULT; break; default: return ERR_PTR(-EOPNOTSUPP); } size = nfsd_getxattr(fhp->fh_dentry, name, &value); if (size < 0) return ERR_PTR(size); acl = posix_acl_from_xattr(value, size); kfree(value); return acl;}intnfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl){ struct inode *inode = fhp->fh_dentry->d_inode; char *name; void *value = NULL; size_t size; int error; if (!IS_POSIXACL(inode) || !inode->i_op || !inode->i_op->setxattr || !inode->i_op->removexattr) return -EOPNOTSUPP; switch(type) { case ACL_TYPE_ACCESS: name = POSIX_ACL_XATTR_ACCESS; break; case ACL_TYPE_DEFAULT: name = POSIX_ACL_XATTR_DEFAULT; break; default: return -EOPNOTSUPP; } if (acl && acl->a_count) { size = posix_acl_xattr_size(acl->a_count); value = kmalloc(size, GFP_KERNEL); if (!value) return -ENOMEM; error = posix_acl_to_xattr(acl, value, size); if (error < 0) goto getout; size = error; } else size = 0; if (size) error = vfs_setxattr(fhp->fh_dentry, name, value, size, 0); else { if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT) error = 0; else { error = vfs_removexattr(fhp->fh_dentry, name); if (error == -ENODATA) error = 0; } }getout: kfree(value); return error;}#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -