summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorkan <kan@FreeBSD.org>2008-12-03 17:54:09 +0000
committerkan <kan@FreeBSD.org>2008-12-03 17:54:09 +0000
commitc7b052069777dd0c8ab24012563d406faf84b2d3 (patch)
treecd637eb99bdbdb213c42a835a7da894b4eeb38bb /sys
parentdbaaeca1e667e32a494a42feac86d15536cc0b4b (diff)
downloadFreeBSD-src-c7b052069777dd0c8ab24012563d406faf84b2d3.zip
FreeBSD-src-c7b052069777dd0c8ab24012563d406faf84b2d3.tar.gz
Change nfsserver slightly so that it does not trip over the timestamp
validation code on ZFS. Problem: when opening file with O_CREAT|O_EXCL NFS has to jump through extra hoops to ensure O_EXCL semantics. Namely, client supplies of 8 bytes (NFSX_V3CREATEVERF) bytes of verification data to uniquely identify this create request. Server then creates a new file with access mode 0, copies received 8 bytes into va_atime member of struct vattr and attempt to set the atime on file using VOP_SETATTR. If that succeeds, it fetches file attributes with VOP_GETATTR and verifies that atime timestamps match. If timestamps do not match, NFS server concludes it has probbaly lost the race to another process creating the file with the same name and bails with EEXIST. This scheme works OK when exported FS is FFS, but if underlying filesystem is ZFS _and_ server is running 64bit kernel, it breaks down due to sanity checking in zfs_setattr function, which refuses to accept any timestamps which have tv_sec that cannot be represented as 32bit int. Since struct timespec fields are 64 bit integers on 64bit platforms and server just copies NFSX_V3CREATEVERF bytes info va_atime, all eight bytes supplied by client end up in va_atime.tv_sec, forcing it out of valid 32bit range. The solution this change implements is simple: it treats NFSX_V3CREATEVERF as two 32bit integers and unpacks them separately into va_atime.tv_sec and va_atime.tv_nsec respectively, thus guaranteeing that tv_sec remains in 32 bit range and ZFS remains happy. Reviewed by: kib
Diffstat (limited to 'sys')
-rw-r--r--sys/nfsserver/nfs_serv.c15
1 files changed, 8 insertions, 7 deletions
diff --git a/sys/nfsserver/nfs_serv.c b/sys/nfsserver/nfs_serv.c
index d528769..3b860ac 100644
--- a/sys/nfsserver/nfs_serv.c
+++ b/sys/nfsserver/nfs_serv.c
@@ -1669,13 +1669,12 @@ nfsrv_create(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
caddr_t bpos;
int error = 0, rdev, len, tsize, dirfor_ret = 1, diraft_ret = 1;
int v3 = (nfsd->nd_flag & ND_NFSV3), how, exclusive_flag = 0;
- caddr_t cp;
struct mbuf *mb, *mreq;
struct vnode *dirp = NULL;
nfsfh_t nfh;
fhandle_t *fhp;
u_quad_t tempsize;
- u_char cverf[NFSX_V3CREATEVERF];
+ struct timespec cverf;
struct mount *mp = NULL;
int tvfslocked;
int vfslocked;
@@ -1754,8 +1753,11 @@ nfsrv_create(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
nfsm_srvsattr(vap);
break;
case NFSV3CREATE_EXCLUSIVE:
- cp = nfsm_dissect_nonblock(caddr_t, NFSX_V3CREATEVERF);
- bcopy(cp, cverf, NFSX_V3CREATEVERF);
+ tl = nfsm_dissect_nonblock(u_int32_t *,
+ NFSX_V3CREATEVERF);
+ /* Unique bytes, endianness is not important. */
+ cverf.tv_sec = tl[0];
+ cverf.tv_nsec = tl[1];
exclusive_flag = 1;
break;
};
@@ -1801,8 +1803,7 @@ nfsrv_create(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
if (exclusive_flag) {
exclusive_flag = 0;
VATTR_NULL(vap);
- bcopy(cverf, (caddr_t)&vap->va_atime,
- NFSX_V3CREATEVERF);
+ vap->va_atime = cverf;
error = VOP_SETATTR(nd.ni_vp, vap,
cred);
}
@@ -1886,7 +1887,7 @@ nfsrv_create(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
}
if (v3) {
if (exclusive_flag && !error &&
- bcmp(cverf, (caddr_t)&vap->va_atime, NFSX_V3CREATEVERF))
+ bcmp(&cverf, &vap->va_atime, sizeof (cverf)))
error = EEXIST;
if (dirp == nd.ni_dvp)
diraft_ret = VOP_GETATTR(dirp, &diraft, cred);
OpenPOWER on IntegriCloud