Loading fs/nfsd/nfs4proc.c +152 −10 Original line number Diff line number Diff line Loading @@ -37,6 +37,8 @@ #include <linux/falloc.h> #include <linux/slab.h> #include <linux/kthread.h> #include <linux/namei.h> #include <linux/sunrpc/addr.h> #include <linux/nfs_ssc.h> Loading Loading @@ -235,6 +237,154 @@ static void nfsd4_set_open_owner_reply_cache(struct nfsd4_compound_state *cstate &resfh->fh_handle); } static inline bool nfsd4_create_is_exclusive(int createmode) { return createmode == NFS4_CREATE_EXCLUSIVE || createmode == NFS4_CREATE_EXCLUSIVE4_1; } /* * Implement NFSv4's unchecked, guarded, and exclusive create * semantics for regular files. Open state for this new file is * subsequently fabricated in nfsd4_process_open2(). * * Upon return, caller must release @fhp and @resfhp. */ static __be32 nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct svc_fh *resfhp, struct nfsd4_open *open) { struct iattr *iap = &open->op_iattr; struct dentry *parent, *child; __u32 v_mtime, v_atime; struct inode *inode; __be32 status; int host_err; if (isdotent(open->op_fname, open->op_fnamelen)) return nfserr_exist; if (!(iap->ia_valid & ATTR_MODE)) iap->ia_mode = 0; status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC); if (status != nfs_ok) return status; parent = fhp->fh_dentry; inode = d_inode(parent); host_err = fh_want_write(fhp); if (host_err) return nfserrno(host_err); fh_lock_nested(fhp, I_MUTEX_PARENT); child = lookup_one_len(open->op_fname, parent, open->op_fnamelen); if (IS_ERR(child)) { status = nfserrno(PTR_ERR(child)); goto out; } if (d_really_is_negative(child)) { status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); if (status != nfs_ok) goto out; } status = fh_compose(resfhp, fhp->fh_export, child, fhp); if (status != nfs_ok) goto out; v_mtime = 0; v_atime = 0; if (nfsd4_create_is_exclusive(open->op_createmode)) { u32 *verifier = (u32 *)open->op_verf.data; /* * Solaris 7 gets confused (bugid 4218508) if these have * the high bit set, as do xfs filesystems without the * "bigtime" feature. So just clear the high bits. If this * is ever changed to use different attrs for storing the * verifier, then do_open_lookup() will also need to be * fixed accordingly. */ v_mtime = verifier[0] & 0x7fffffff; v_atime = verifier[1] & 0x7fffffff; } if (d_really_is_positive(child)) { status = nfs_ok; switch (open->op_createmode) { case NFS4_CREATE_UNCHECKED: if (!d_is_reg(child)) break; /* * In NFSv4, we don't want to truncate the file * now. This would be wrong if the OPEN fails for * some other reason. Furthermore, if the size is * nonzero, we should ignore it according to spec! */ open->op_truncate = (iap->ia_valid & ATTR_SIZE) && !iap->ia_size; break; case NFS4_CREATE_GUARDED: status = nfserr_exist; break; case NFS4_CREATE_EXCLUSIVE: if (d_inode(child)->i_mtime.tv_sec == v_mtime && d_inode(child)->i_atime.tv_sec == v_atime && d_inode(child)->i_size == 0) { open->op_created = true; break; /* subtle */ } status = nfserr_exist; break; case NFS4_CREATE_EXCLUSIVE4_1: if (d_inode(child)->i_mtime.tv_sec == v_mtime && d_inode(child)->i_atime.tv_sec == v_atime && d_inode(child)->i_size == 0) { open->op_created = true; goto set_attr; /* subtle */ } status = nfserr_exist; } goto out; } if (!IS_POSIXACL(inode)) iap->ia_mode &= ~current_umask(); host_err = vfs_create(&init_user_ns, inode, child, iap->ia_mode, true); if (host_err < 0) { status = nfserrno(host_err); goto out; } open->op_created = true; /* A newly created file already has a file size of zero. */ if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0)) iap->ia_valid &= ~ATTR_SIZE; if (nfsd4_create_is_exclusive(open->op_createmode)) { iap->ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_MTIME_SET|ATTR_ATIME_SET; iap->ia_mtime.tv_sec = v_mtime; iap->ia_atime.tv_sec = v_atime; iap->ia_mtime.tv_nsec = 0; iap->ia_atime.tv_nsec = 0; } set_attr: status = nfsd_create_setattr(rqstp, fhp, resfhp, iap); out: fh_unlock(fhp); if (child && !IS_ERR(child)) dput(child); fh_drop_write(fhp); return status; } static __be32 do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_open *open, struct svc_fh **resfh) { Loading Loading @@ -264,16 +414,8 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru * yes | yes | GUARDED4 | GUARDED4 */ /* * Note: create modes (UNCHECKED,GUARDED...) are the same * in NFSv4 as in v3 except EXCLUSIVE4_1. */ current->fs->umask = open->op_umask; status = do_nfsd_create(rqstp, current_fh, open->op_fname, open->op_fnamelen, &open->op_iattr, *resfh, open->op_createmode, (u32 *)open->op_verf.data, &open->op_truncate, &open->op_created); status = nfsd4_create_file(rqstp, current_fh, *resfh, open); current->fs->umask = 0; if (!status && open->op_label.len) Loading @@ -284,7 +426,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru * use the returned bitmask to indicate which attributes * we used to store the verifier: */ if (nfsd_create_is_exclusive(open->op_createmode) && status == 0) if (nfsd4_create_is_exclusive(open->op_createmode) && status == 0) open->op_bmval[1] |= (FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_MODIFY); } else Loading Loading
fs/nfsd/nfs4proc.c +152 −10 Original line number Diff line number Diff line Loading @@ -37,6 +37,8 @@ #include <linux/falloc.h> #include <linux/slab.h> #include <linux/kthread.h> #include <linux/namei.h> #include <linux/sunrpc/addr.h> #include <linux/nfs_ssc.h> Loading Loading @@ -235,6 +237,154 @@ static void nfsd4_set_open_owner_reply_cache(struct nfsd4_compound_state *cstate &resfh->fh_handle); } static inline bool nfsd4_create_is_exclusive(int createmode) { return createmode == NFS4_CREATE_EXCLUSIVE || createmode == NFS4_CREATE_EXCLUSIVE4_1; } /* * Implement NFSv4's unchecked, guarded, and exclusive create * semantics for regular files. Open state for this new file is * subsequently fabricated in nfsd4_process_open2(). * * Upon return, caller must release @fhp and @resfhp. */ static __be32 nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct svc_fh *resfhp, struct nfsd4_open *open) { struct iattr *iap = &open->op_iattr; struct dentry *parent, *child; __u32 v_mtime, v_atime; struct inode *inode; __be32 status; int host_err; if (isdotent(open->op_fname, open->op_fnamelen)) return nfserr_exist; if (!(iap->ia_valid & ATTR_MODE)) iap->ia_mode = 0; status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC); if (status != nfs_ok) return status; parent = fhp->fh_dentry; inode = d_inode(parent); host_err = fh_want_write(fhp); if (host_err) return nfserrno(host_err); fh_lock_nested(fhp, I_MUTEX_PARENT); child = lookup_one_len(open->op_fname, parent, open->op_fnamelen); if (IS_ERR(child)) { status = nfserrno(PTR_ERR(child)); goto out; } if (d_really_is_negative(child)) { status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); if (status != nfs_ok) goto out; } status = fh_compose(resfhp, fhp->fh_export, child, fhp); if (status != nfs_ok) goto out; v_mtime = 0; v_atime = 0; if (nfsd4_create_is_exclusive(open->op_createmode)) { u32 *verifier = (u32 *)open->op_verf.data; /* * Solaris 7 gets confused (bugid 4218508) if these have * the high bit set, as do xfs filesystems without the * "bigtime" feature. So just clear the high bits. If this * is ever changed to use different attrs for storing the * verifier, then do_open_lookup() will also need to be * fixed accordingly. */ v_mtime = verifier[0] & 0x7fffffff; v_atime = verifier[1] & 0x7fffffff; } if (d_really_is_positive(child)) { status = nfs_ok; switch (open->op_createmode) { case NFS4_CREATE_UNCHECKED: if (!d_is_reg(child)) break; /* * In NFSv4, we don't want to truncate the file * now. This would be wrong if the OPEN fails for * some other reason. Furthermore, if the size is * nonzero, we should ignore it according to spec! */ open->op_truncate = (iap->ia_valid & ATTR_SIZE) && !iap->ia_size; break; case NFS4_CREATE_GUARDED: status = nfserr_exist; break; case NFS4_CREATE_EXCLUSIVE: if (d_inode(child)->i_mtime.tv_sec == v_mtime && d_inode(child)->i_atime.tv_sec == v_atime && d_inode(child)->i_size == 0) { open->op_created = true; break; /* subtle */ } status = nfserr_exist; break; case NFS4_CREATE_EXCLUSIVE4_1: if (d_inode(child)->i_mtime.tv_sec == v_mtime && d_inode(child)->i_atime.tv_sec == v_atime && d_inode(child)->i_size == 0) { open->op_created = true; goto set_attr; /* subtle */ } status = nfserr_exist; } goto out; } if (!IS_POSIXACL(inode)) iap->ia_mode &= ~current_umask(); host_err = vfs_create(&init_user_ns, inode, child, iap->ia_mode, true); if (host_err < 0) { status = nfserrno(host_err); goto out; } open->op_created = true; /* A newly created file already has a file size of zero. */ if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0)) iap->ia_valid &= ~ATTR_SIZE; if (nfsd4_create_is_exclusive(open->op_createmode)) { iap->ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_MTIME_SET|ATTR_ATIME_SET; iap->ia_mtime.tv_sec = v_mtime; iap->ia_atime.tv_sec = v_atime; iap->ia_mtime.tv_nsec = 0; iap->ia_atime.tv_nsec = 0; } set_attr: status = nfsd_create_setattr(rqstp, fhp, resfhp, iap); out: fh_unlock(fhp); if (child && !IS_ERR(child)) dput(child); fh_drop_write(fhp); return status; } static __be32 do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_open *open, struct svc_fh **resfh) { Loading Loading @@ -264,16 +414,8 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru * yes | yes | GUARDED4 | GUARDED4 */ /* * Note: create modes (UNCHECKED,GUARDED...) are the same * in NFSv4 as in v3 except EXCLUSIVE4_1. */ current->fs->umask = open->op_umask; status = do_nfsd_create(rqstp, current_fh, open->op_fname, open->op_fnamelen, &open->op_iattr, *resfh, open->op_createmode, (u32 *)open->op_verf.data, &open->op_truncate, &open->op_created); status = nfsd4_create_file(rqstp, current_fh, *resfh, open); current->fs->umask = 0; if (!status && open->op_label.len) Loading @@ -284,7 +426,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru * use the returned bitmask to indicate which attributes * we used to store the verifier: */ if (nfsd_create_is_exclusive(open->op_createmode) && status == 0) if (nfsd4_create_is_exclusive(open->op_createmode) && status == 0) open->op_bmval[1] |= (FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_MODIFY); } else Loading