Browse Source

nfsd: don't fail unchecked creates of non-special files

Allow a v3 unchecked open of a non-regular file succeed as if it were a
lookup; typically a client in such a case will want to fall back on a
local open, so succeeding and giving it the filehandle is more useful
than failing with nfserr_exist, which makes it appear that nothing at
all exists by that name.

Similarly for v4, on an open-create, return the same errors we would on
an attempt to open a non-regular file, instead of returning
nfserr_exist.

This fixes a problem found doing a v4 open of a symlink with
O_RDONLY|O_CREAT, which resulted in the current client returning EEXIST.

Thanks also to Trond for analysis.

Cc: stable@kernel.org
Reported-by: Orion Poplawski <orion@cora.nwra.com>
Tested-by: Orion Poplawski <orion@cora.nwra.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
J. Bruce Fields 13 years ago
parent
commit
9dc4e6c4d1
2 changed files with 5 additions and 5 deletions
  1. 4 4
      fs/nfsd/nfs4proc.c
  2. 1 1
      fs/nfsd/vfs.c

+ 4 - 4
fs/nfsd/nfs4proc.c

@@ -235,15 +235,15 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
 		 */
 		 */
 		if (open->op_createmode == NFS4_CREATE_EXCLUSIVE && status == 0)
 		if (open->op_createmode == NFS4_CREATE_EXCLUSIVE && status == 0)
 			open->op_bmval[1] = (FATTR4_WORD1_TIME_ACCESS |
 			open->op_bmval[1] = (FATTR4_WORD1_TIME_ACCESS |
-						FATTR4_WORD1_TIME_MODIFY);
+							FATTR4_WORD1_TIME_MODIFY);
 	} else {
 	} else {
 		status = nfsd_lookup(rqstp, current_fh,
 		status = nfsd_lookup(rqstp, current_fh,
 				     open->op_fname.data, open->op_fname.len, resfh);
 				     open->op_fname.data, open->op_fname.len, resfh);
 		fh_unlock(current_fh);
 		fh_unlock(current_fh);
-		if (status)
-			goto out;
-		status = nfsd_check_obj_isreg(resfh);
 	}
 	}
+	if (status)
+		goto out;
+	status = nfsd_check_obj_isreg(resfh);
 	if (status)
 	if (status)
 		goto out;
 		goto out;
 
 

+ 1 - 1
fs/nfsd/vfs.c

@@ -1458,7 +1458,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
 		switch (createmode) {
 		switch (createmode) {
 		case NFS3_CREATE_UNCHECKED:
 		case NFS3_CREATE_UNCHECKED:
 			if (! S_ISREG(dchild->d_inode->i_mode))
 			if (! S_ISREG(dchild->d_inode->i_mode))
-				err = nfserr_exist;
+				goto out;
 			else if (truncp) {
 			else if (truncp) {
 				/* in nfsv4, we need to treat this case a little
 				/* in nfsv4, we need to treat this case a little
 				 * differently.  we don't want to truncate the
 				 * differently.  we don't want to truncate the