1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-16 10:20:30 +00:00

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
This commit is contained in:
Alexander Kabaev 2008-12-03 17:54:09 +00:00
parent b4d091f3a4
commit 7d464bbf38
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=185586

View File

@ -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);