X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fnfsd%2Fvfs.c;h=95645569f1d74a2ee9f044966e81182b32d070bd;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=d50269bc5a7c52ced2c5a9a0cae26705d1d725f1;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index d50269bc5..95645569f 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_NFSD_V3 @@ -43,7 +45,16 @@ #endif /* CONFIG_NFSD_V3 */ #include #include -#include +#include +#include +#include +#include +#ifdef CONFIG_NFSD_V4 +#include +#include +#include +#include +#endif /* CONFIG_NFSD_V4 */ #include @@ -69,6 +80,7 @@ struct raparms { unsigned int p_count; ino_t p_ino; dev_t p_dev; + int p_set; struct file_ra_state p_ra; }; @@ -242,12 +254,19 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, /* Get inode */ err = fh_verify(rqstp, fhp, ftype, accmode); - if (err || !iap->ia_valid) + 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. @@ -291,8 +310,10 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, * 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); @@ -344,6 +365,162 @@ out_nfserr: 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 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; + + 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; +} + +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 == -EINVAL) { + error = nfserr_attrnotsupp; + goto out; + } else if (error < 0) + goto out_nfserr; + + if (pacl) { + error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS); + if (error < 0) + goto out_nfserr; + } + + if (dpacl) { + error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_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) +{ + 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; +} + +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, POSIX_ACL_XATTR_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, POSIX_ACL_XATTR_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 @@ -376,12 +553,13 @@ static struct accessmap nfs3_anyaccess[] = { * 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 } }; @@ -454,7 +632,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor */ 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; @@ -472,12 +650,15 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, 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; @@ -485,26 +666,20 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, * 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); @@ -518,12 +693,7 @@ out: 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); } /* @@ -531,43 +701,50 @@ nfsd_close(struct file *filp) * As this calls fsync (not fdatasync) there is no need for a write_inode * after it. */ -inline void nfsd_dosync(struct file *filp, struct dentry *dp, - struct file_operations *fop) +static inline int nfsd_dosync(struct file *filp, struct dentry *dp, + const struct file_operations *fop) { struct inode *inode = dp->d_inode; int (*fsync) (struct file *, struct dentry *, int); + int err; - filemap_fdatawrite(inode->i_mapping); - if (fop && (fsync = fop->fsync)) - fsync(filp, dp, 0); - filemap_fdatawait(inode->i_mapping); + err = filemap_fdatawrite(inode->i_mapping); + if (err == 0 && fop && (fsync = fop->fsync)) + err = fsync(filp, dp, 0); + if (err == 0) + err = filemap_fdatawait(inode->i_mapping); + + return err; } -void +static int nfsd_sync(struct file *filp) { + int err; struct inode *inode = filp->f_dentry->d_inode; dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name); - down(&inode->i_sem); - nfsd_dosync(filp, filp->f_dentry, filp->f_op); - up(&inode->i_sem); + mutex_lock(&inode->i_mutex); + err=nfsd_dosync(filp, filp->f_dentry, filp->f_op); + mutex_unlock(&inode->i_mutex); + + return err; } -void +int nfsd_sync_dir(struct dentry *dp) { - nfsd_dosync(NULL, dp, dp->d_inode->i_fop); + return nfsd_dosync(NULL, dp, dp->d_inode->i_fop); } /* * 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, struct address_space *mapping) +nfsd_get_raparms(dev_t dev, ino_t ino) { struct raparms *ra, **rap, **frap = NULL; int depth = 0; @@ -589,7 +766,7 @@ nfsd_get_raparms(dev_t dev, ino_t ino, struct address_space *mapping) ra = *frap; ra->p_dev = dev; ra->p_ino = ino; - file_ra_state_init(&ra->p_ra, mapping); + ra->p_set = 0; found: if (rap != &raparm_cache) { *rap = ra->p_next; @@ -634,101 +811,91 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset return size; } -/* - * Read data from a file. count must contain the requested read count - * on entry. On return, *count contains the number of bytes actually read. - * N.B. After this call fhp needs an fh_put - */ -int -nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, - struct kvec *vec, int vlen, unsigned long *count) +static int +nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, + loff_t offset, struct kvec *vec, int vlen, unsigned long *count) { + struct inode *inode; struct raparms *ra; mm_segment_t oldfs; int err; - 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))) - goto out_close; + goto out; #endif /* Get readahead parameters */ - ra = nfsd_get_raparms(inode->i_sb->s_dev, inode->i_ino, - inode->i_mapping->host->i_mapping); - if (ra) - file.f_ra = ra->p_ra; + ra = nfsd_get_raparms(inode->i_sb->s_dev, inode->i_ino); + + if (ra && ra->p_set) + file->f_ra = ra->p_ra; - if (file.f_op->sendfile) { + 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, (struct iovec __user *)vec, vlen, &offset); + err = vfs_readv(file, (struct iovec __user *)vec, vlen, &offset); set_fs(oldfs); } /* Write back readahead params */ if (ra) { spin_lock(&ra_lock); - ra->p_ra = file.f_ra; + 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); + fsnotify_access(file->f_dentry); } else err = nfserrno(err); -out_close: - nfsd_close(&file); out: return err; } -/* - * Write data to a file. - * The stable flag requests synchronous writes. - * N.B. After this call fhp needs an fh_put - */ -int -nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, - struct kvec *vec, int vlen, +static void kill_suid(struct dentry *dentry) +{ + struct iattr ia; + ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID; + + mutex_lock(&dentry->d_inode->i_mutex); + notify_change(dentry, &ia); + mutex_unlock(&dentry->d_inode->i_mutex); +} + +static int +nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, + loff_t offset, struct kvec *vec, int vlen, unsigned long cnt, int *stablep) { struct svc_export *exp; - struct file file; struct dentry *dentry; struct inode *inode; mm_segment_t oldfs; int err = 0; int stable = *stablep; - err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file); - if (err) - goto out; - if (!cnt) - goto out_close; +#ifdef MSNFS err = nfserr_perm; -#ifdef MSNFS if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && - (!lock_may_write(file.f_dentry->d_inode, offset, cnt))) - goto out_close; + (!lock_may_write(file->f_dentry->d_inode, offset, cnt))) + goto out; #endif - dentry = file.f_dentry; + dentry = file->f_dentry; inode = dentry->d_inode; exp = fhp->fh_export; @@ -741,7 +908,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, * 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 */ } @@ -749,26 +916,20 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, 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, (struct iovec __user *)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); + fsnotify_modify(file->f_dentry); } /* clear setuid/setgid flag after write */ - if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) { - struct iattr ia; - ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID; - - down(&inode->i_sem); - notify_change(dentry, &ia); - up(&inode->i_sem); - } + if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) + kill_suid(dentry); if (err >= 0 && stable) { static ino_t last_ino; @@ -790,14 +951,13 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, if (atomic_read(&inode->i_writecount) > 1 || (last_ino == inode->i_ino && last_dev == inode->i_sb->s_dev)) { dprintk("nfsd: write defer %d\n", current->pid); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((HZ+99)/100); + msleep(10); dprintk("nfsd: write resume %d\n", current->pid); } if (inode->i_state & I_DIRTY) { dprintk("nfsd: write sync %d\n", current->pid); - nfsd_sync(&file); + err=nfsd_sync(file); } #if 0 wake_up(&inode->i_wait); @@ -812,12 +972,71 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, err = 0; else err = nfserrno(err); -out_close: - nfsd_close(&file); out: return err; } +/* + * Read data from a file. count must contain the requested read count + * on entry. On return, *count contains the number of bytes actually read. + * N.B. After this call fhp needs an fh_put + */ +int +nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, + loff_t offset, struct kvec *vec, int vlen, + unsigned long *count) +{ + int err; + + if (file) { + err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, + MAY_READ|MAY_OWNER_OVERRIDE); + if (err) + goto out; + err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count); + } else { + err = nfsd_open(rqstp, fhp, S_IFREG, MAY_READ, &file); + if (err) + goto out; + err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count); + nfsd_close(file); + } +out: + return err; +} + +/* + * Write data to a file. + * The stable flag requests synchronous writes. + * N.B. After this call fhp needs an fh_put + */ +int +nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, + loff_t offset, struct kvec *vec, int vlen, unsigned long cnt, + int *stablep) +{ + int err = 0; + + if (file) { + err = nfsd_permission(fhp->fh_export, fhp->fh_dentry, + MAY_WRITE|MAY_OWNER_OVERRIDE); + if (err) + goto out; + err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen, cnt, + stablep); + } else { + err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file); + if (err) + goto out; + + if (cnt) + err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen, + cnt, stablep); + nfsd_close(file); + } +out: + return err; +} #ifdef CONFIG_NFSD_V3 /* @@ -832,7 +1051,7 @@ int 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) @@ -841,14 +1060,14 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, 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) { + err = nfserrno(nfsd_sync(file)); } else { err = nfserr_notsupp; } } - nfsd_close(&file); + nfsd_close(file); return err; } #endif /* CONFIG_NFSD_V3 */ @@ -910,7 +1129,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, "nfsd_create: parent %s/%s not locked!\n", dentry->d_parent->d_name.name, dentry->d_name.name); - err = -EIO; + err = nfserr_io; goto out; } } @@ -937,13 +1156,13 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, err = vfs_create(dirp, dchild, iap->ia_mode, NULL); break; case S_IFDIR: - err = vfs_mkdir(dirp, dchild, iap->ia_mode); + err = vfs_mkdir(dirp, dchild, iap->ia_mode, NULL); break; case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: - err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev); + err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev, NULL); break; default: printk("nfsd: bad file type %o in nfsd_create\n", type); @@ -953,7 +1172,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) { - nfsd_sync_dir(dentry); + err = nfserrno(nfsd_sync_dir(dentry)); write_inode_now(dchild->d_inode, 1); } @@ -963,9 +1182,11 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, * send along the gid when it tries to implement setgid * directories via NFS. */ - err = 0; - if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) - err = nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0); + if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) { + int err2 = nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0); + if (err2) + err = err2; + } /* * Update the file handle to get the new inode info. */ @@ -1084,17 +1305,10 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) { - nfsd_sync_dir(dentry); + err = nfserrno(nfsd_sync_dir(dentry)); /* setattr will sync the child (or not) */ } - /* - * Update the filehandle to get the new inode info. - */ - err = fh_update(resfhp); - if (err) - goto out; - if (createmode == NFS3_CREATE_EXCLUSIVE) { /* Cram the verifier into atime/mtime/mode */ iap->ia_valid = ATTR_MTIME|ATTR_ATIME @@ -1115,8 +1329,17 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, * implement setgid directories via NFS. Clear out all that cruft. */ set_attr: - if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID)) != 0) - err = nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0); + if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID)) != 0) { + int err2 = nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0); + if (err2) + err = err2; + } + + /* + * Update the filehandle to get the new inode info. + */ + if (!err) + err = fh_update(resfhp); out: fh_unlock(fhp); @@ -1219,16 +1442,18 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, else { strncpy(path_alloced, path, plen); path_alloced[plen] = 0; - err = vfs_symlink(dentry->d_inode, dnew, path_alloced, mode); + err = vfs_symlink(dentry->d_inode, dnew, + path_alloced, mode, NULL); kfree(path_alloced); } } else - err = vfs_symlink(dentry->d_inode, dnew, path, mode); + err = vfs_symlink(dentry->d_inode, dnew, + path, mode, NULL); - if (!err) { + if (!err) if (EX_ISSYNC(fhp->fh_export)) - nfsd_sync_dir(dentry); - } else + err = nfsd_sync_dir(dentry); + if (err) err = nfserrno(err); fh_unlock(fhp); @@ -1281,10 +1506,10 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, dold = tfhp->fh_dentry; dest = dold->d_inode; - err = vfs_link(dold, dirp, dnew); + err = vfs_link(dold, dirp, dnew, NULL); if (!err) { if (EX_ISSYNC(ffhp->fh_export)) { - nfsd_sync_dir(ddir); + err = nfserrno(nfsd_sync_dir(ddir)); write_inode_now(dest, 1); } } else { @@ -1368,13 +1593,14 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, if ((ffhp->fh_export->ex_flags & NFSEXP_MSNFS) && ((atomic_read(&odentry->d_count) > 1) || (atomic_read(&ndentry->d_count) > 1))) { - err = nfserr_perm; + err = -EPERM; } else #endif err = vfs_rename(fdir, odentry, tdir, ndentry); if (!err && EX_ISSYNC(tfhp->fh_export)) { - nfsd_sync_dir(tdentry); - nfsd_sync_dir(fdentry); + err = nfsd_sync_dir(tdentry); + if (!err) + err = nfsd_sync_dir(fdentry); } out_dput_new: @@ -1439,27 +1665,24 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, #ifdef MSNFS if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && (atomic_read(&rdentry->d_count) > 1)) { - err = nfserr_perm; + err = -EPERM; } else #endif - err = vfs_unlink(dirp, rdentry); + err = vfs_unlink(dirp, rdentry, NULL); } else { /* It's RMDIR */ - err = vfs_rmdir(dirp, rdentry); + err = vfs_rmdir(dirp, rdentry, NULL); } dput(rdentry); - if (err) - goto out_nfserr; - if (EX_ISSYNC(fhp->fh_export)) - nfsd_sync_dir(dentry); - -out: - return err; + if (err == 0 && + EX_ISSYNC(fhp->fh_export)) + err = nfsd_sync_dir(dentry); out_nfserr: err = nfserrno(err); - goto out; +out: + return err; } /* @@ -1471,14 +1694,14 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, 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; - offset = vfs_llseek(&file, offset, 0); + offset = vfs_llseek(file, offset, 0); if (offset < 0) { err = nfserrno((int)offset); goto out_close; @@ -1492,18 +1715,18 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, 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 = vfs_llseek(&file, 0, 1); + *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; } @@ -1556,7 +1779,8 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) */ if (!(acc & MAY_LOCAL_ACCESS)) if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { - if (EX_RDONLY(exp) || IS_RDONLY(inode)) + if (EX_RDONLY(exp) || IS_RDONLY(inode) + || MNT_IS_RDONLY(exp->ex_mnt)) return nfserr_rofs; if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) return nfserr_perm; @@ -1639,3 +1863,89 @@ nfsd_racache_init(int cache_size) 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; +} + +int +nfsd_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) */