#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/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>
unsigned int p_count;
ino_t p_ino;
dev_t p_dev;
+ int p_set;
struct file_ra_state p_ra;
};
/* Obtain dentry and export. */
err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC);
if (err)
- goto out;
+ return err;
dparent = fhp->fh_dentry;
exp = fhp->fh_export;
+ exp_get(exp);
err = nfserr_acces;
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:
* If we are changing the size of the file, then
* we need to break all leases.
*/
- err = break_lease(inode, FMODE_WRITE);
- if (err)
+ err = break_lease(inode, FMODE_WRITE | O_NONBLOCK);
+ if (err == -EWOULDBLOCK)
+ err = -ETIMEDOUT;
+ if (err) /* ENOMEM or EWOULDBLOCK */
goto out_nfserr;
err = get_write_access(inode);
goto out;
}
+#if defined(CONFIG_NFSD_V4)
+
+static int
+set_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);
+}
+
+int
+nfsd4_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;
+
+ error = -EOPNOTSUPP;
+ if (inode->i_op == NULL)
+ goto out_err;
+ if (inode->i_op->getxattr == NULL)
+ goto out_err;
+
+ error = security_inode_getxattr(dentry, key);
+ if (error)
+ goto out_err;
+
+ buflen = inode->i_op->getxattr(dentry, key, NULL, 0);
+ if (buflen <= 0) {
+ error = buflen < 0 ? buflen : -ENODATA;
+ goto out_err;
+ }
+
+ buf = kmalloc(buflen, GFP_KERNEL);
+ if (buf == NULL) {
+ error = -ENOMEM;
+ goto out_err;
+ }
+
+ error = inode->i_op->getxattr(dentry, key, buf, buflen);
+ if (error < 0)
+ goto out_err;
+
+ pacl = posix_acl_from_xattr(buf, buflen);
+ out:
+ kfree(buf);
+ return pacl;
+ out_err:
+ pacl = ERR_PTR(error);
+ goto out;
+}
+
+int
+nfsd4_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
* 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
+ * 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 },
- { NFS3_ACCESS_EXTEND, MAY_WRITE },
+ { NFS3_ACCESS_MODIFY, MAY_WRITE|MAY_LOCAL_ACCESS },
+ { NFS3_ACCESS_EXTEND, MAY_WRITE|MAY_LOCAL_ACCESS },
{ 0, 0 }
};
*/
int
nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
- int access, struct file *filp)
+ int access, struct file **filp)
{
struct dentry *dentry;
struct inode *inode;
dentry = fhp->fh_dentry;
inode = dentry->d_inode;
- /* Disallow access to files with the append-only bit set or
- * with mandatory locking enabled
+ /* Disallow write access to files with the append-only bit set
+ * or any access when mandatory locking enabled
*/
err = nfserr_perm;
- if (IS_APPEND(inode) || IS_ISMNDLK(inode))
+ if (IS_APPEND(inode) && (access & MAY_WRITE))
goto out;
+ if (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 = break_lease(inode, (access & MAY_WRITE) ? FMODE_WRITE : 0);
- if (err)
+ err = break_lease(inode, O_NONBLOCK | ((access & MAY_WRITE) ? FMODE_WRITE : 0));
+ if (err == -EWOULDBLOCK)
+ err = -ETIMEDOUT;
+ if (err) /* NOMEM or WOULDBLOCK */
goto out_nfserr;
if (access & MAY_WRITE) {
- err = get_write_access(inode);
- if (err)
- goto out_nfserr;
-
flags = O_WRONLY|O_LARGEFILE;
DQUOT_INIT(inode);
}
-
- err = open_private_file(filp, dentry, flags);
- if (!err) {
- filp->f_vfsmnt = fhp->fh_export->ex_mnt;
- } else if (access & MAY_WRITE)
- put_write_access(inode);
-
+ *filp = dentry_open(dget(dentry), mntget(fhp->fh_export->ex_mnt), flags);
+ if (IS_ERR(*filp))
+ err = PTR_ERR(*filp);
out_nfserr:
if (err)
err = nfserrno(err);
void
nfsd_close(struct file *filp)
{
- struct dentry *dentry = filp->f_dentry;
- struct inode *inode = dentry->d_inode;
-
- close_private_file(filp);
- if (filp->f_mode & FMODE_WRITE)
- put_write_access(inode);
+ fput(filp);
}
/*
* Obtain the readahead parameters for the file
* specified by (dev, ino).
*/
-static spinlock_t ra_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(ra_lock);
static inline struct raparms *
nfsd_get_raparms(dev_t dev, ino_t ino)
ra = *frap;
ra->p_dev = dev;
ra->p_ino = ino;
- memset(&ra->p_ra, 0, sizeof(ra->p_ra));
+ ra->p_set = 0;
found:
if (rap != &raparm_cache) {
*rap = ra->p_next;
nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset , unsigned long size)
{
unsigned long count = desc->count;
- struct svc_rqst *rqstp = (struct svc_rqst *)desc->buf;
+ struct svc_rqst *rqstp = desc->arg.data;
if (size > count)
size = count;
*/
int
nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
- struct iovec *vec, int vlen, unsigned long *count)
+ struct kvec *vec, int vlen, unsigned long *count)
{
struct raparms *ra;
mm_segment_t oldfs;
int err;
- struct file file;
+ struct file *file;
struct inode *inode;
err = nfsd_open(rqstp, fhp, S_IFREG, MAY_READ, &file);
if (err)
goto out;
err = nfserr_perm;
- inode = file.f_dentry->d_inode;
+ inode = file->f_dentry->d_inode;
#ifdef MSNFS
if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
(!lock_may_read(inode, offset, *count)))
/* Get readahead parameters */
ra = nfsd_get_raparms(inode->i_sb->s_dev, inode->i_ino);
- if (ra)
- file.f_ra = ra->p_ra;
- if (file.f_op->sendfile) {
+ if (ra && ra->p_set)
+ file->f_ra = ra->p_ra;
+
+ if (file->f_op->sendfile) {
svc_pushback_unused_pages(rqstp);
- err = file.f_op->sendfile(&file, &offset, *count,
+ err = file->f_op->sendfile(file, &offset, *count,
nfsd_read_actor, rqstp);
} else {
oldfs = get_fs();
set_fs(KERNEL_DS);
- err = vfs_readv(&file, vec, vlen, &offset);
+ err = vfs_readv(file, (struct iovec __user *)vec, vlen, &offset);
set_fs(oldfs);
}
/* Write back readahead params */
- if (ra)
- ra->p_ra = file.f_ra;
+ if (ra) {
+ spin_lock(&ra_lock);
+ ra->p_ra = file->f_ra;
+ ra->p_set = 1;
+ ra->p_count--;
+ spin_unlock(&ra_lock);
+ }
if (err >= 0) {
nfsdstats.io_read += err;
*count = err;
err = 0;
- dnotify_parent(file.f_dentry, DN_ACCESS);
+ dnotify_parent(file->f_dentry, DN_ACCESS);
} else
err = nfserrno(err);
out_close:
- nfsd_close(&file);
+ nfsd_close(file);
out:
return err;
}
*/
int
nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
- struct iovec *vec, int vlen,
+ struct kvec *vec, int vlen,
unsigned long cnt, int *stablep)
{
struct svc_export *exp;
- struct file file;
+ struct file *file;
struct dentry *dentry;
struct inode *inode;
mm_segment_t oldfs;
#ifdef MSNFS
if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
- (!lock_may_write(file.f_dentry->d_inode, offset, cnt)))
+ (!lock_may_write(file->f_dentry->d_inode, offset, cnt)))
goto out_close;
#endif
- dentry = file.f_dentry;
+ dentry = file->f_dentry;
inode = dentry->d_inode;
exp = fhp->fh_export;
* flushing the data to disk is handled separately below.
*/
- if (file.f_op->fsync == 0) {/* COMMIT3 cannot work */
+ if (file->f_op->fsync == 0) {/* COMMIT3 cannot work */
stable = 2;
*stablep = 2; /* FILE_SYNC */
}
if (!EX_ISSYNC(exp))
stable = 0;
if (stable && !EX_WGATHER(exp))
- file.f_flags |= O_SYNC;
+ file->f_flags |= O_SYNC;
/* Write the data. */
oldfs = get_fs(); set_fs(KERNEL_DS);
- err = vfs_writev(&file, vec, vlen, &offset);
+ err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset);
set_fs(oldfs);
if (err >= 0) {
nfsdstats.io_write += cnt;
- dnotify_parent(file.f_dentry, DN_MODIFY);
+ dnotify_parent(file->f_dentry, DN_MODIFY);
}
/* clear setuid/setgid flag after write */
if (inode->i_state & I_DIRTY) {
dprintk("nfsd: write sync %d\n", current->pid);
- nfsd_sync(&file);
+ nfsd_sync(file);
}
#if 0
wake_up(&inode->i_wait);
else
err = nfserrno(err);
out_close:
- nfsd_close(&file);
+ nfsd_close(file);
out:
return err;
}
nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
loff_t offset, unsigned long count)
{
- struct file file;
+ struct file *file;
int err;
if ((u64)count > ~(u64)offset)
if ((err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file)) != 0)
return err;
if (EX_ISSYNC(fhp->fh_export)) {
- if (file.f_op && file.f_op->fsync) {
- nfsd_sync(&file);
+ if (file->f_op && file->f_op->fsync) {
+ nfsd_sync(file);
} else {
err = nfserr_notsupp;
}
}
- nfsd_close(&file);
+ nfsd_close(file);
return err;
}
#endif /* CONFIG_NFSD_V3 */
char *fname, int flen, struct iattr *iap,
int type, dev_t rdev, struct svc_fh *resfhp)
{
- struct dentry *dentry, *dchild;
+ struct dentry *dentry, *dchild = NULL;
struct inode *dirp;
int err;
goto out;
} else {
/* called from nfsd_proc_create */
- dchild = resfhp->fh_dentry;
+ dchild = dget(resfhp->fh_dentry);
if (!fhp->fh_locked) {
/* not actually possible */
printk(KERN_ERR
if (!err)
err = fh_update(resfhp);
out:
+ if (dchild && !IS_ERR(dchild))
+ dput(dchild);
return err;
out_nfserr:
struct svc_fh *resfhp, int createmode, u32 *verifier,
int *truncp)
{
- struct dentry *dentry, *dchild;
+ struct dentry *dentry, *dchild = NULL;
struct inode *dirp;
int err;
__u32 v_mtime=0, v_atime=0;
out:
fh_unlock(fhp);
+ if (dchild && !IS_ERR(dchild))
+ dput(dchild);
return err;
out_nfserr:
{
struct dentry *dentry, *dnew;
int err, cerr;
+ umode_t mode;
err = nfserr_noent;
if (!flen || !plen)
if (IS_ERR(dnew))
goto out_nfserr;
+ mode = S_IALLUGO;
+ /* Only the MODE ATTRibute is even vaguely meaningful */
+ if (iap && (iap->ia_valid & ATTR_MODE))
+ mode = iap->ia_mode & S_IALLUGO;
+
if (unlikely(path[plen] != 0)) {
char *path_alloced = kmalloc(plen+1, GFP_KERNEL);
if (path_alloced == NULL)
else {
strncpy(path_alloced, path, plen);
path_alloced[plen] = 0;
- err = vfs_symlink(dentry->d_inode, dnew, path_alloced);
+ err = vfs_symlink(dentry->d_inode, dnew, path_alloced, mode);
kfree(path_alloced);
}
} else
- err = vfs_symlink(dentry->d_inode, dnew, path);
+ err = vfs_symlink(dentry->d_inode, dnew, path, mode);
if (!err) {
if (EX_ISSYNC(fhp->fh_export))
nfsd_sync_dir(dentry);
- if (iap) {
- iap->ia_valid &= ATTR_MODE /* ~(ATTR_MODE|ATTR_UID|ATTR_GID)*/;
- if (iap->ia_valid) {
- iap->ia_valid |= ATTR_CTIME;
- iap->ia_mode = (iap->ia_mode&S_IALLUGO)
- | S_IFLNK;
- err = notify_change(dnew, iap);
- if (err)
- err = nfserrno(err);
- else if (EX_ISSYNC(fhp->fh_export))
- write_inode_now(dentry->d_inode, 1);
- }
- }
} else
err = nfserrno(err);
fh_unlock(fhp);
- /* Compose the fh so the dentry will be freed ... */
cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
+ dput(dnew);
if (err==0) err = cerr;
out:
return err;
struct readdir_cd *cdp, encode_dent_fn func)
{
int err;
- struct file file;
+ struct file *file;
loff_t offset = *offsetp;
err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file);
if (err)
goto out;
- if (offset > ~(u32) 0)
- goto out_close;
- file.f_pos = offset;
+ 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
do {
cdp->err = nfserr_eof; /* will be cleared on successful read */
- err = vfs_readdir(&file, (filldir_t) func, cdp);
+ err = vfs_readdir(file, (filldir_t) func, cdp);
} while (err >=0 && cdp->err == nfs_ok);
if (err)
err = nfserrno(err);
else
err = cdp->err;
- *offsetp = file.f_pos;
+ *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);
+ nfsd_close(file);
out:
return err;
}