a5df139ec6
Commit d224f05fcf
pre-parsed the next operation number for
the put file handle operations. This patch uses this next
operation number, plus the type of the file handle being set by
the put file handle operation, to implement the rules in RFC5661
Sec. 2.6 with respect to replying NFSERR_WRONGSEC.
This patch also adds a check to see if NFSERR_WRONGSEC should be
replied when about to perform Lookup, Lookupp or Open with a file
name component, so that the NFSERR_WRONGSEC reply is done for
these operations, as required by RFC5661 Sec. 2.6.
This patch does not have any practical effect for the FreeBSD NFSv4
client and I believe that the same is true for the Linux client,
since NFSERR_WRONGSEC is considered a fatal error at this time.
MFC after: 2 weeks
6023 lines
166 KiB
C
6023 lines
166 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Copyright (c) 1989, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* Rick Macklem at The University of Guelph.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "opt_inet.h"
|
|
#include "opt_inet6.h"
|
|
/*
|
|
* nfs version 2, 3 and 4 server calls to vnode ops
|
|
* - these routines generally have 3 phases
|
|
* 1 - break down and validate rpc request in mbuf list
|
|
* 2 - do the vnode ops for the request, usually by calling a nfsvno_XXX()
|
|
* function in nfsd_port.c
|
|
* 3 - build the rpc reply in an mbuf list
|
|
* For nfsv4, these functions are called for each Op within the Compound RPC.
|
|
*/
|
|
|
|
#include <fs/nfs/nfsport.h>
|
|
#include <sys/extattr.h>
|
|
#include <sys/filio.h>
|
|
|
|
/* Global vars */
|
|
extern u_int32_t newnfs_false, newnfs_true;
|
|
extern enum vtype nv34tov_type[8];
|
|
extern struct timeval nfsboottime;
|
|
extern int nfsrv_enable_crossmntpt;
|
|
extern int nfsrv_statehashsize;
|
|
extern int nfsrv_layouthashsize;
|
|
extern time_t nfsdev_time;
|
|
extern volatile int nfsrv_devidcnt;
|
|
extern int nfsd_debuglevel;
|
|
extern u_long sb_max_adj;
|
|
extern int nfsrv_pnfsatime;
|
|
extern int nfsrv_maxpnfsmirror;
|
|
extern int nfs_maxcopyrange;
|
|
|
|
static int nfs_async = 0;
|
|
SYSCTL_DECL(_vfs_nfsd);
|
|
SYSCTL_INT(_vfs_nfsd, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0,
|
|
"Tell client that writes were synced even though they were not");
|
|
extern int nfsrv_doflexfile;
|
|
SYSCTL_INT(_vfs_nfsd, OID_AUTO, default_flexfile, CTLFLAG_RW,
|
|
&nfsrv_doflexfile, 0, "Make Flex File Layout the default for pNFS");
|
|
static int nfsrv_linux42server = 1;
|
|
SYSCTL_INT(_vfs_nfsd, OID_AUTO, linux42server, CTLFLAG_RW,
|
|
&nfsrv_linux42server, 0,
|
|
"Enable Linux style NFSv4.2 server (non-RFC compliant)");
|
|
static bool nfsrv_openaccess = true;
|
|
SYSCTL_BOOL(_vfs_nfsd, OID_AUTO, v4openaccess, CTLFLAG_RW,
|
|
&nfsrv_openaccess, 0,
|
|
"Enable Linux style NFSv4 Open access check");
|
|
|
|
/*
|
|
* This list defines the GSS mechanisms supported.
|
|
* (Don't ask me how you get these strings from the RFC stuff like
|
|
* iso(1), org(3)... but someone did it, so I don't need to know.)
|
|
*/
|
|
static struct nfsgss_mechlist nfsgss_mechlist[] = {
|
|
{ 9, "\052\206\110\206\367\022\001\002\002", 11 },
|
|
{ 0, "", 0 },
|
|
};
|
|
|
|
/* local functions */
|
|
static void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
|
|
struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
|
|
vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
|
|
int *diraft_retp, nfsattrbit_t *attrbitp,
|
|
NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
|
|
int pathlen);
|
|
static void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
|
|
struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
|
|
vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
|
|
int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
|
|
NFSPROC_T *p, struct nfsexstuff *exp);
|
|
|
|
/*
|
|
* nfs access service (not a part of NFS V2)
|
|
*/
|
|
int
|
|
nfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int getret, error = 0;
|
|
struct nfsvattr nva;
|
|
u_int32_t testmode, nfsmode, supported = 0;
|
|
accmode_t deletebit;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_postopattr(nd, 1, &nva);
|
|
goto out;
|
|
}
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
nfsmode = fxdr_unsigned(u_int32_t, *tl);
|
|
if ((nd->nd_flag & ND_NFSV4) &&
|
|
(nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP |
|
|
NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE |
|
|
NFSACCESS_EXECUTE | NFSACCESS_XAREAD | NFSACCESS_XAWRITE |
|
|
NFSACCESS_XALIST))) {
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
vput(vp);
|
|
goto out;
|
|
}
|
|
if (nfsmode & NFSACCESS_READ) {
|
|
supported |= NFSACCESS_READ;
|
|
if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
|
|
NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
|
|
nfsmode &= ~NFSACCESS_READ;
|
|
}
|
|
if (nfsmode & NFSACCESS_MODIFY) {
|
|
supported |= NFSACCESS_MODIFY;
|
|
if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
|
|
NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
|
|
nfsmode &= ~NFSACCESS_MODIFY;
|
|
}
|
|
if (nfsmode & NFSACCESS_EXTEND) {
|
|
supported |= NFSACCESS_EXTEND;
|
|
if (nfsvno_accchk(vp, VWRITE | VAPPEND, nd->nd_cred, exp, p,
|
|
NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
|
|
nfsmode &= ~NFSACCESS_EXTEND;
|
|
}
|
|
if (nfsmode & NFSACCESS_XAREAD) {
|
|
supported |= NFSACCESS_XAREAD;
|
|
if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
|
|
NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
|
|
nfsmode &= ~NFSACCESS_XAREAD;
|
|
}
|
|
if (nfsmode & NFSACCESS_XAWRITE) {
|
|
supported |= NFSACCESS_XAWRITE;
|
|
if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p,
|
|
NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
|
|
nfsmode &= ~NFSACCESS_XAWRITE;
|
|
}
|
|
if (nfsmode & NFSACCESS_XALIST) {
|
|
supported |= NFSACCESS_XALIST;
|
|
if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p,
|
|
NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
|
|
nfsmode &= ~NFSACCESS_XALIST;
|
|
}
|
|
if (nfsmode & NFSACCESS_DELETE) {
|
|
supported |= NFSACCESS_DELETE;
|
|
if (vp->v_type == VDIR)
|
|
deletebit = VDELETE_CHILD;
|
|
else
|
|
deletebit = VDELETE;
|
|
if (nfsvno_accchk(vp, deletebit, nd->nd_cred, exp, p,
|
|
NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
|
|
nfsmode &= ~NFSACCESS_DELETE;
|
|
}
|
|
if (vnode_vtype(vp) == VDIR)
|
|
testmode = NFSACCESS_LOOKUP;
|
|
else
|
|
testmode = NFSACCESS_EXECUTE;
|
|
if (nfsmode & testmode) {
|
|
supported |= (nfsmode & testmode);
|
|
if (nfsvno_accchk(vp, VEXEC, nd->nd_cred, exp, p,
|
|
NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported))
|
|
nfsmode &= ~testmode;
|
|
}
|
|
nfsmode &= supported;
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
|
|
nfsrv_postopattr(nd, getret, &nva);
|
|
}
|
|
vput(vp);
|
|
if (nd->nd_flag & ND_NFSV4) {
|
|
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(supported);
|
|
} else
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(nfsmode);
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs getattr service
|
|
*/
|
|
int
|
|
nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
struct nfsvattr nva;
|
|
fhandle_t fh;
|
|
int at_root = 0, error = 0, supports_nfsv4acls;
|
|
struct nfsreferral *refp;
|
|
nfsattrbit_t attrbits, tmpbits;
|
|
struct mount *mp;
|
|
struct vnode *tvp = NULL;
|
|
struct vattr va;
|
|
uint64_t mounted_on_fileno = 0;
|
|
accmode_t accmode;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat)
|
|
goto out;
|
|
if (nd->nd_flag & ND_NFSV4) {
|
|
error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
|
|
if (error) {
|
|
vput(vp);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Check for a referral.
|
|
*/
|
|
refp = nfsv4root_getreferral(vp, NULL, 0);
|
|
if (refp != NULL) {
|
|
(void) nfsrv_putreferralattr(nd, &attrbits, refp, 1,
|
|
&nd->nd_repstat);
|
|
vput(vp);
|
|
goto out;
|
|
}
|
|
if (nd->nd_repstat == 0) {
|
|
accmode = 0;
|
|
NFSSET_ATTRBIT(&tmpbits, &attrbits);
|
|
|
|
/*
|
|
* GETATTR with write-only attr time_access_set and time_modify_set
|
|
* should return NFS4ERR_INVAL.
|
|
*/
|
|
if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEACCESSSET) ||
|
|
NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_TIMEMODIFYSET)){
|
|
error = NFSERR_INVAL;
|
|
vput(vp);
|
|
goto out;
|
|
}
|
|
if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) {
|
|
NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL);
|
|
accmode |= VREAD_ACL;
|
|
}
|
|
if (NFSNONZERO_ATTRBIT(&tmpbits))
|
|
accmode |= VREAD_ATTRIBUTES;
|
|
if (accmode != 0)
|
|
nd->nd_repstat = nfsvno_accchk(vp, accmode,
|
|
nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE,
|
|
NFSACCCHK_VPISLOCKED, NULL);
|
|
}
|
|
}
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
|
|
if (!nd->nd_repstat) {
|
|
if (nd->nd_flag & ND_NFSV4) {
|
|
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE))
|
|
nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsrv_checkgetattr(nd, vp,
|
|
&nva, &attrbits, p);
|
|
if (nd->nd_repstat == 0) {
|
|
supports_nfsv4acls = nfs_supportsnfsv4acls(vp);
|
|
mp = vp->v_mount;
|
|
if (nfsrv_enable_crossmntpt != 0 &&
|
|
vp->v_type == VDIR &&
|
|
(vp->v_vflag & VV_ROOT) != 0 &&
|
|
vp != rootvnode) {
|
|
tvp = mp->mnt_vnodecovered;
|
|
VREF(tvp);
|
|
at_root = 1;
|
|
} else
|
|
at_root = 0;
|
|
vfs_ref(mp);
|
|
NFSVOPUNLOCK(vp);
|
|
if (at_root != 0) {
|
|
if ((nd->nd_repstat =
|
|
NFSVOPLOCK(tvp, LK_SHARED)) == 0) {
|
|
nd->nd_repstat = VOP_GETATTR(
|
|
tvp, &va, nd->nd_cred);
|
|
vput(tvp);
|
|
} else
|
|
vrele(tvp);
|
|
if (nd->nd_repstat == 0)
|
|
mounted_on_fileno = (uint64_t)
|
|
va.va_fileid;
|
|
else
|
|
at_root = 0;
|
|
}
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = vfs_busy(mp, 0);
|
|
vfs_rel(mp);
|
|
if (nd->nd_repstat == 0) {
|
|
(void)nfsvno_fillattr(nd, mp, vp, &nva,
|
|
&fh, 0, &attrbits, nd->nd_cred, p,
|
|
isdgram, 1, supports_nfsv4acls,
|
|
at_root, mounted_on_fileno);
|
|
vfs_unbusy(mp);
|
|
}
|
|
vrele(vp);
|
|
} else
|
|
vput(vp);
|
|
} else {
|
|
nfsrv_fillattr(nd, &nva);
|
|
vput(vp);
|
|
}
|
|
} else {
|
|
vput(vp);
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs setattr service
|
|
*/
|
|
int
|
|
nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
struct nfsvattr nva, nva2;
|
|
u_int32_t *tl;
|
|
int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
|
|
int gotproxystateid;
|
|
struct timespec guard = { 0, 0 };
|
|
nfsattrbit_t attrbits, retbits;
|
|
nfsv4stateid_t stateid;
|
|
NFSACL_T *aclp = NULL;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
|
|
goto out;
|
|
}
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
aclp = acl_alloc(M_WAITOK);
|
|
aclp->acl_cnt = 0;
|
|
#endif
|
|
gotproxystateid = 0;
|
|
NFSVNO_ATTRINIT(&nva);
|
|
if (nd->nd_flag & ND_NFSV4) {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
|
|
stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
stateid.other[0] = *tl++;
|
|
stateid.other[1] = *tl++;
|
|
stateid.other[2] = *tl;
|
|
if (stateid.other[0] == 0x55555555 &&
|
|
stateid.other[1] == 0x55555555 &&
|
|
stateid.other[2] == 0x55555555 &&
|
|
stateid.seqid == 0xffffffff)
|
|
gotproxystateid = 1;
|
|
}
|
|
error = nfsrv_sattr(nd, vp, &nva, &attrbits, aclp, p);
|
|
if (error)
|
|
goto nfsmout;
|
|
|
|
/* For NFSv4, only va_uid is used from nva2. */
|
|
NFSZERO_ATTRBIT(&retbits);
|
|
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
|
|
preat_ret = nfsvno_getattr(vp, &nva2, nd, p, 1, &retbits);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = preat_ret;
|
|
|
|
NFSZERO_ATTRBIT(&retbits);
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
gcheck = fxdr_unsigned(int, *tl);
|
|
if (gcheck) {
|
|
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
fxdr_nfsv3time(tl, &guard);
|
|
}
|
|
if (!nd->nd_repstat && gcheck &&
|
|
(nva2.na_ctime.tv_sec != guard.tv_sec ||
|
|
nva2.na_ctime.tv_nsec != guard.tv_nsec))
|
|
nd->nd_repstat = NFSERR_NOT_SYNC;
|
|
if (nd->nd_repstat) {
|
|
vput(vp);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
|
|
goto out;
|
|
}
|
|
} else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
|
|
nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
|
|
|
|
/*
|
|
* Now that we have all the fields, lets do it.
|
|
* If the size is being changed write access is required, otherwise
|
|
* just check for a read only file system.
|
|
*/
|
|
if (!nd->nd_repstat) {
|
|
if (NFSVNO_NOTSETSIZE(&nva)) {
|
|
if (NFSVNO_EXRDONLY(exp) ||
|
|
(vfs_flags(vp->v_mount) & MNT_RDONLY))
|
|
nd->nd_repstat = EROFS;
|
|
} else {
|
|
if (vnode_vtype(vp) != VREG)
|
|
nd->nd_repstat = EINVAL;
|
|
else if (nva2.na_uid != nd->nd_cred->cr_uid ||
|
|
NFSVNO_EXSTRICTACCESS(exp))
|
|
nd->nd_repstat = nfsvno_accchk(vp,
|
|
VWRITE, nd->nd_cred, exp, p,
|
|
NFSACCCHK_NOOVERRIDE,
|
|
NFSACCCHK_VPISLOCKED, NULL);
|
|
}
|
|
}
|
|
/*
|
|
* Proxy operations from the MDS are allowed via the all 0s special
|
|
* stateid.
|
|
*/
|
|
if (nd->nd_repstat == 0 && (nd->nd_flag & ND_NFSV4) != 0 &&
|
|
gotproxystateid == 0)
|
|
nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid,
|
|
&nva, &attrbits, exp, p);
|
|
|
|
if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
|
|
/*
|
|
* For V4, try setting the attrbutes in sets, so that the
|
|
* reply bitmap will be correct for an error case.
|
|
*/
|
|
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) ||
|
|
NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) {
|
|
NFSVNO_ATTRINIT(&nva2);
|
|
NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid);
|
|
NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid);
|
|
nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
|
|
exp);
|
|
if (!nd->nd_repstat) {
|
|
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER))
|
|
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
|
|
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP))
|
|
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP);
|
|
}
|
|
}
|
|
if (!nd->nd_repstat &&
|
|
NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) {
|
|
NFSVNO_ATTRINIT(&nva2);
|
|
NFSVNO_SETATTRVAL(&nva2, size, nva.na_size);
|
|
nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
|
|
exp);
|
|
if (!nd->nd_repstat)
|
|
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE);
|
|
}
|
|
if (!nd->nd_repstat &&
|
|
(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) ||
|
|
NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) {
|
|
NFSVNO_ATTRINIT(&nva2);
|
|
NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime);
|
|
NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime);
|
|
if (nva.na_vaflags & VA_UTIMES_NULL) {
|
|
nva2.na_vaflags |= VA_UTIMES_NULL;
|
|
NFSVNO_SETACTIVE(&nva2, vaflags);
|
|
}
|
|
nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
|
|
exp);
|
|
if (!nd->nd_repstat) {
|
|
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET))
|
|
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET);
|
|
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))
|
|
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET);
|
|
}
|
|
}
|
|
if (!nd->nd_repstat &&
|
|
NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE)) {
|
|
NFSVNO_ATTRINIT(&nva2);
|
|
NFSVNO_SETATTRVAL(&nva2, btime, nva.na_btime);
|
|
nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
|
|
exp);
|
|
if (!nd->nd_repstat)
|
|
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMECREATE);
|
|
}
|
|
if (!nd->nd_repstat &&
|
|
(NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE) ||
|
|
NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))) {
|
|
NFSVNO_ATTRINIT(&nva2);
|
|
NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode);
|
|
nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
|
|
exp);
|
|
if (!nd->nd_repstat) {
|
|
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE))
|
|
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
|
|
if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODESETMASKED))
|
|
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODESETMASKED);
|
|
}
|
|
}
|
|
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
|
|
NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) {
|
|
nd->nd_repstat = nfsrv_setacl(vp, aclp, nd->nd_cred, p);
|
|
if (!nd->nd_repstat)
|
|
NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL);
|
|
}
|
|
#endif
|
|
} else if (!nd->nd_repstat) {
|
|
nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p,
|
|
exp);
|
|
}
|
|
if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) {
|
|
postat_ret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = postat_ret;
|
|
}
|
|
vput(vp);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
|
|
else if (nd->nd_flag & ND_NFSV4)
|
|
(void) nfsrv_putattrbit(nd, &retbits);
|
|
else if (!nd->nd_repstat)
|
|
nfsrv_fillattr(nd, &nva);
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vput(vp);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
if (nd->nd_flag & ND_NFSV4) {
|
|
/*
|
|
* For all nd_repstat, the V4 reply includes a bitmap,
|
|
* even NFSERR_BADXDR, which is what this will end up
|
|
* returning.
|
|
*/
|
|
(void) nfsrv_putattrbit(nd, &retbits);
|
|
}
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs lookup rpc
|
|
* (Also performs lookup parent for v4)
|
|
*/
|
|
int
|
|
nfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
|
|
{
|
|
struct nameidata named;
|
|
vnode_t vp, dirp = NULL;
|
|
int error = 0, dattr_ret = 1;
|
|
struct nfsvattr nva, dattr;
|
|
char *bufp;
|
|
u_long *hashp;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_postopattr(nd, dattr_ret, &dattr);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* For some reason, if dp is a symlink, the error
|
|
* returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR.
|
|
*/
|
|
if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) {
|
|
nd->nd_repstat = NFSERR_SYMLINK;
|
|
vrele(dp);
|
|
goto out;
|
|
}
|
|
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
|
|
LOCKLEAF | SAVESTART);
|
|
nfsvno_setpathbuf(&named, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
|
|
if (error) {
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
goto out;
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
|
|
} else {
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
}
|
|
if (nd->nd_repstat) {
|
|
if (dirp) {
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
dattr_ret = nfsvno_getattr(dirp, &dattr, nd, p,
|
|
0, NULL);
|
|
vrele(dirp);
|
|
}
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_postopattr(nd, dattr_ret, &dattr);
|
|
goto out;
|
|
}
|
|
if (named.ni_startdir)
|
|
vrele(named.ni_startdir);
|
|
nfsvno_relpathbuf(&named);
|
|
vp = named.ni_vp;
|
|
if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) &&
|
|
vp->v_type != VDIR && vp->v_type != VLNK)
|
|
/*
|
|
* Only allow lookup of VDIR and VLNK for traversal of
|
|
* non-exported volumes during NFSv4 mounting.
|
|
*/
|
|
nd->nd_repstat = ENOENT;
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
|
|
if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
|
|
if (vpp != NULL && nd->nd_repstat == 0)
|
|
*vpp = vp;
|
|
else
|
|
vput(vp);
|
|
if (dirp) {
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
dattr_ret = nfsvno_getattr(dirp, &dattr, nd, p, 0,
|
|
NULL);
|
|
vrele(dirp);
|
|
}
|
|
if (nd->nd_repstat) {
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_postopattr(nd, dattr_ret, &dattr);
|
|
goto out;
|
|
}
|
|
if (nd->nd_flag & ND_NFSV2) {
|
|
(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
|
|
nfsrv_fillattr(nd, &nva);
|
|
} else if (nd->nd_flag & ND_NFSV3) {
|
|
(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
|
|
nfsrv_postopattr(nd, 0, &nva);
|
|
nfsrv_postopattr(nd, dattr_ret, &dattr);
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs readlink service
|
|
*/
|
|
int
|
|
nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
struct mbuf *mp = NULL, *mpend = NULL;
|
|
int getret = 1, len;
|
|
struct nfsvattr nva;
|
|
struct thread *p = curthread;
|
|
uint16_t off;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_postopattr(nd, getret, &nva);
|
|
goto out;
|
|
}
|
|
if (vnode_vtype(vp) != VLNK) {
|
|
if (nd->nd_flag & ND_NFSV2)
|
|
nd->nd_repstat = ENXIO;
|
|
else
|
|
nd->nd_repstat = EINVAL;
|
|
}
|
|
if (nd->nd_repstat == 0) {
|
|
if ((nd->nd_flag & ND_EXTPG) != 0)
|
|
nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred,
|
|
nd->nd_maxextsiz, p, &mp, &mpend, &len);
|
|
else
|
|
nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred,
|
|
0, p, &mp, &mpend, &len);
|
|
}
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
|
|
vput(vp);
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_postopattr(nd, getret, &nva);
|
|
if (nd->nd_repstat)
|
|
goto out;
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(len);
|
|
if (mp != NULL) {
|
|
nd->nd_mb->m_next = mp;
|
|
nd->nd_mb = mpend;
|
|
if ((mpend->m_flags & M_EXTPG) != 0) {
|
|
nd->nd_bextpg = mpend->m_epg_npgs - 1;
|
|
nd->nd_bpos = (char *)(void *)
|
|
PHYS_TO_DMAP(mpend->m_epg_pa[nd->nd_bextpg]);
|
|
off = (nd->nd_bextpg == 0) ? mpend->m_epg_1st_off : 0;
|
|
nd->nd_bpos += off + mpend->m_epg_last_len;
|
|
nd->nd_bextpgsiz = PAGE_SIZE - mpend->m_epg_last_len -
|
|
off;
|
|
} else
|
|
nd->nd_bpos = mtod(mpend, char *) + mpend->m_len;
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* nfs read service
|
|
*/
|
|
int
|
|
nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int error = 0, cnt, getret = 1, gotproxystateid, reqlen, eof = 0;
|
|
struct mbuf *m2, *m3;
|
|
struct nfsvattr nva;
|
|
off_t off = 0x0;
|
|
struct nfsstate st, *stp = &st;
|
|
struct nfslock lo, *lop = &lo;
|
|
nfsv4stateid_t stateid;
|
|
nfsquad_t clientid;
|
|
struct thread *p = curthread;
|
|
uint16_t poff;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_postopattr(nd, getret, &nva);
|
|
goto out;
|
|
}
|
|
if (nd->nd_flag & ND_NFSV2) {
|
|
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
off = (off_t)fxdr_unsigned(u_int32_t, *tl++);
|
|
reqlen = fxdr_unsigned(int, *tl);
|
|
} else if (nd->nd_flag & ND_NFSV3) {
|
|
NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
|
|
off = fxdr_hyper(tl);
|
|
tl += 2;
|
|
reqlen = fxdr_unsigned(int, *tl);
|
|
} else {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED);
|
|
reqlen = fxdr_unsigned(int, *(tl + 6));
|
|
}
|
|
if (reqlen > NFS_SRVMAXDATA(nd)) {
|
|
reqlen = NFS_SRVMAXDATA(nd);
|
|
} else if (reqlen < 0) {
|
|
error = EBADRPC;
|
|
goto nfsmout;
|
|
}
|
|
gotproxystateid = 0;
|
|
if (nd->nd_flag & ND_NFSV4) {
|
|
stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
|
|
lop->lo_flags = NFSLCK_READ;
|
|
stp->ls_ownerlen = 0;
|
|
stp->ls_op = NULL;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
|
|
clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK1 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
stp->ls_stateid.other[2] = *tl++;
|
|
/*
|
|
* Don't allow the client to use a special stateid for a DS op.
|
|
*/
|
|
if ((nd->nd_flag & ND_DSSERVER) != 0 &&
|
|
((stp->ls_stateid.other[0] == 0x0 &&
|
|
stp->ls_stateid.other[1] == 0x0 &&
|
|
stp->ls_stateid.other[2] == 0x0) ||
|
|
(stp->ls_stateid.other[0] == 0xffffffff &&
|
|
stp->ls_stateid.other[1] == 0xffffffff &&
|
|
stp->ls_stateid.other[2] == 0xffffffff) ||
|
|
stp->ls_stateid.seqid != 0))
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
/* However, allow the proxy stateid. */
|
|
if (stp->ls_stateid.seqid == 0xffffffff &&
|
|
stp->ls_stateid.other[0] == 0x55555555 &&
|
|
stp->ls_stateid.other[1] == 0x55555555 &&
|
|
stp->ls_stateid.other[2] == 0x55555555)
|
|
gotproxystateid = 1;
|
|
off = fxdr_hyper(tl);
|
|
lop->lo_first = off;
|
|
tl += 2;
|
|
lop->lo_end = off + reqlen;
|
|
/*
|
|
* Paranoia, just in case it wraps around.
|
|
*/
|
|
if (lop->lo_end < off)
|
|
lop->lo_end = NFS64BITSSET;
|
|
}
|
|
if (vnode_vtype(vp) != VREG) {
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nd->nd_repstat = EINVAL;
|
|
else
|
|
nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
|
|
EINVAL;
|
|
}
|
|
getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = getret;
|
|
if (!nd->nd_repstat &&
|
|
(nva.na_uid != nd->nd_cred->cr_uid ||
|
|
NFSVNO_EXSTRICTACCESS(exp))) {
|
|
nd->nd_repstat = nfsvno_accchk(vp, VREAD,
|
|
nd->nd_cred, exp, p,
|
|
NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
|
|
if (nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
|
|
nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
|
|
NFSACCCHK_VPISLOCKED, NULL);
|
|
}
|
|
/*
|
|
* DS reads are marked by ND_DSSERVER or use the proxy special
|
|
* stateid.
|
|
*/
|
|
if (nd->nd_repstat == 0 && (nd->nd_flag & (ND_NFSV4 | ND_DSSERVER)) ==
|
|
ND_NFSV4 && gotproxystateid == 0)
|
|
nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
|
|
&stateid, exp, nd, p);
|
|
if (nd->nd_repstat) {
|
|
vput(vp);
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_postopattr(nd, getret, &nva);
|
|
goto out;
|
|
}
|
|
if (off >= nva.na_size) {
|
|
cnt = 0;
|
|
eof = 1;
|
|
} else if (reqlen == 0)
|
|
cnt = 0;
|
|
else if ((off + reqlen) >= nva.na_size) {
|
|
cnt = nva.na_size - off;
|
|
eof = 1;
|
|
} else
|
|
cnt = reqlen;
|
|
m3 = NULL;
|
|
if (cnt > 0) {
|
|
/*
|
|
* If cnt > MCLBYTES and the reply will not be saved, use
|
|
* ext_pgs mbufs for TLS.
|
|
* For NFSv4.0, we do not know for sure if the reply will
|
|
* be saved, so do not use ext_pgs mbufs for NFSv4.0.
|
|
* Always use ext_pgs mbufs if ND_EXTPG is set.
|
|
*/
|
|
if ((nd->nd_flag & ND_EXTPG) != 0 || (cnt > MCLBYTES &&
|
|
(nd->nd_flag & (ND_TLS | ND_SAVEREPLY)) == ND_TLS &&
|
|
(nd->nd_flag & (ND_NFSV4 | ND_NFSV41)) != ND_NFSV4))
|
|
nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred,
|
|
nd->nd_maxextsiz, p, &m3, &m2);
|
|
else
|
|
nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred,
|
|
0, p, &m3, &m2);
|
|
if (!(nd->nd_flag & ND_NFSV4)) {
|
|
getret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = getret;
|
|
}
|
|
if (nd->nd_repstat) {
|
|
vput(vp);
|
|
if (m3)
|
|
m_freem(m3);
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_postopattr(nd, getret, &nva);
|
|
goto out;
|
|
}
|
|
}
|
|
vput(vp);
|
|
if (nd->nd_flag & ND_NFSV2) {
|
|
nfsrv_fillattr(nd, &nva);
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
} else {
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
nfsrv_postopattr(nd, getret, &nva);
|
|
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(cnt);
|
|
} else
|
|
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
if (eof)
|
|
*tl++ = newnfs_true;
|
|
else
|
|
*tl++ = newnfs_false;
|
|
}
|
|
*tl = txdr_unsigned(cnt);
|
|
if (m3) {
|
|
nd->nd_mb->m_next = m3;
|
|
nd->nd_mb = m2;
|
|
if ((m2->m_flags & M_EXTPG) != 0) {
|
|
nd->nd_flag |= ND_EXTPG;
|
|
nd->nd_bextpg = m2->m_epg_npgs - 1;
|
|
nd->nd_bpos = (char *)(void *)
|
|
PHYS_TO_DMAP(m2->m_epg_pa[nd->nd_bextpg]);
|
|
poff = (nd->nd_bextpg == 0) ? m2->m_epg_1st_off : 0;
|
|
nd->nd_bpos += poff + m2->m_epg_last_len;
|
|
nd->nd_bextpgsiz = PAGE_SIZE - m2->m_epg_last_len -
|
|
poff;
|
|
} else
|
|
nd->nd_bpos = mtod(m2, char *) + m2->m_len;
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs write service
|
|
*/
|
|
int
|
|
nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
struct nfsvattr nva, forat;
|
|
int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
|
|
int gotproxystateid, stable = NFSWRITE_FILESYNC;
|
|
off_t off;
|
|
struct nfsstate st, *stp = &st;
|
|
struct nfslock lo, *lop = &lo;
|
|
nfsv4stateid_t stateid;
|
|
nfsquad_t clientid;
|
|
nfsattrbit_t attrbits;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
|
|
goto out;
|
|
}
|
|
gotproxystateid = 0;
|
|
if (nd->nd_flag & ND_NFSV2) {
|
|
NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
|
|
off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
|
|
tl += 2;
|
|
retlen = len = fxdr_unsigned(int32_t, *tl);
|
|
} else if (nd->nd_flag & ND_NFSV3) {
|
|
NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
|
|
off = fxdr_hyper(tl);
|
|
tl += 3;
|
|
stable = fxdr_unsigned(int, *tl++);
|
|
retlen = len = fxdr_unsigned(int32_t, *tl);
|
|
} else {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED);
|
|
stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
|
|
lop->lo_flags = NFSLCK_WRITE;
|
|
stp->ls_ownerlen = 0;
|
|
stp->ls_op = NULL;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
|
|
clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK2 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
stp->ls_stateid.other[2] = *tl++;
|
|
/*
|
|
* Don't allow the client to use a special stateid for a DS op.
|
|
*/
|
|
if ((nd->nd_flag & ND_DSSERVER) != 0 &&
|
|
((stp->ls_stateid.other[0] == 0x0 &&
|
|
stp->ls_stateid.other[1] == 0x0 &&
|
|
stp->ls_stateid.other[2] == 0x0) ||
|
|
(stp->ls_stateid.other[0] == 0xffffffff &&
|
|
stp->ls_stateid.other[1] == 0xffffffff &&
|
|
stp->ls_stateid.other[2] == 0xffffffff) ||
|
|
stp->ls_stateid.seqid != 0))
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
/* However, allow the proxy stateid. */
|
|
if (stp->ls_stateid.seqid == 0xffffffff &&
|
|
stp->ls_stateid.other[0] == 0x55555555 &&
|
|
stp->ls_stateid.other[1] == 0x55555555 &&
|
|
stp->ls_stateid.other[2] == 0x55555555)
|
|
gotproxystateid = 1;
|
|
off = fxdr_hyper(tl);
|
|
lop->lo_first = off;
|
|
tl += 2;
|
|
stable = fxdr_unsigned(int, *tl++);
|
|
retlen = len = fxdr_unsigned(int32_t, *tl);
|
|
lop->lo_end = off + len;
|
|
/*
|
|
* Paranoia, just in case it wraps around, which shouldn't
|
|
* ever happen anyhow.
|
|
*/
|
|
if (lop->lo_end < lop->lo_first)
|
|
lop->lo_end = NFS64BITSSET;
|
|
}
|
|
|
|
if (retlen > NFS_SRVMAXIO || retlen < 0)
|
|
nd->nd_repstat = EIO;
|
|
if (vnode_vtype(vp) != VREG && !nd->nd_repstat) {
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nd->nd_repstat = EINVAL;
|
|
else
|
|
nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
|
|
EINVAL;
|
|
}
|
|
NFSZERO_ATTRBIT(&attrbits);
|
|
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
|
|
forat_ret = nfsvno_getattr(vp, &forat, nd, p, 1, &attrbits);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = forat_ret;
|
|
if (!nd->nd_repstat &&
|
|
(forat.na_uid != nd->nd_cred->cr_uid ||
|
|
NFSVNO_EXSTRICTACCESS(exp)))
|
|
nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
|
|
nd->nd_cred, exp, p,
|
|
NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL);
|
|
/*
|
|
* DS reads are marked by ND_DSSERVER or use the proxy special
|
|
* stateid.
|
|
*/
|
|
if (nd->nd_repstat == 0 && (nd->nd_flag & (ND_NFSV4 | ND_DSSERVER)) ==
|
|
ND_NFSV4 && gotproxystateid == 0)
|
|
nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
|
|
&stateid, exp, nd, p);
|
|
if (nd->nd_repstat) {
|
|
vput(vp);
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* For NFS Version 2, it is not obvious what a write of zero length
|
|
* should do, but I might as well be consistent with Version 3,
|
|
* which is to return ok so long as there are no permission problems.
|
|
*/
|
|
if (retlen > 0) {
|
|
nd->nd_repstat = nfsvno_write(vp, off, retlen, &stable,
|
|
nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
|
|
error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
|
|
if (error)
|
|
goto nfsmout;
|
|
}
|
|
if (nd->nd_flag & ND_NFSV4)
|
|
aftat_ret = 0;
|
|
else
|
|
aftat_ret = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
|
|
vput(vp);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = aftat_ret;
|
|
if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
|
|
if (nd->nd_repstat)
|
|
goto out;
|
|
NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(retlen);
|
|
/*
|
|
* If nfs_async is set, then pretend the write was FILESYNC.
|
|
* Warning: Doing this violates RFC1813 and runs a risk
|
|
* of data written by a client being lost when the server
|
|
* crashes/reboots.
|
|
*/
|
|
if (stable == NFSWRITE_UNSTABLE && nfs_async == 0)
|
|
*tl++ = txdr_unsigned(stable);
|
|
else
|
|
*tl++ = txdr_unsigned(NFSWRITE_FILESYNC);
|
|
/*
|
|
* Actually, there is no need to txdr these fields,
|
|
* but it may make the values more human readable,
|
|
* for debugging purposes.
|
|
*/
|
|
*tl++ = txdr_unsigned(nfsboottime.tv_sec);
|
|
*tl = txdr_unsigned(nfsboottime.tv_usec);
|
|
} else if (!nd->nd_repstat)
|
|
nfsrv_fillattr(nd, &nva);
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs create service (creates regular files for V2 and V3. Spec. files for V2.)
|
|
* now does a truncate to 0 length via. setattr if it already exists
|
|
* The core creation routine has been extracted out into nfsrv_creatsub(),
|
|
* so it can also be used by nfsrv_open() for V4.
|
|
*/
|
|
int
|
|
nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t dp, struct nfsexstuff *exp)
|
|
{
|
|
struct nfsvattr nva, dirfor, diraft;
|
|
struct nfsv2_sattr *sp;
|
|
struct nameidata named;
|
|
u_int32_t *tl;
|
|
int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1;
|
|
int how = NFSCREATE_UNCHECKED, exclusive_flag = 0;
|
|
NFSDEV_T rdev = 0;
|
|
vnode_t vp = NULL, dirp = NULL;
|
|
fhandle_t fh;
|
|
char *bufp;
|
|
u_long *hashp;
|
|
enum vtype vtyp;
|
|
int32_t cverf[2], tverf[2] = { 0, 0 };
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
goto out;
|
|
}
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
|
|
LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
|
|
nfsvno_setpathbuf(&named, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
|
|
if (error)
|
|
goto nfsmout;
|
|
if (!nd->nd_repstat) {
|
|
NFSVNO_ATTRINIT(&nva);
|
|
if (nd->nd_flag & ND_NFSV2) {
|
|
NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
|
|
vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
|
|
if (vtyp == VNON)
|
|
vtyp = VREG;
|
|
NFSVNO_SETATTRVAL(&nva, type, vtyp);
|
|
NFSVNO_SETATTRVAL(&nva, mode,
|
|
nfstov_mode(sp->sa_mode));
|
|
switch (nva.na_type) {
|
|
case VREG:
|
|
tsize = fxdr_unsigned(int32_t, sp->sa_size);
|
|
if (tsize != -1)
|
|
NFSVNO_SETATTRVAL(&nva, size,
|
|
(u_quad_t)tsize);
|
|
break;
|
|
case VCHR:
|
|
case VBLK:
|
|
case VFIFO:
|
|
rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
how = fxdr_unsigned(int, *tl);
|
|
switch (how) {
|
|
case NFSCREATE_GUARDED:
|
|
case NFSCREATE_UNCHECKED:
|
|
error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p);
|
|
if (error)
|
|
goto nfsmout;
|
|
break;
|
|
case NFSCREATE_EXCLUSIVE:
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
|
|
cverf[0] = *tl++;
|
|
cverf[1] = *tl;
|
|
exclusive_flag = 1;
|
|
break;
|
|
}
|
|
NFSVNO_SETATTRVAL(&nva, type, VREG);
|
|
}
|
|
}
|
|
if (nd->nd_repstat) {
|
|
nfsvno_relpathbuf(&named);
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
dirfor_ret = nfsvno_getattr(dp, &dirfor, nd, p, 1,
|
|
NULL);
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
|
|
&diraft);
|
|
}
|
|
vput(dp);
|
|
goto out;
|
|
}
|
|
|
|
nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
|
|
if (dirp) {
|
|
if (nd->nd_flag & ND_NFSV2) {
|
|
vrele(dirp);
|
|
dirp = NULL;
|
|
} else {
|
|
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
|
|
NULL);
|
|
}
|
|
}
|
|
if (nd->nd_repstat) {
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
|
|
&diraft);
|
|
if (dirp)
|
|
vrele(dirp);
|
|
goto out;
|
|
}
|
|
|
|
if (!(nd->nd_flag & ND_NFSV2)) {
|
|
switch (how) {
|
|
case NFSCREATE_GUARDED:
|
|
if (named.ni_vp)
|
|
nd->nd_repstat = EEXIST;
|
|
break;
|
|
case NFSCREATE_UNCHECKED:
|
|
break;
|
|
case NFSCREATE_EXCLUSIVE:
|
|
if (named.ni_vp == NULL)
|
|
NFSVNO_SETATTRVAL(&nva, mode, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Iff doesn't exist, create it
|
|
* otherwise just truncate to 0 length
|
|
* should I set the mode too ?
|
|
*/
|
|
nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva,
|
|
&exclusive_flag, cverf, rdev, exp);
|
|
|
|
if (!nd->nd_repstat) {
|
|
nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
|
|
NULL);
|
|
vput(vp);
|
|
if (!nd->nd_repstat) {
|
|
tverf[0] = nva.na_atime.tv_sec;
|
|
tverf[1] = nva.na_atime.tv_nsec;
|
|
}
|
|
}
|
|
if (nd->nd_flag & ND_NFSV2) {
|
|
if (!nd->nd_repstat) {
|
|
(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
|
|
nfsrv_fillattr(nd, &nva);
|
|
}
|
|
} else {
|
|
if (exclusive_flag && !nd->nd_repstat && (cverf[0] != tverf[0]
|
|
|| cverf[1] != tverf[1]))
|
|
nd->nd_repstat = EEXIST;
|
|
diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
|
|
vrele(dirp);
|
|
if (!nd->nd_repstat) {
|
|
(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 1);
|
|
nfsrv_postopattr(nd, 0, &nva);
|
|
}
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vput(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs v3 mknod service (and v4 create)
|
|
*/
|
|
int
|
|
nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
|
|
{
|
|
struct nfsvattr nva, dirfor, diraft;
|
|
u_int32_t *tl;
|
|
struct nameidata named;
|
|
int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
|
|
u_int32_t major, minor;
|
|
enum vtype vtyp = VNON;
|
|
nfstype nfs4type = NFNON;
|
|
vnode_t vp, dirp = NULL;
|
|
nfsattrbit_t attrbits;
|
|
char *bufp = NULL, *pathcp = NULL;
|
|
u_long *hashp, cnflags;
|
|
NFSACL_T *aclp = NULL;
|
|
struct thread *p = curthread;
|
|
|
|
NFSVNO_ATTRINIT(&nva);
|
|
cnflags = (LOCKPARENT | SAVESTART);
|
|
if (nd->nd_repstat) {
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
goto out;
|
|
}
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
aclp = acl_alloc(M_WAITOK);
|
|
aclp->acl_cnt = 0;
|
|
#endif
|
|
|
|
/*
|
|
* For V4, the creation stuff is here, Yuck!
|
|
*/
|
|
if (nd->nd_flag & ND_NFSV4) {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
vtyp = nfsv34tov_type(*tl);
|
|
nfs4type = fxdr_unsigned(nfstype, *tl);
|
|
switch (nfs4type) {
|
|
case NFLNK:
|
|
error = nfsvno_getsymlink(nd, &nva, p, &pathcp,
|
|
&pathlen);
|
|
if (error)
|
|
goto nfsmout;
|
|
break;
|
|
case NFCHR:
|
|
case NFBLK:
|
|
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
major = fxdr_unsigned(u_int32_t, *tl++);
|
|
minor = fxdr_unsigned(u_int32_t, *tl);
|
|
nva.na_rdev = NFSMAKEDEV(major, minor);
|
|
break;
|
|
case NFSOCK:
|
|
case NFFIFO:
|
|
break;
|
|
case NFDIR:
|
|
cnflags = (LOCKPARENT | SAVENAME);
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_BADTYPE;
|
|
vrele(dp);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
goto out;
|
|
}
|
|
}
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags | NOCACHE);
|
|
nfsvno_setpathbuf(&named, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
|
|
if (error)
|
|
goto nfsmout;
|
|
if (!nd->nd_repstat) {
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
vtyp = nfsv34tov_type(*tl);
|
|
}
|
|
error = nfsrv_sattr(nd, NULL, &nva, &attrbits, aclp, p);
|
|
if (error)
|
|
goto nfsmout;
|
|
nva.na_type = vtyp;
|
|
if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) &&
|
|
(vtyp == VCHR || vtyp == VBLK)) {
|
|
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
major = fxdr_unsigned(u_int32_t, *tl++);
|
|
minor = fxdr_unsigned(u_int32_t, *tl);
|
|
nva.na_rdev = NFSMAKEDEV(major, minor);
|
|
}
|
|
}
|
|
|
|
dirfor_ret = nfsvno_getattr(dp, &dirfor, nd, p, 0, NULL);
|
|
if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
|
|
if (!dirfor_ret && NFSVNO_ISSETGID(&nva) &&
|
|
dirfor.na_gid == nva.na_gid)
|
|
NFSVNO_UNSET(&nva, gid);
|
|
nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
|
|
}
|
|
if (nd->nd_repstat) {
|
|
vrele(dp);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
nfsvno_relpathbuf(&named);
|
|
if (pathcp)
|
|
free(pathcp, M_TEMP);
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
|
|
&diraft);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Yuck! For V4, mkdir and link are here and some V4 clients don't fill
|
|
* in va_mode, so we'll have to set a default here.
|
|
*/
|
|
if (NFSVNO_NOTSETMODE(&nva)) {
|
|
if (vtyp == VLNK)
|
|
nva.na_mode = 0755;
|
|
else
|
|
nva.na_mode = 0400;
|
|
}
|
|
|
|
if (vtyp == VDIR)
|
|
named.ni_cnd.cn_flags |= WILLBEDIR;
|
|
nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
|
|
if (nd->nd_repstat) {
|
|
if (dirp) {
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd,
|
|
p, 0, NULL);
|
|
vrele(dirp);
|
|
}
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
|
|
&diraft);
|
|
goto out;
|
|
}
|
|
if (dirp)
|
|
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
|
|
|
|
if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) {
|
|
if (vtyp == VDIR) {
|
|
nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp,
|
|
&dirfor, &diraft, &diraft_ret, &attrbits, aclp, p,
|
|
exp);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
goto out;
|
|
} else if (vtyp == VLNK) {
|
|
nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
|
|
&dirfor, &diraft, &diraft_ret, &attrbits,
|
|
aclp, p, exp, pathcp, pathlen);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
free(pathcp, M_TEMP);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
|
|
if (!nd->nd_repstat) {
|
|
vp = named.ni_vp;
|
|
nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp);
|
|
nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
|
|
if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1,
|
|
NULL);
|
|
if (vpp != NULL && nd->nd_repstat == 0) {
|
|
NFSVOPUNLOCK(vp);
|
|
*vpp = vp;
|
|
} else
|
|
vput(vp);
|
|
}
|
|
|
|
diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
|
|
vrele(dirp);
|
|
if (!nd->nd_repstat) {
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
|
|
nfsrv_postopattr(nd, 0, &nva);
|
|
} else {
|
|
NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
|
|
*tl++ = newnfs_false;
|
|
txdr_hyper(dirfor.na_filerev, tl);
|
|
tl += 2;
|
|
txdr_hyper(diraft.na_filerev, tl);
|
|
(void) nfsrv_putattrbit(nd, &attrbits);
|
|
}
|
|
}
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vrele(dp);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
if (bufp)
|
|
nfsvno_relpathbuf(&named);
|
|
if (pathcp)
|
|
free(pathcp, M_TEMP);
|
|
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs remove service
|
|
*/
|
|
int
|
|
nfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t dp, struct nfsexstuff *exp)
|
|
{
|
|
struct nameidata named;
|
|
u_int32_t *tl;
|
|
int error = 0, dirfor_ret = 1, diraft_ret = 1;
|
|
vnode_t dirp = NULL;
|
|
struct nfsvattr dirfor, diraft;
|
|
char *bufp;
|
|
u_long *hashp;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
goto out;
|
|
}
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE,
|
|
LOCKPARENT | LOCKLEAF);
|
|
nfsvno_setpathbuf(&named, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
|
|
if (error) {
|
|
vput(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
goto out;
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
|
|
} else {
|
|
vput(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
}
|
|
if (dirp) {
|
|
if (!(nd->nd_flag & ND_NFSV2)) {
|
|
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
|
|
NULL);
|
|
} else {
|
|
vrele(dirp);
|
|
dirp = NULL;
|
|
}
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
if (nd->nd_flag & ND_NFSV4) {
|
|
if (vnode_vtype(named.ni_vp) == VDIR)
|
|
nd->nd_repstat = nfsvno_rmdirsub(&named, 1,
|
|
nd->nd_cred, p, exp);
|
|
else
|
|
nd->nd_repstat = nfsvno_removesub(&named, 1,
|
|
nd->nd_cred, p, exp);
|
|
} else if (nd->nd_procnum == NFSPROC_RMDIR) {
|
|
nd->nd_repstat = nfsvno_rmdirsub(&named, 0,
|
|
nd->nd_cred, p, exp);
|
|
} else {
|
|
nd->nd_repstat = nfsvno_removesub(&named, 0,
|
|
nd->nd_cred, p, exp);
|
|
}
|
|
}
|
|
if (!(nd->nd_flag & ND_NFSV2)) {
|
|
if (dirp) {
|
|
diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0,
|
|
NULL);
|
|
vrele(dirp);
|
|
}
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
|
|
&diraft);
|
|
} else if (!nd->nd_repstat) {
|
|
NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
|
|
*tl++ = newnfs_false;
|
|
txdr_hyper(dirfor.na_filerev, tl);
|
|
tl += 2;
|
|
txdr_hyper(diraft.na_filerev, tl);
|
|
}
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs rename service
|
|
*/
|
|
int
|
|
nfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
|
|
vnode_t dp, vnode_t todp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
|
|
{
|
|
u_int32_t *tl;
|
|
int error = 0, fdirfor_ret = 1, fdiraft_ret = 1;
|
|
int tdirfor_ret = 1, tdiraft_ret = 1;
|
|
struct nameidata fromnd, tond;
|
|
vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL;
|
|
struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft;
|
|
struct nfsexstuff tnes;
|
|
struct nfsrvfh tfh;
|
|
char *bufp, *tbufp = NULL;
|
|
u_long *hashp;
|
|
fhandle_t fh;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
|
|
nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
|
|
goto out;
|
|
}
|
|
if (!(nd->nd_flag & ND_NFSV2))
|
|
fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd, p, 1, NULL);
|
|
tond.ni_cnd.cn_nameiop = 0;
|
|
tond.ni_startdir = NULL;
|
|
NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT | SAVESTART);
|
|
nfsvno_setpathbuf(&fromnd, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen);
|
|
if (error) {
|
|
vput(dp);
|
|
if (todp)
|
|
vrele(todp);
|
|
nfsvno_relpathbuf(&fromnd);
|
|
goto out;
|
|
}
|
|
/*
|
|
* Unlock dp in this code section, so it is unlocked before
|
|
* tdp gets locked. This avoids a potential LOR if tdp is the
|
|
* parent directory of dp.
|
|
*/
|
|
if (nd->nd_flag & ND_NFSV4) {
|
|
tdp = todp;
|
|
tnes = *toexp;
|
|
if (dp != tdp) {
|
|
NFSVOPUNLOCK(dp);
|
|
/* Might lock tdp. */
|
|
tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 0,
|
|
NULL);
|
|
} else {
|
|
tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 1,
|
|
NULL);
|
|
NFSVOPUNLOCK(dp);
|
|
}
|
|
} else {
|
|
tfh.nfsrvfh_len = 0;
|
|
error = nfsrv_mtofh(nd, &tfh);
|
|
if (error == 0)
|
|
error = nfsvno_getfh(dp, &fh, p);
|
|
if (error) {
|
|
vput(dp);
|
|
/* todp is always NULL except NFSv4 */
|
|
nfsvno_relpathbuf(&fromnd);
|
|
goto out;
|
|
}
|
|
|
|
/* If this is the same file handle, just VREF() the vnode. */
|
|
if (tfh.nfsrvfh_len == NFSX_MYFH &&
|
|
!NFSBCMP(tfh.nfsrvfh_data, &fh, NFSX_MYFH)) {
|
|
VREF(dp);
|
|
tdp = dp;
|
|
tnes = *exp;
|
|
tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd, p, 1,
|
|
NULL);
|
|
NFSVOPUNLOCK(dp);
|
|
} else {
|
|
NFSVOPUNLOCK(dp);
|
|
nd->nd_cred->cr_uid = nd->nd_saveduid;
|
|
nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL,
|
|
0, -1); /* Locks tdp. */
|
|
if (tdp) {
|
|
tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd,
|
|
p, 1, NULL);
|
|
NFSVOPUNLOCK(tdp);
|
|
}
|
|
}
|
|
}
|
|
NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART);
|
|
nfsvno_setpathbuf(&tond, &tbufp, &hashp);
|
|
if (!nd->nd_repstat) {
|
|
error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen);
|
|
if (error) {
|
|
if (tdp)
|
|
vrele(tdp);
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&fromnd);
|
|
nfsvno_relpathbuf(&tond);
|
|
goto out;
|
|
}
|
|
}
|
|
if (nd->nd_repstat) {
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
|
|
&fdiraft);
|
|
nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
|
|
&tdiraft);
|
|
}
|
|
if (tdp)
|
|
vrele(tdp);
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&fromnd);
|
|
nfsvno_relpathbuf(&tond);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Done parsing, now down to business.
|
|
*/
|
|
nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 0, exp, p, &fdirp);
|
|
if (nd->nd_repstat) {
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
|
|
&fdiraft);
|
|
nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
|
|
&tdiraft);
|
|
}
|
|
if (fdirp)
|
|
vrele(fdirp);
|
|
if (tdp)
|
|
vrele(tdp);
|
|
nfsvno_relpathbuf(&tond);
|
|
goto out;
|
|
}
|
|
if (vnode_vtype(fromnd.ni_vp) == VDIR)
|
|
tond.ni_cnd.cn_flags |= WILLBEDIR;
|
|
nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, p, &tdirp);
|
|
nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd->nd_repstat,
|
|
nd->nd_flag, nd->nd_cred, p);
|
|
if (fdirp)
|
|
fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd, p, 0, NULL);
|
|
if (tdirp)
|
|
tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd, p, 0, NULL);
|
|
if (fdirp)
|
|
vrele(fdirp);
|
|
if (tdirp)
|
|
vrele(tdirp);
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
|
|
nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
|
|
} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
|
|
NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
|
|
*tl++ = newnfs_false;
|
|
txdr_hyper(fdirfor.na_filerev, tl);
|
|
tl += 2;
|
|
txdr_hyper(fdiraft.na_filerev, tl);
|
|
tl += 2;
|
|
*tl++ = newnfs_false;
|
|
txdr_hyper(tdirfor.na_filerev, tl);
|
|
tl += 2;
|
|
txdr_hyper(tdiraft.na_filerev, tl);
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs link service
|
|
*/
|
|
int
|
|
nfsrvd_link(struct nfsrv_descript *nd, int isdgram,
|
|
vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
|
|
{
|
|
struct nameidata named;
|
|
u_int32_t *tl;
|
|
int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1;
|
|
vnode_t dirp = NULL, dp = NULL;
|
|
struct nfsvattr dirfor, diraft, at;
|
|
struct nfsexstuff tnes;
|
|
struct nfsrvfh dfh;
|
|
char *bufp;
|
|
u_long *hashp;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_postopattr(nd, getret, &at);
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
goto out;
|
|
}
|
|
NFSVOPUNLOCK(vp);
|
|
if (vnode_vtype(vp) == VDIR) {
|
|
if (nd->nd_flag & ND_NFSV4)
|
|
nd->nd_repstat = NFSERR_ISDIR;
|
|
else
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
if (tovp)
|
|
vrele(tovp);
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
if (nd->nd_flag & ND_NFSV4) {
|
|
dp = tovp;
|
|
tnes = *toexp;
|
|
} else {
|
|
error = nfsrv_mtofh(nd, &dfh);
|
|
if (error) {
|
|
vrele(vp);
|
|
/* tovp is always NULL unless NFSv4 */
|
|
goto out;
|
|
}
|
|
nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL,
|
|
0, -1);
|
|
if (dp)
|
|
NFSVOPUNLOCK(dp);
|
|
}
|
|
}
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
|
|
LOCKPARENT | SAVENAME | NOCACHE);
|
|
if (!nd->nd_repstat) {
|
|
nfsvno_setpathbuf(&named, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
|
|
if (error) {
|
|
vrele(vp);
|
|
if (dp)
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
goto out;
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
|
|
p, &dirp);
|
|
} else {
|
|
if (dp)
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
}
|
|
}
|
|
if (dirp) {
|
|
if (nd->nd_flag & ND_NFSV2) {
|
|
vrele(dirp);
|
|
dirp = NULL;
|
|
} else {
|
|
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
|
|
NULL);
|
|
}
|
|
}
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp);
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
getret = nfsvno_getattr(vp, &at, nd, p, 0, NULL);
|
|
if (dirp) {
|
|
diraft_ret = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
|
|
vrele(dirp);
|
|
}
|
|
vrele(vp);
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
nfsrv_postopattr(nd, getret, &at);
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
} else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
|
|
NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
|
|
*tl++ = newnfs_false;
|
|
txdr_hyper(dirfor.na_filerev, tl);
|
|
tl += 2;
|
|
txdr_hyper(diraft.na_filerev, tl);
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs symbolic link service
|
|
*/
|
|
int
|
|
nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
|
|
{
|
|
struct nfsvattr nva, dirfor, diraft;
|
|
struct nameidata named;
|
|
int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
|
|
vnode_t dirp = NULL;
|
|
char *bufp, *pathcp = NULL;
|
|
u_long *hashp;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
goto out;
|
|
}
|
|
if (vpp)
|
|
*vpp = NULL;
|
|
NFSVNO_ATTRINIT(&nva);
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
|
|
LOCKPARENT | SAVESTART | NOCACHE);
|
|
nfsvno_setpathbuf(&named, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
|
|
if (!error && !nd->nd_repstat)
|
|
error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
|
|
if (error) {
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
goto out;
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
|
|
} else {
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
}
|
|
if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
|
|
vrele(dirp);
|
|
dirp = NULL;
|
|
}
|
|
|
|
/*
|
|
* And call nfsrvd_symlinksub() to do the common code. It will
|
|
* return EBADRPC upon a parsing error, 0 otherwise.
|
|
*/
|
|
if (!nd->nd_repstat) {
|
|
if (dirp != NULL)
|
|
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
|
|
NULL);
|
|
nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
|
|
&dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
|
|
pathcp, pathlen);
|
|
} else if (dirp != NULL) {
|
|
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
|
|
vrele(dirp);
|
|
}
|
|
if (pathcp)
|
|
free(pathcp, M_TEMP);
|
|
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
if (!nd->nd_repstat) {
|
|
(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
|
|
nfsrv_postopattr(nd, 0, &nva);
|
|
}
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Common code for creating a symbolic link.
|
|
*/
|
|
static void
|
|
nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
|
|
struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
|
|
vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
|
|
int *diraft_retp, nfsattrbit_t *attrbitp,
|
|
NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
|
|
int pathlen)
|
|
{
|
|
u_int32_t *tl;
|
|
|
|
nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
|
|
!(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
|
|
if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
|
|
nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp);
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
|
|
nvap, nd, p, 1, NULL);
|
|
}
|
|
if (vpp != NULL && nd->nd_repstat == 0) {
|
|
NFSVOPUNLOCK(ndp->ni_vp);
|
|
*vpp = ndp->ni_vp;
|
|
} else
|
|
vput(ndp->ni_vp);
|
|
}
|
|
if (dirp) {
|
|
*diraft_retp = nfsvno_getattr(dirp, diraftp, nd, p, 0, NULL);
|
|
vrele(dirp);
|
|
}
|
|
if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
|
|
NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
|
|
*tl++ = newnfs_false;
|
|
txdr_hyper(dirforp->na_filerev, tl);
|
|
tl += 2;
|
|
txdr_hyper(diraftp->na_filerev, tl);
|
|
(void) nfsrv_putattrbit(nd, attrbitp);
|
|
}
|
|
|
|
NFSEXITCODE2(0, nd);
|
|
}
|
|
|
|
/*
|
|
* nfs mkdir service
|
|
*/
|
|
int
|
|
nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t dp, vnode_t *vpp, fhandle_t *fhp, struct nfsexstuff *exp)
|
|
{
|
|
struct nfsvattr nva, dirfor, diraft;
|
|
struct nameidata named;
|
|
u_int32_t *tl;
|
|
int error = 0, dirfor_ret = 1, diraft_ret = 1;
|
|
vnode_t dirp = NULL;
|
|
char *bufp;
|
|
u_long *hashp;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
goto out;
|
|
}
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
|
|
LOCKPARENT | SAVENAME | NOCACHE);
|
|
nfsvno_setpathbuf(&named, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
|
|
if (error)
|
|
goto nfsmout;
|
|
if (!nd->nd_repstat) {
|
|
NFSVNO_ATTRINIT(&nva);
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
error = nfsrv_sattr(nd, NULL, &nva, NULL, NULL, p);
|
|
if (error)
|
|
goto nfsmout;
|
|
} else {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
nva.na_mode = nfstov_mode(*tl++);
|
|
}
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
|
|
} else {
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
}
|
|
if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
|
|
vrele(dirp);
|
|
dirp = NULL;
|
|
}
|
|
if (nd->nd_repstat) {
|
|
if (dirp != NULL) {
|
|
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0,
|
|
NULL);
|
|
vrele(dirp);
|
|
}
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
|
|
&diraft);
|
|
goto out;
|
|
}
|
|
if (dirp != NULL)
|
|
dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd, p, 0, NULL);
|
|
|
|
/*
|
|
* Call nfsrvd_mkdirsub() for the code common to V4 as well.
|
|
*/
|
|
nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
|
|
&diraft_ret, NULL, NULL, p, exp);
|
|
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
if (!nd->nd_repstat) {
|
|
(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
|
|
nfsrv_postopattr(nd, 0, &nva);
|
|
}
|
|
nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
|
|
} else if (!nd->nd_repstat) {
|
|
(void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
|
|
nfsrv_fillattr(nd, &nva);
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Code common to mkdir for V2,3 and 4.
|
|
*/
|
|
static void
|
|
nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
|
|
struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
|
|
vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
|
|
int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
|
|
NFSPROC_T *p, struct nfsexstuff *exp)
|
|
{
|
|
vnode_t vp;
|
|
u_int32_t *tl;
|
|
|
|
NFSVNO_SETATTRVAL(nvap, type, VDIR);
|
|
nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
|
|
nd->nd_cred, p, exp);
|
|
if (!nd->nd_repstat) {
|
|
vp = ndp->ni_vp;
|
|
nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp);
|
|
nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
|
|
if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_getattr(vp, nvap, nd, p, 1,
|
|
NULL);
|
|
if (vpp && !nd->nd_repstat) {
|
|
NFSVOPUNLOCK(vp);
|
|
*vpp = vp;
|
|
} else {
|
|
vput(vp);
|
|
}
|
|
}
|
|
if (dirp) {
|
|
*diraft_retp = nfsvno_getattr(dirp, diraftp, nd, p, 0, NULL);
|
|
vrele(dirp);
|
|
}
|
|
if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
|
|
NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
|
|
*tl++ = newnfs_false;
|
|
txdr_hyper(dirforp->na_filerev, tl);
|
|
tl += 2;
|
|
txdr_hyper(diraftp->na_filerev, tl);
|
|
(void) nfsrv_putattrbit(nd, attrbitp);
|
|
}
|
|
|
|
NFSEXITCODE2(0, nd);
|
|
}
|
|
|
|
/*
|
|
* nfs commit service
|
|
*/
|
|
int
|
|
nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
struct nfsvattr bfor, aft;
|
|
u_int32_t *tl;
|
|
int error = 0, for_ret = 1, aft_ret = 1, cnt;
|
|
u_int64_t off;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
|
|
goto out;
|
|
}
|
|
|
|
/* Return NFSERR_ISDIR in NFSv4 when commit on a directory. */
|
|
if (vp->v_type != VREG) {
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
error = NFSERR_NOTSUPP;
|
|
else
|
|
error = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_INVAL;
|
|
goto nfsmout;
|
|
}
|
|
NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
|
|
|
|
/*
|
|
* XXX At this time VOP_FSYNC() does not accept offset and byte
|
|
* count parameters, so these arguments are useless (someday maybe).
|
|
*/
|
|
off = fxdr_hyper(tl);
|
|
tl += 2;
|
|
cnt = fxdr_unsigned(int, *tl);
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
for_ret = nfsvno_getattr(vp, &bfor, nd, p, 1, NULL);
|
|
nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
|
|
if (nd->nd_flag & ND_NFSV3) {
|
|
aft_ret = nfsvno_getattr(vp, &aft, nd, p, 1, NULL);
|
|
nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
|
|
}
|
|
vput(vp);
|
|
if (!nd->nd_repstat) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
|
|
*tl++ = txdr_unsigned(nfsboottime.tv_sec);
|
|
*tl = txdr_unsigned(nfsboottime.tv_usec);
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs statfs service
|
|
*/
|
|
int
|
|
nfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
struct statfs *sf;
|
|
u_int32_t *tl;
|
|
int getret = 1;
|
|
struct nfsvattr at;
|
|
u_quad_t tval;
|
|
struct thread *p = curthread;
|
|
|
|
sf = NULL;
|
|
if (nd->nd_repstat) {
|
|
nfsrv_postopattr(nd, getret, &at);
|
|
goto out;
|
|
}
|
|
sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
|
|
nd->nd_repstat = nfsvno_statfs(vp, sf);
|
|
getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
|
|
vput(vp);
|
|
if (nd->nd_flag & ND_NFSV3)
|
|
nfsrv_postopattr(nd, getret, &at);
|
|
if (nd->nd_repstat)
|
|
goto out;
|
|
if (nd->nd_flag & ND_NFSV2) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
|
|
*tl++ = txdr_unsigned(NFS_V2MAXDATA);
|
|
*tl++ = txdr_unsigned(sf->f_bsize);
|
|
*tl++ = txdr_unsigned(sf->f_blocks);
|
|
*tl++ = txdr_unsigned(sf->f_bfree);
|
|
*tl = txdr_unsigned(sf->f_bavail);
|
|
} else {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
|
|
tval = (u_quad_t)sf->f_blocks;
|
|
tval *= (u_quad_t)sf->f_bsize;
|
|
txdr_hyper(tval, tl); tl += 2;
|
|
tval = (u_quad_t)sf->f_bfree;
|
|
tval *= (u_quad_t)sf->f_bsize;
|
|
txdr_hyper(tval, tl); tl += 2;
|
|
tval = (u_quad_t)sf->f_bavail;
|
|
tval *= (u_quad_t)sf->f_bsize;
|
|
txdr_hyper(tval, tl); tl += 2;
|
|
tval = (u_quad_t)sf->f_files;
|
|
txdr_hyper(tval, tl); tl += 2;
|
|
tval = (u_quad_t)sf->f_ffree;
|
|
txdr_hyper(tval, tl); tl += 2;
|
|
tval = (u_quad_t)sf->f_ffree;
|
|
txdr_hyper(tval, tl); tl += 2;
|
|
*tl = 0;
|
|
}
|
|
|
|
out:
|
|
free(sf, M_STATFS);
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* nfs fsinfo service
|
|
*/
|
|
int
|
|
nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
struct nfsfsinfo fs;
|
|
int getret = 1;
|
|
struct nfsvattr at;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_postopattr(nd, getret, &at);
|
|
goto out;
|
|
}
|
|
getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
|
|
nfsvno_getfs(&fs, isdgram);
|
|
vput(vp);
|
|
nfsrv_postopattr(nd, getret, &at);
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
|
|
*tl++ = txdr_unsigned(fs.fs_rtmax);
|
|
*tl++ = txdr_unsigned(fs.fs_rtpref);
|
|
*tl++ = txdr_unsigned(fs.fs_rtmult);
|
|
*tl++ = txdr_unsigned(fs.fs_wtmax);
|
|
*tl++ = txdr_unsigned(fs.fs_wtpref);
|
|
*tl++ = txdr_unsigned(fs.fs_wtmult);
|
|
*tl++ = txdr_unsigned(fs.fs_dtpref);
|
|
txdr_hyper(fs.fs_maxfilesize, tl);
|
|
tl += 2;
|
|
txdr_nfsv3time(&fs.fs_timedelta, tl);
|
|
tl += 2;
|
|
*tl = txdr_unsigned(fs.fs_properties);
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* nfs pathconf service
|
|
*/
|
|
int
|
|
nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
struct nfsv3_pathconf *pc;
|
|
int getret = 1;
|
|
long linkmax, namemax, chownres, notrunc;
|
|
struct nfsvattr at;
|
|
struct thread *p = curthread;
|
|
|
|
if (nd->nd_repstat) {
|
|
nfsrv_postopattr(nd, getret, &at);
|
|
goto out;
|
|
}
|
|
nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
|
|
nd->nd_cred, p);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
|
|
nd->nd_cred, p);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
|
|
&chownres, nd->nd_cred, p);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, ¬runc,
|
|
nd->nd_cred, p);
|
|
getret = nfsvno_getattr(vp, &at, nd, p, 1, NULL);
|
|
vput(vp);
|
|
nfsrv_postopattr(nd, getret, &at);
|
|
if (!nd->nd_repstat) {
|
|
NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
|
|
pc->pc_linkmax = txdr_unsigned(linkmax);
|
|
pc->pc_namemax = txdr_unsigned(namemax);
|
|
pc->pc_notrunc = txdr_unsigned(notrunc);
|
|
pc->pc_chownrestricted = txdr_unsigned(chownres);
|
|
|
|
/*
|
|
* These should probably be supported by VOP_PATHCONF(), but
|
|
* until msdosfs is exportable (why would you want to?), the
|
|
* Unix defaults should be ok.
|
|
*/
|
|
pc->pc_caseinsensitive = newnfs_false;
|
|
pc->pc_casepreserving = newnfs_true;
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 lock service
|
|
*/
|
|
int
|
|
nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int i;
|
|
struct nfsstate *stp = NULL;
|
|
struct nfslock *lop;
|
|
struct nfslockconflict cf;
|
|
int error = 0;
|
|
u_short flags = NFSLCK_LOCK, lflags;
|
|
u_int64_t offset, len;
|
|
nfsv4stateid_t stateid;
|
|
nfsquad_t clientid;
|
|
struct thread *p = curthread;
|
|
|
|
NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
|
|
i = fxdr_unsigned(int, *tl++);
|
|
switch (i) {
|
|
case NFSV4LOCKT_READW:
|
|
flags |= NFSLCK_BLOCKING;
|
|
case NFSV4LOCKT_READ:
|
|
lflags = NFSLCK_READ;
|
|
break;
|
|
case NFSV4LOCKT_WRITEW:
|
|
flags |= NFSLCK_BLOCKING;
|
|
case NFSV4LOCKT_WRITE:
|
|
lflags = NFSLCK_WRITE;
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
if (*tl++ == newnfs_true)
|
|
flags |= NFSLCK_RECLAIM;
|
|
offset = fxdr_hyper(tl);
|
|
tl += 2;
|
|
len = fxdr_hyper(tl);
|
|
tl += 2;
|
|
if (*tl == newnfs_true)
|
|
flags |= NFSLCK_OPENTOLOCK;
|
|
if (flags & NFSLCK_OPENTOLOCK) {
|
|
NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
|
|
i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
|
|
if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
stp = malloc(sizeof (struct nfsstate) + i,
|
|
M_NFSDSTATE, M_WAITOK);
|
|
stp->ls_ownerlen = i;
|
|
stp->ls_op = nd->nd_rp;
|
|
stp->ls_seq = fxdr_unsigned(int, *tl++);
|
|
stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
|
|
NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set
|
|
* the stateid to the current stateid, if it is set.
|
|
*/
|
|
if ((nd->nd_flag & ND_NFSV41) != 0 &&
|
|
stp->ls_stateid.seqid == 1 &&
|
|
stp->ls_stateid.other[0] == 0 &&
|
|
stp->ls_stateid.other[1] == 0 &&
|
|
stp->ls_stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
|
|
stp->ls_stateid = nd->nd_curstateid;
|
|
stp->ls_stateid.seqid = 0;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
|
|
clientid.lval[0] = *tl++;
|
|
clientid.lval[1] = *tl++;
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK3 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
|
|
if (error)
|
|
goto nfsmout;
|
|
} else {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
|
|
stp = malloc(sizeof (struct nfsstate),
|
|
M_NFSDSTATE, M_WAITOK);
|
|
stp->ls_ownerlen = 0;
|
|
stp->ls_op = nd->nd_rp;
|
|
stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
|
|
NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set
|
|
* the stateid to the current stateid, if it is set.
|
|
*/
|
|
if ((nd->nd_flag & ND_NFSV41) != 0 &&
|
|
stp->ls_stateid.seqid == 1 &&
|
|
stp->ls_stateid.other[0] == 0 &&
|
|
stp->ls_stateid.other[1] == 0 &&
|
|
stp->ls_stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
|
|
stp->ls_stateid = nd->nd_curstateid;
|
|
stp->ls_stateid.seqid = 0;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
stp->ls_seq = fxdr_unsigned(int, *tl);
|
|
clientid.lval[0] = stp->ls_stateid.other[0];
|
|
clientid.lval[1] = stp->ls_stateid.other[1];
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK4 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
}
|
|
lop = malloc(sizeof (struct nfslock),
|
|
M_NFSDLOCK, M_WAITOK);
|
|
lop->lo_first = offset;
|
|
if (len == NFS64BITSSET) {
|
|
lop->lo_end = NFS64BITSSET;
|
|
} else {
|
|
lop->lo_end = offset + len;
|
|
if (lop->lo_end <= lop->lo_first)
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
lop->lo_flags = lflags;
|
|
stp->ls_flags = flags;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
|
|
/*
|
|
* Do basic access checking.
|
|
*/
|
|
if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
|
|
if (vnode_vtype(vp) == VDIR)
|
|
nd->nd_repstat = NFSERR_ISDIR;
|
|
else
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
if (lflags & NFSLCK_WRITE) {
|
|
nd->nd_repstat = nfsvno_accchk(vp, VWRITE,
|
|
nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
|
|
NFSACCCHK_VPISLOCKED, NULL);
|
|
} else {
|
|
nd->nd_repstat = nfsvno_accchk(vp, VREAD,
|
|
nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
|
|
NFSACCCHK_VPISLOCKED, NULL);
|
|
if (nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
|
|
nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
|
|
NFSACCCHK_VPISLOCKED, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We call nfsrv_lockctrl() even if nd_repstat set, so that the
|
|
* seqid# gets updated. nfsrv_lockctrl() will return the value
|
|
* of nd_repstat, if it gets that far.
|
|
*/
|
|
nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
|
|
&stateid, exp, nd, p);
|
|
if (lop)
|
|
free(lop, M_NFSDLOCK);
|
|
if (stp)
|
|
free(stp, M_NFSDSTATE);
|
|
if (!nd->nd_repstat) {
|
|
/* For NFSv4.1, set the Current StateID. */
|
|
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
|
nd->nd_curstateid = stateid;
|
|
nd->nd_flag |= ND_CURSTATEID;
|
|
}
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
|
|
*tl++ = txdr_unsigned(stateid.seqid);
|
|
NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
|
|
} else if (nd->nd_repstat == NFSERR_DENIED) {
|
|
NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
|
|
txdr_hyper(cf.cl_first, tl);
|
|
tl += 2;
|
|
if (cf.cl_end == NFS64BITSSET)
|
|
len = NFS64BITSSET;
|
|
else
|
|
len = cf.cl_end - cf.cl_first;
|
|
txdr_hyper(len, tl);
|
|
tl += 2;
|
|
if (cf.cl_flags == NFSLCK_WRITE)
|
|
*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
|
|
else
|
|
*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
|
|
*tl++ = stateid.other[0];
|
|
*tl = stateid.other[1];
|
|
(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
|
|
}
|
|
vput(vp);
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vput(vp);
|
|
if (stp)
|
|
free(stp, M_NFSDSTATE);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 lock test service
|
|
*/
|
|
int
|
|
nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int i;
|
|
struct nfsstate *stp = NULL;
|
|
struct nfslock lo, *lop = &lo;
|
|
struct nfslockconflict cf;
|
|
int error = 0;
|
|
nfsv4stateid_t stateid;
|
|
nfsquad_t clientid;
|
|
u_int64_t len;
|
|
struct thread *p = curthread;
|
|
|
|
NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
|
|
i = fxdr_unsigned(int, *(tl + 7));
|
|
if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
stp = malloc(sizeof (struct nfsstate) + i,
|
|
M_NFSDSTATE, M_WAITOK);
|
|
stp->ls_ownerlen = i;
|
|
stp->ls_op = NULL;
|
|
stp->ls_flags = NFSLCK_TEST;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
i = fxdr_unsigned(int, *tl++);
|
|
switch (i) {
|
|
case NFSV4LOCKT_READW:
|
|
stp->ls_flags |= NFSLCK_BLOCKING;
|
|
case NFSV4LOCKT_READ:
|
|
lo.lo_flags = NFSLCK_READ;
|
|
break;
|
|
case NFSV4LOCKT_WRITEW:
|
|
stp->ls_flags |= NFSLCK_BLOCKING;
|
|
case NFSV4LOCKT_WRITE:
|
|
lo.lo_flags = NFSLCK_WRITE;
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
lo.lo_first = fxdr_hyper(tl);
|
|
tl += 2;
|
|
len = fxdr_hyper(tl);
|
|
if (len == NFS64BITSSET) {
|
|
lo.lo_end = NFS64BITSSET;
|
|
} else {
|
|
lo.lo_end = lo.lo_first + len;
|
|
if (lo.lo_end <= lo.lo_first)
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
tl += 2;
|
|
clientid.lval[0] = *tl++;
|
|
clientid.lval[1] = *tl;
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK5 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
|
|
if (error)
|
|
goto nfsmout;
|
|
if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
|
|
if (vnode_vtype(vp) == VDIR)
|
|
nd->nd_repstat = NFSERR_ISDIR;
|
|
else
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
|
|
&stateid, exp, nd, p);
|
|
if (nd->nd_repstat) {
|
|
if (nd->nd_repstat == NFSERR_DENIED) {
|
|
NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
|
|
txdr_hyper(cf.cl_first, tl);
|
|
tl += 2;
|
|
if (cf.cl_end == NFS64BITSSET)
|
|
len = NFS64BITSSET;
|
|
else
|
|
len = cf.cl_end - cf.cl_first;
|
|
txdr_hyper(len, tl);
|
|
tl += 2;
|
|
if (cf.cl_flags == NFSLCK_WRITE)
|
|
*tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
|
|
else
|
|
*tl++ = txdr_unsigned(NFSV4LOCKT_READ);
|
|
*tl++ = stp->ls_stateid.other[0];
|
|
*tl = stp->ls_stateid.other[1];
|
|
(void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
|
|
}
|
|
}
|
|
vput(vp);
|
|
if (stp)
|
|
free(stp, M_NFSDSTATE);
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vput(vp);
|
|
if (stp)
|
|
free(stp, M_NFSDSTATE);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 unlock service
|
|
*/
|
|
int
|
|
nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int i;
|
|
struct nfsstate *stp;
|
|
struct nfslock *lop;
|
|
int error = 0;
|
|
nfsv4stateid_t stateid;
|
|
nfsquad_t clientid;
|
|
u_int64_t len;
|
|
struct thread *p = curthread;
|
|
|
|
NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
|
|
stp = malloc(sizeof (struct nfsstate),
|
|
M_NFSDSTATE, M_WAITOK);
|
|
lop = malloc(sizeof (struct nfslock),
|
|
M_NFSDLOCK, M_WAITOK);
|
|
stp->ls_flags = NFSLCK_UNLOCK;
|
|
lop->lo_flags = NFSLCK_UNLOCK;
|
|
stp->ls_op = nd->nd_rp;
|
|
i = fxdr_unsigned(int, *tl++);
|
|
switch (i) {
|
|
case NFSV4LOCKT_READW:
|
|
stp->ls_flags |= NFSLCK_BLOCKING;
|
|
case NFSV4LOCKT_READ:
|
|
break;
|
|
case NFSV4LOCKT_WRITEW:
|
|
stp->ls_flags |= NFSLCK_BLOCKING;
|
|
case NFSV4LOCKT_WRITE:
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
free(stp, M_NFSDSTATE);
|
|
free(lop, M_NFSDLOCK);
|
|
goto nfsmout;
|
|
}
|
|
stp->ls_ownerlen = 0;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
stp->ls_seq = fxdr_unsigned(int, *tl++);
|
|
stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
|
|
NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set the
|
|
* stateid to the current stateid, if it is set.
|
|
*/
|
|
if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
|
|
stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
|
|
stp->ls_stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
|
|
stp->ls_stateid = nd->nd_curstateid;
|
|
stp->ls_stateid.seqid = 0;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
free(stp, M_NFSDSTATE);
|
|
free(lop, M_NFSDLOCK);
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
lop->lo_first = fxdr_hyper(tl);
|
|
tl += 2;
|
|
len = fxdr_hyper(tl);
|
|
if (len == NFS64BITSSET) {
|
|
lop->lo_end = NFS64BITSSET;
|
|
} else {
|
|
lop->lo_end = lop->lo_first + len;
|
|
if (lop->lo_end <= lop->lo_first)
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
clientid.lval[0] = stp->ls_stateid.other[0];
|
|
clientid.lval[1] = stp->ls_stateid.other[1];
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK6 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
|
|
if (vnode_vtype(vp) == VDIR)
|
|
nd->nd_repstat = NFSERR_ISDIR;
|
|
else
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
/*
|
|
* Call nfsrv_lockctrl() even if nd_repstat is set, so that the
|
|
* seqid# gets incremented. nfsrv_lockctrl() will return the
|
|
* value of nd_repstat, if it gets that far.
|
|
*/
|
|
nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
|
|
&stateid, exp, nd, p);
|
|
if (stp)
|
|
free(stp, M_NFSDSTATE);
|
|
if (lop)
|
|
free(lop, M_NFSDLOCK);
|
|
if (!nd->nd_repstat) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
|
|
*tl++ = txdr_unsigned(stateid.seqid);
|
|
NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
|
|
}
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 open service
|
|
*/
|
|
int
|
|
nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int i, retext;
|
|
struct nfsstate *stp = NULL;
|
|
int error = 0, create, claim, exclusive_flag = 0, override;
|
|
u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
|
|
int how = NFSCREATE_UNCHECKED;
|
|
int32_t cverf[2], tverf[2] = { 0, 0 };
|
|
vnode_t vp = NULL, dirp = NULL;
|
|
struct nfsvattr nva, dirfor, diraft;
|
|
struct nameidata named;
|
|
nfsv4stateid_t stateid, delegstateid;
|
|
nfsattrbit_t attrbits;
|
|
nfsquad_t clientid;
|
|
char *bufp = NULL;
|
|
u_long *hashp;
|
|
NFSACL_T *aclp = NULL;
|
|
struct thread *p = curthread;
|
|
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
aclp = acl_alloc(M_WAITOK);
|
|
aclp->acl_cnt = 0;
|
|
#endif
|
|
NFSZERO_ATTRBIT(&attrbits);
|
|
named.ni_startdir = NULL;
|
|
named.ni_cnd.cn_nameiop = 0;
|
|
NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
|
|
i = fxdr_unsigned(int, *(tl + 5));
|
|
if (i <= 0 || i > NFSV4_OPAQUELIMIT) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
stp = malloc(sizeof (struct nfsstate) + i,
|
|
M_NFSDSTATE, M_WAITOK);
|
|
stp->ls_ownerlen = i;
|
|
stp->ls_op = nd->nd_rp;
|
|
stp->ls_flags = NFSLCK_OPEN;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
|
|
i = fxdr_unsigned(int, *tl++);
|
|
retext = 0;
|
|
if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG |
|
|
NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) {
|
|
retext = 1;
|
|
/* For now, ignore these. */
|
|
i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG);
|
|
switch (i & NFSV4OPEN_WANTDELEGMASK) {
|
|
case NFSV4OPEN_WANTANYDELEG:
|
|
stp->ls_flags |= (NFSLCK_WANTRDELEG |
|
|
NFSLCK_WANTWDELEG);
|
|
i &= ~NFSV4OPEN_WANTDELEGMASK;
|
|
break;
|
|
case NFSV4OPEN_WANTREADDELEG:
|
|
stp->ls_flags |= NFSLCK_WANTRDELEG;
|
|
i &= ~NFSV4OPEN_WANTDELEGMASK;
|
|
break;
|
|
case NFSV4OPEN_WANTWRITEDELEG:
|
|
stp->ls_flags |= NFSLCK_WANTWDELEG;
|
|
i &= ~NFSV4OPEN_WANTDELEGMASK;
|
|
break;
|
|
case NFSV4OPEN_WANTNODELEG:
|
|
stp->ls_flags |= NFSLCK_WANTNODELEG;
|
|
i &= ~NFSV4OPEN_WANTDELEGMASK;
|
|
break;
|
|
case NFSV4OPEN_WANTCANCEL:
|
|
printf("NFSv4: ignore Open WantCancel\n");
|
|
i &= ~NFSV4OPEN_WANTDELEGMASK;
|
|
break;
|
|
default:
|
|
/* nd_repstat will be set to NFSERR_INVAL below. */
|
|
break;
|
|
}
|
|
}
|
|
switch (i) {
|
|
case NFSV4OPEN_ACCESSREAD:
|
|
stp->ls_flags |= NFSLCK_READACCESS;
|
|
break;
|
|
case NFSV4OPEN_ACCESSWRITE:
|
|
stp->ls_flags |= NFSLCK_WRITEACCESS;
|
|
break;
|
|
case NFSV4OPEN_ACCESSBOTH:
|
|
stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
i = fxdr_unsigned(int, *tl++);
|
|
switch (i) {
|
|
case NFSV4OPEN_DENYNONE:
|
|
break;
|
|
case NFSV4OPEN_DENYREAD:
|
|
stp->ls_flags |= NFSLCK_READDENY;
|
|
break;
|
|
case NFSV4OPEN_DENYWRITE:
|
|
stp->ls_flags |= NFSLCK_WRITEDENY;
|
|
break;
|
|
case NFSV4OPEN_DENYBOTH:
|
|
stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
clientid.lval[0] = *tl++;
|
|
clientid.lval[1] = *tl;
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK7 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
|
|
if (error)
|
|
goto nfsmout;
|
|
NFSVNO_ATTRINIT(&nva);
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
create = fxdr_unsigned(int, *tl);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd, p, 0, NULL);
|
|
if (create == NFSV4OPEN_CREATE) {
|
|
nva.na_type = VREG;
|
|
nva.na_mode = 0;
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
how = fxdr_unsigned(int, *tl);
|
|
switch (how) {
|
|
case NFSCREATE_UNCHECKED:
|
|
case NFSCREATE_GUARDED:
|
|
error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
|
|
if (error)
|
|
goto nfsmout;
|
|
/*
|
|
* If the na_gid being set is the same as that of
|
|
* the directory it is going in, clear it, since
|
|
* that is what will be set by default. This allows
|
|
* a user that isn't in that group to do the create.
|
|
*/
|
|
if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
|
|
nva.na_gid == dirfor.na_gid)
|
|
NFSVNO_UNSET(&nva, gid);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
|
|
break;
|
|
case NFSCREATE_EXCLUSIVE:
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
|
|
cverf[0] = *tl++;
|
|
cverf[1] = *tl;
|
|
break;
|
|
case NFSCREATE_EXCLUSIVE41:
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
|
|
cverf[0] = *tl++;
|
|
cverf[1] = *tl;
|
|
error = nfsv4_sattr(nd, NULL, &nva, &attrbits, aclp, p);
|
|
if (error != 0)
|
|
goto nfsmout;
|
|
if (NFSISSET_ATTRBIT(&attrbits,
|
|
NFSATTRBIT_TIMEACCESSSET))
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
/*
|
|
* If the na_gid being set is the same as that of
|
|
* the directory it is going in, clear it, since
|
|
* that is what will be set by default. This allows
|
|
* a user that isn't in that group to do the create.
|
|
*/
|
|
if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) &&
|
|
nva.na_gid == dirfor.na_gid)
|
|
NFSVNO_UNSET(&nva, gid);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
} else if (create != NFSV4OPEN_NOCREATE) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
|
|
/*
|
|
* Now, handle the claim, which usually includes looking up a
|
|
* name in the directory referenced by dp. The exception is
|
|
* NFSV4OPEN_CLAIMPREVIOUS.
|
|
*/
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
claim = fxdr_unsigned(int, *tl);
|
|
if (claim == NFSV4OPEN_CLAIMDELEGATECUR || claim ==
|
|
NFSV4OPEN_CLAIMDELEGATECURFH) {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
|
|
stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
|
|
stp->ls_flags |= NFSLCK_DELEGCUR;
|
|
} else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV || claim ==
|
|
NFSV4OPEN_CLAIMDELEGATEPREVFH) {
|
|
stp->ls_flags |= NFSLCK_DELEGPREV;
|
|
}
|
|
if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
|
|
|| claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
|
|
if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
|
|
claim != NFSV4OPEN_CLAIMNULL)
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
if (nd->nd_repstat) {
|
|
nd->nd_repstat = nfsrv_opencheck(clientid,
|
|
&stateid, stp, NULL, nd, p, nd->nd_repstat);
|
|
goto nfsmout;
|
|
}
|
|
if (create == NFSV4OPEN_CREATE)
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
|
|
LOCKPARENT | LOCKLEAF | SAVESTART | NOCACHE);
|
|
else
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
|
|
LOCKLEAF | SAVESTART);
|
|
nfsvno_setpathbuf(&named, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
|
|
if (error) {
|
|
vrele(dp);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
free(stp, M_NFSDSTATE);
|
|
nfsvno_relpathbuf(&named);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
|
|
p, &dirp);
|
|
} else {
|
|
vrele(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
}
|
|
if (create == NFSV4OPEN_CREATE) {
|
|
switch (how) {
|
|
case NFSCREATE_UNCHECKED:
|
|
if (named.ni_vp) {
|
|
/*
|
|
* Clear the setable attribute bits, except
|
|
* for Size, if it is being truncated.
|
|
*/
|
|
NFSZERO_ATTRBIT(&attrbits);
|
|
if (NFSVNO_ISSETSIZE(&nva))
|
|
NFSSETBIT_ATTRBIT(&attrbits,
|
|
NFSATTRBIT_SIZE);
|
|
}
|
|
break;
|
|
case NFSCREATE_GUARDED:
|
|
if (named.ni_vp && !nd->nd_repstat)
|
|
nd->nd_repstat = EEXIST;
|
|
break;
|
|
case NFSCREATE_EXCLUSIVE:
|
|
exclusive_flag = 1;
|
|
if (!named.ni_vp)
|
|
nva.na_mode = 0;
|
|
break;
|
|
case NFSCREATE_EXCLUSIVE41:
|
|
exclusive_flag = 1;
|
|
break;
|
|
}
|
|
}
|
|
nfsvno_open(nd, &named, clientid, &stateid, stp,
|
|
&exclusive_flag, &nva, cverf, create, aclp, &attrbits,
|
|
nd->nd_cred, exp, &vp);
|
|
} else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim ==
|
|
NFSV4OPEN_CLAIMFH || claim == NFSV4OPEN_CLAIMDELEGATECURFH ||
|
|
claim == NFSV4OPEN_CLAIMDELEGATEPREVFH) {
|
|
if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
i = fxdr_unsigned(int, *tl);
|
|
switch (i) {
|
|
case NFSV4OPEN_DELEGATEREAD:
|
|
stp->ls_flags |= NFSLCK_DELEGREAD;
|
|
break;
|
|
case NFSV4OPEN_DELEGATEWRITE:
|
|
stp->ls_flags |= NFSLCK_DELEGWRITE;
|
|
case NFSV4OPEN_DELEGATENONE:
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
stp->ls_flags |= NFSLCK_RECLAIM;
|
|
} else {
|
|
if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE)
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
vp = dp;
|
|
NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY);
|
|
if (!VN_IS_DOOMED(vp))
|
|
nd->nd_repstat = nfsrv_opencheck(clientid, &stateid,
|
|
stp, vp, nd, p, nd->nd_repstat);
|
|
else
|
|
nd->nd_repstat = NFSERR_PERM;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
|
|
/*
|
|
* Do basic access checking.
|
|
*/
|
|
if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
|
|
/*
|
|
* The IETF working group decided that this is the correct
|
|
* error return for all non-regular files.
|
|
*/
|
|
nd->nd_repstat = (vp->v_type == VDIR) ? NFSERR_ISDIR : NFSERR_SYMLINK;
|
|
}
|
|
|
|
/*
|
|
* If the Open is being done for a file that already exists, apply
|
|
* normal permission checking including for the file owner, if
|
|
* vfs.nfsd.v4openaccess is set.
|
|
* Previously, the owner was always allowed to open the file to
|
|
* be consistent with the NFS tradition of always allowing the
|
|
* owner of the file to write to the file regardless of permissions.
|
|
* It now appears that the Linux client expects the owner
|
|
* permissions to be checked for opens that are not creating the
|
|
* file. I believe the correct approach is to use the Access
|
|
* operation's results to be consistent with NFSv3, but that is
|
|
* not what the current Linux client appears to be doing.
|
|
* Since both the Linux and OpenSolaris NFSv4 servers do this check,
|
|
* I have enabled it by default.
|
|
* If this semantic change causes a problem, it can be disabled by
|
|
* setting the sysctl vfs.nfsd.v4openaccess to 0 to re-enable the
|
|
* previous semantics.
|
|
*/
|
|
if (nfsrv_openaccess && create == NFSV4OPEN_NOCREATE)
|
|
override = NFSACCCHK_NOOVERRIDE;
|
|
else
|
|
override = NFSACCCHK_ALLOWOWNER;
|
|
if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
|
|
nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred,
|
|
exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
|
|
if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
|
|
nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred,
|
|
exp, p, override, NFSACCCHK_VPISLOCKED, NULL);
|
|
if (nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_accchk(vp, VEXEC,
|
|
nd->nd_cred, exp, p, override,
|
|
NFSACCCHK_VPISLOCKED, NULL);
|
|
}
|
|
|
|
if (!nd->nd_repstat) {
|
|
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
|
|
if (!nd->nd_repstat) {
|
|
tverf[0] = nva.na_atime.tv_sec;
|
|
tverf[1] = nva.na_atime.tv_nsec;
|
|
}
|
|
}
|
|
if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] ||
|
|
cverf[1] != tverf[1]))
|
|
nd->nd_repstat = EEXIST;
|
|
/*
|
|
* Do the open locking/delegation stuff.
|
|
*/
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
|
|
&delegstateid, &rflags, exp, p, nva.na_filerev);
|
|
|
|
/*
|
|
* vp must be unlocked before the call to nfsvno_getattr(dirp,...)
|
|
* below, to avoid a deadlock with the lookup in nfsvno_namei() above.
|
|
* (ie: Leave the NFSVOPUNLOCK() about here.)
|
|
*/
|
|
if (vp)
|
|
NFSVOPUNLOCK(vp);
|
|
if (stp)
|
|
free(stp, M_NFSDSTATE);
|
|
if (!nd->nd_repstat && dirp)
|
|
nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd, p, 0, NULL);
|
|
if (!nd->nd_repstat) {
|
|
/* For NFSv4.1, set the Current StateID. */
|
|
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
|
nd->nd_curstateid = stateid;
|
|
nd->nd_flag |= ND_CURSTATEID;
|
|
}
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(stateid.seqid);
|
|
NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
|
|
*tl++ = newnfs_true;
|
|
*tl++ = 0;
|
|
*tl++ = 0;
|
|
*tl++ = 0;
|
|
*tl++ = 0;
|
|
} else {
|
|
*tl++ = newnfs_false; /* Since dirp is not locked */
|
|
txdr_hyper(dirfor.na_filerev, tl);
|
|
tl += 2;
|
|
txdr_hyper(diraft.na_filerev, tl);
|
|
tl += 2;
|
|
}
|
|
*tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
|
|
(void) nfsrv_putattrbit(nd, &attrbits);
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
if (rflags & NFSV4OPEN_READDELEGATE)
|
|
*tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
|
|
else if (rflags & NFSV4OPEN_WRITEDELEGATE)
|
|
*tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
|
|
else if (retext != 0) {
|
|
*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT);
|
|
if ((rflags & NFSV4OPEN_WDNOTWANTED) != 0) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
|
|
} else if ((rflags & NFSV4OPEN_WDSUPPFTYPE) != 0) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(NFSV4OPEN_NOTSUPPFTYPE);
|
|
} else if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) {
|
|
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION);
|
|
*tl = newnfs_false;
|
|
} else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) {
|
|
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE);
|
|
*tl = newnfs_false;
|
|
} else {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(NFSV4OPEN_NOTWANTED);
|
|
}
|
|
} else
|
|
*tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
|
|
if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(delegstateid.seqid);
|
|
NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
|
|
NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
if (rflags & NFSV4OPEN_RECALL)
|
|
*tl = newnfs_true;
|
|
else
|
|
*tl = newnfs_false;
|
|
if (rflags & NFSV4OPEN_WRITEDELEGATE) {
|
|
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
|
|
txdr_hyper(nva.na_size, tl);
|
|
}
|
|
NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
|
|
*tl++ = txdr_unsigned(0x0);
|
|
acemask = NFSV4ACE_ALLFILESMASK;
|
|
if (nva.na_mode & S_IRUSR)
|
|
acemask |= NFSV4ACE_READMASK;
|
|
if (nva.na_mode & S_IWUSR)
|
|
acemask |= NFSV4ACE_WRITEMASK;
|
|
if (nva.na_mode & S_IXUSR)
|
|
acemask |= NFSV4ACE_EXECUTEMASK;
|
|
*tl = txdr_unsigned(acemask);
|
|
(void) nfsm_strtom(nd, "OWNER@", 6);
|
|
}
|
|
*vpp = vp;
|
|
} else if (vp) {
|
|
vrele(vp);
|
|
}
|
|
if (dirp)
|
|
vrele(dirp);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vrele(dp);
|
|
#ifdef NFS4_ACL_EXTATTR_NAME
|
|
acl_free(aclp);
|
|
#endif
|
|
if (stp)
|
|
free(stp, M_NFSDSTATE);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 close service
|
|
*/
|
|
int
|
|
nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
struct nfsstate st, *stp = &st;
|
|
int error = 0, writeacc;
|
|
nfsv4stateid_t stateid;
|
|
nfsquad_t clientid;
|
|
struct nfsvattr na;
|
|
struct thread *p = curthread;
|
|
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
|
|
stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
|
|
stp->ls_ownerlen = 0;
|
|
stp->ls_op = nd->nd_rp;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
|
|
NFSX_STATEIDOTHER);
|
|
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set the
|
|
* stateid to the current stateid, if it is set.
|
|
*/
|
|
if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
|
|
stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
|
|
stp->ls_stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0)
|
|
stp->ls_stateid = nd->nd_curstateid;
|
|
else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
stp->ls_flags = NFSLCK_CLOSE;
|
|
clientid.lval[0] = stp->ls_stateid.other[0];
|
|
clientid.lval[1] = stp->ls_stateid.other[1];
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK8 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p,
|
|
&writeacc);
|
|
/* For pNFS, update the attributes. */
|
|
if (writeacc != 0 || nfsrv_pnfsatime != 0)
|
|
nfsrv_updatemdsattr(vp, &na, p);
|
|
vput(vp);
|
|
if (!nd->nd_repstat) {
|
|
/*
|
|
* If the stateid that has been closed is the current stateid,
|
|
* unset it.
|
|
*/
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0 &&
|
|
stateid.other[0] == nd->nd_curstateid.other[0] &&
|
|
stateid.other[1] == nd->nd_curstateid.other[1] &&
|
|
stateid.other[2] == nd->nd_curstateid.other[2])
|
|
nd->nd_flag &= ~ND_CURSTATEID;
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
|
|
*tl++ = txdr_unsigned(stateid.seqid);
|
|
NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
|
|
}
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 delegpurge service
|
|
*/
|
|
int
|
|
nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int error = 0;
|
|
nfsquad_t clientid;
|
|
struct thread *p = curthread;
|
|
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
clientid.lval[0] = *tl++;
|
|
clientid.lval[1] = *tl;
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK9 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL,
|
|
NFSV4OP_DELEGPURGE, nd->nd_cred, p, NULL);
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 delegreturn service
|
|
*/
|
|
int
|
|
nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int error = 0, writeacc;
|
|
nfsv4stateid_t stateid;
|
|
nfsquad_t clientid;
|
|
struct nfsvattr na;
|
|
struct thread *p = curthread;
|
|
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
|
|
stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
|
|
clientid.lval[0] = stateid.other[0];
|
|
clientid.lval[1] = stateid.other[1];
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK10 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp,
|
|
NFSV4OP_DELEGRETURN, nd->nd_cred, p, &writeacc);
|
|
/* For pNFS, update the attributes. */
|
|
if (writeacc != 0 || nfsrv_pnfsatime != 0)
|
|
nfsrv_updatemdsattr(vp, &na, p);
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 get file handle service
|
|
*/
|
|
int
|
|
nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
fhandle_t fh;
|
|
struct thread *p = curthread;
|
|
|
|
nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
|
|
vput(vp);
|
|
if (!nd->nd_repstat)
|
|
(void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 open confirm service
|
|
*/
|
|
int
|
|
nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
struct nfsstate st, *stp = &st;
|
|
int error = 0;
|
|
nfsv4stateid_t stateid;
|
|
nfsquad_t clientid;
|
|
struct thread *p = curthread;
|
|
|
|
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
goto nfsmout;
|
|
}
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
|
|
stp->ls_ownerlen = 0;
|
|
stp->ls_op = nd->nd_rp;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
|
|
NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
|
|
stp->ls_flags = NFSLCK_CONFIRM;
|
|
clientid.lval[0] = stp->ls_stateid.other[0];
|
|
clientid.lval[1] = stp->ls_stateid.other[1];
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK11 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p,
|
|
NULL);
|
|
if (!nd->nd_repstat) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
|
|
*tl++ = txdr_unsigned(stateid.seqid);
|
|
NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
|
|
}
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 open downgrade service
|
|
*/
|
|
int
|
|
nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int i;
|
|
struct nfsstate st, *stp = &st;
|
|
int error = 0;
|
|
nfsv4stateid_t stateid;
|
|
nfsquad_t clientid;
|
|
struct thread *p = curthread;
|
|
|
|
/* opendowngrade can only work on a file object.*/
|
|
if (vp->v_type != VREG) {
|
|
error = NFSERR_INVAL;
|
|
goto nfsmout;
|
|
}
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
|
|
stp->ls_ownerlen = 0;
|
|
stp->ls_op = nd->nd_rp;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
|
|
NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set the
|
|
* stateid to the current stateid, if it is set.
|
|
*/
|
|
if ((nd->nd_flag & ND_NFSV41) != 0 && stp->ls_stateid.seqid == 1 &&
|
|
stp->ls_stateid.other[0] == 0 && stp->ls_stateid.other[1] == 0 &&
|
|
stp->ls_stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0)
|
|
stp->ls_stateid = nd->nd_curstateid;
|
|
else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
|
|
i = fxdr_unsigned(int, *tl++);
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
i &= ~NFSV4OPEN_WANTDELEGMASK;
|
|
switch (i) {
|
|
case NFSV4OPEN_ACCESSREAD:
|
|
stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
|
|
break;
|
|
case NFSV4OPEN_ACCESSWRITE:
|
|
stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
|
|
break;
|
|
case NFSV4OPEN_ACCESSBOTH:
|
|
stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
|
|
NFSLCK_DOWNGRADE);
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
i = fxdr_unsigned(int, *tl);
|
|
switch (i) {
|
|
case NFSV4OPEN_DENYNONE:
|
|
break;
|
|
case NFSV4OPEN_DENYREAD:
|
|
stp->ls_flags |= NFSLCK_READDENY;
|
|
break;
|
|
case NFSV4OPEN_DENYWRITE:
|
|
stp->ls_flags |= NFSLCK_WRITEDENY;
|
|
break;
|
|
case NFSV4OPEN_DENYBOTH:
|
|
stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
|
|
clientid.lval[0] = stp->ls_stateid.other[0];
|
|
clientid.lval[1] = stp->ls_stateid.other[1];
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK12 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
|
|
nd, p, NULL);
|
|
if (!nd->nd_repstat) {
|
|
/* For NFSv4.1, set the Current StateID. */
|
|
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
|
nd->nd_curstateid = stateid;
|
|
nd->nd_flag |= ND_CURSTATEID;
|
|
}
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
|
|
*tl++ = txdr_unsigned(stateid.seqid);
|
|
NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
|
|
}
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 renew lease service
|
|
*/
|
|
int
|
|
nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int error = 0;
|
|
nfsquad_t clientid;
|
|
struct thread *p = curthread;
|
|
|
|
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
goto nfsmout;
|
|
}
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
|
|
clientid.lval[0] = *tl++;
|
|
clientid.lval[1] = *tl;
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK13 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
|
|
NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p);
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 security info service
|
|
*/
|
|
int
|
|
nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
|
|
vnode_t dp, struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int len;
|
|
struct nameidata named;
|
|
vnode_t dirp = NULL, vp;
|
|
struct nfsrvfh fh;
|
|
struct nfsexstuff retnes;
|
|
u_int32_t *sizp;
|
|
int error = 0, i;
|
|
uint64_t savflag;
|
|
char *bufp;
|
|
u_long *hashp;
|
|
struct thread *p = curthread;
|
|
|
|
/*
|
|
* All this just to get the export flags for the name.
|
|
*/
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
|
|
LOCKLEAF | SAVESTART);
|
|
nfsvno_setpathbuf(&named, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
|
|
if (error) {
|
|
vput(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
goto out;
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
|
|
} else {
|
|
vput(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
}
|
|
if (dirp)
|
|
vrele(dirp);
|
|
if (nd->nd_repstat)
|
|
goto out;
|
|
vrele(named.ni_startdir);
|
|
nfsvno_relpathbuf(&named);
|
|
fh.nfsrvfh_len = NFSX_MYFH;
|
|
vp = named.ni_vp;
|
|
nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
|
|
vput(vp);
|
|
savflag = nd->nd_flag;
|
|
if (!nd->nd_repstat) {
|
|
/*
|
|
* Pretend the next op is Secinfo, so that no wrongsec
|
|
* test will be done.
|
|
*/
|
|
nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0,
|
|
NFSV4OP_SECINFO);
|
|
if (vp)
|
|
vput(vp);
|
|
}
|
|
nd->nd_flag = savflag;
|
|
if (nd->nd_repstat)
|
|
goto out;
|
|
|
|
/*
|
|
* Finally have the export flags for name, so we can create
|
|
* the security info.
|
|
*/
|
|
len = 0;
|
|
NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
|
|
|
|
/* If nes_numsecflavor == 0, all are allowed. */
|
|
if (retnes.nes_numsecflavor == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(RPCAUTH_UNIX);
|
|
*tl = txdr_unsigned(RPCAUTH_GSS);
|
|
nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl++ = txdr_unsigned(RPCAUTHGSS_SVCNONE);
|
|
*tl = txdr_unsigned(RPCAUTH_GSS);
|
|
nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl++ = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
|
|
*tl = txdr_unsigned(RPCAUTH_GSS);
|
|
nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
|
|
len = 4;
|
|
}
|
|
for (i = 0; i < retnes.nes_numsecflavor; i++) {
|
|
if (retnes.nes_secflavors[i] == AUTH_SYS) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(RPCAUTH_UNIX);
|
|
len++;
|
|
} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(RPCAUTH_GSS);
|
|
(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
|
|
len++;
|
|
} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(RPCAUTH_GSS);
|
|
(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
|
|
len++;
|
|
} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
|
|
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(RPCAUTH_GSS);
|
|
(void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
|
|
len++;
|
|
}
|
|
}
|
|
*sizp = txdr_unsigned(len);
|
|
|
|
out:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 security info no name service
|
|
*/
|
|
int
|
|
nfsrvd_secinfononame(struct nfsrv_descript *nd, int isdgram,
|
|
vnode_t dp, struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl, *sizp;
|
|
struct nameidata named;
|
|
vnode_t dirp = NULL, vp;
|
|
struct nfsrvfh fh;
|
|
struct nfsexstuff retnes;
|
|
int error = 0, fhstyle, i, len;
|
|
uint64_t savflag;
|
|
char *bufp;
|
|
u_long *hashp;
|
|
struct thread *p = curthread;
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
|
|
fhstyle = fxdr_unsigned(int, *tl);
|
|
switch (fhstyle) {
|
|
case NFSSECINFONONAME_PARENT:
|
|
NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
|
|
LOCKLEAF | SAVESTART);
|
|
nfsvno_setpathbuf(&named, &bufp, &hashp);
|
|
error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
|
|
if (error != 0) {
|
|
vput(dp);
|
|
nfsvno_relpathbuf(&named);
|
|
goto nfsmout;
|
|
}
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
|
|
else
|
|
vput(dp);
|
|
if (dirp != NULL)
|
|
vrele(dirp);
|
|
vrele(named.ni_startdir);
|
|
nfsvno_relpathbuf(&named);
|
|
vp = named.ni_vp;
|
|
break;
|
|
case NFSSECINFONONAME_CURFH:
|
|
vp = dp;
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
vput(dp);
|
|
}
|
|
if (nd->nd_repstat != 0)
|
|
goto nfsmout;
|
|
fh.nfsrvfh_len = NFSX_MYFH;
|
|
nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
|
|
vput(vp);
|
|
savflag = nd->nd_flag;
|
|
if (nd->nd_repstat == 0) {
|
|
/*
|
|
* Pretend the next op is Secinfo, so that no wrongsec
|
|
* test will be done.
|
|
*/
|
|
nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0,
|
|
NFSV4OP_SECINFO);
|
|
if (vp != NULL)
|
|
vput(vp);
|
|
}
|
|
nd->nd_flag = savflag;
|
|
if (nd->nd_repstat != 0)
|
|
goto nfsmout;
|
|
|
|
/*
|
|
* Finally have the export flags for fh/parent, so we can create
|
|
* the security info.
|
|
*/
|
|
len = 0;
|
|
NFSM_BUILD(sizp, uint32_t *, NFSX_UNSIGNED);
|
|
|
|
/* If nes_numsecflavor == 0, all are allowed. */
|
|
if (retnes.nes_numsecflavor == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(RPCAUTH_UNIX);
|
|
*tl = txdr_unsigned(RPCAUTH_GSS);
|
|
nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl++ = txdr_unsigned(RPCAUTHGSS_SVCNONE);
|
|
*tl = txdr_unsigned(RPCAUTH_GSS);
|
|
nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl++ = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
|
|
*tl = txdr_unsigned(RPCAUTH_GSS);
|
|
nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
|
|
len = 4;
|
|
}
|
|
for (i = 0; i < retnes.nes_numsecflavor; i++) {
|
|
if (retnes.nes_secflavors[i] == AUTH_SYS) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(RPCAUTH_UNIX);
|
|
len++;
|
|
} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(RPCAUTH_GSS);
|
|
nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl = txdr_unsigned(RPCAUTHGSS_SVCNONE);
|
|
len++;
|
|
} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(RPCAUTH_GSS);
|
|
nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY);
|
|
len++;
|
|
} else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(RPCAUTH_GSS);
|
|
nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
|
|
nfsgss_mechlist[KERBV_MECH].len);
|
|
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(GSS_KERBV_QOP);
|
|
*tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY);
|
|
len++;
|
|
}
|
|
}
|
|
*sizp = txdr_unsigned(len);
|
|
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 set client id service
|
|
*/
|
|
int
|
|
nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int i;
|
|
int error = 0, idlen;
|
|
struct nfsclient *clp = NULL;
|
|
#ifdef INET
|
|
struct sockaddr_in *rin;
|
|
#endif
|
|
#ifdef INET6
|
|
struct sockaddr_in6 *rin6;
|
|
#endif
|
|
#if defined(INET) || defined(INET6)
|
|
u_char *ucp, *ucp2;
|
|
#endif
|
|
u_char *verf, *addrbuf;
|
|
nfsquad_t clientid, confirm;
|
|
struct thread *p = curthread;
|
|
|
|
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
goto nfsmout;
|
|
}
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto out;
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
|
|
verf = (u_char *)tl;
|
|
tl += (NFSX_VERF / NFSX_UNSIGNED);
|
|
i = fxdr_unsigned(int, *tl);
|
|
if (i > NFSV4_OPAQUELIMIT || i <= 0) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
idlen = i;
|
|
if (nd->nd_flag & ND_GSS)
|
|
i += nd->nd_princlen;
|
|
clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
|
|
M_ZERO);
|
|
clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
|
|
nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
|
|
NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
|
|
/* Allocated large enough for an AF_INET or AF_INET6 socket. */
|
|
clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
|
|
M_WAITOK | M_ZERO);
|
|
clp->lc_req.nr_cred = NULL;
|
|
NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
|
|
clp->lc_idlen = idlen;
|
|
error = nfsrv_mtostr(nd, clp->lc_id, idlen);
|
|
if (error)
|
|
goto nfsmout;
|
|
if (nd->nd_flag & ND_GSS) {
|
|
clp->lc_flags = LCL_GSS;
|
|
if (nd->nd_flag & ND_GSSINTEGRITY)
|
|
clp->lc_flags |= LCL_GSSINTEGRITY;
|
|
else if (nd->nd_flag & ND_GSSPRIVACY)
|
|
clp->lc_flags |= LCL_GSSPRIVACY;
|
|
} else {
|
|
clp->lc_flags = 0;
|
|
}
|
|
if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
|
|
clp->lc_flags |= LCL_NAME;
|
|
clp->lc_namelen = nd->nd_princlen;
|
|
clp->lc_name = &clp->lc_id[idlen];
|
|
NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
|
|
} else {
|
|
clp->lc_uid = nd->nd_cred->cr_uid;
|
|
clp->lc_gid = nd->nd_cred->cr_gid;
|
|
}
|
|
|
|
/* If the client is using TLS, do so for the callback connection. */
|
|
if (nd->nd_flag & ND_TLS)
|
|
clp->lc_flags |= LCL_TLSCB;
|
|
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
|
|
error = nfsrv_getclientipaddr(nd, clp);
|
|
if (error)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
|
|
|
|
/*
|
|
* nfsrv_setclient() does the actual work of adding it to the
|
|
* client list. If there is no error, the structure has been
|
|
* linked into the client list and clp should no longer be used
|
|
* here. When an error is returned, it has not been linked in,
|
|
* so it should be free'd.
|
|
*/
|
|
nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
|
|
if (nd->nd_repstat == NFSERR_CLIDINUSE) {
|
|
/*
|
|
* 8 is the maximum length of the port# string.
|
|
*/
|
|
addrbuf = malloc(INET6_ADDRSTRLEN + 8, M_TEMP, M_WAITOK);
|
|
switch (clp->lc_req.nr_nam->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
if (clp->lc_flags & LCL_TCPCALLBACK)
|
|
(void) nfsm_strtom(nd, "tcp", 3);
|
|
else
|
|
(void) nfsm_strtom(nd, "udp", 3);
|
|
rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
|
|
ucp = (u_char *)&rin->sin_addr.s_addr;
|
|
ucp2 = (u_char *)&rin->sin_port;
|
|
sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
|
|
ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
|
|
ucp2[0] & 0xff, ucp2[1] & 0xff);
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
if (clp->lc_flags & LCL_TCPCALLBACK)
|
|
(void) nfsm_strtom(nd, "tcp6", 4);
|
|
else
|
|
(void) nfsm_strtom(nd, "udp6", 4);
|
|
rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
|
|
ucp = inet_ntop(AF_INET6, &rin6->sin6_addr, addrbuf,
|
|
INET6_ADDRSTRLEN);
|
|
if (ucp != NULL)
|
|
i = strlen(ucp);
|
|
else
|
|
i = 0;
|
|
ucp2 = (u_char *)&rin6->sin6_port;
|
|
sprintf(&addrbuf[i], ".%d.%d", ucp2[0] & 0xff,
|
|
ucp2[1] & 0xff);
|
|
break;
|
|
#endif
|
|
}
|
|
(void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
|
|
free(addrbuf, M_TEMP);
|
|
}
|
|
if (clp) {
|
|
free(clp->lc_req.nr_nam, M_SONAME);
|
|
NFSFREEMUTEX(&clp->lc_req.nr_mtx);
|
|
free(clp->lc_stateid, M_NFSDCLIENT);
|
|
free(clp, M_NFSDCLIENT);
|
|
}
|
|
if (!nd->nd_repstat) {
|
|
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
|
|
*tl++ = clientid.lval[0];
|
|
*tl++ = clientid.lval[1];
|
|
*tl++ = confirm.lval[0];
|
|
*tl = confirm.lval[1];
|
|
}
|
|
|
|
out:
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
if (clp) {
|
|
free(clp->lc_req.nr_nam, M_SONAME);
|
|
NFSFREEMUTEX(&clp->lc_req.nr_mtx);
|
|
free(clp->lc_stateid, M_NFSDCLIENT);
|
|
free(clp, M_NFSDCLIENT);
|
|
}
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 set client id confirm service
|
|
*/
|
|
int
|
|
nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
|
|
__unused int isdgram, __unused vnode_t vp,
|
|
__unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int error = 0;
|
|
nfsquad_t clientid, confirm;
|
|
struct thread *p = curthread;
|
|
|
|
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
goto nfsmout;
|
|
}
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
|
|
clientid.lval[0] = *tl++;
|
|
clientid.lval[1] = *tl++;
|
|
confirm.lval[0] = *tl++;
|
|
confirm.lval[1] = *tl;
|
|
|
|
/*
|
|
* nfsrv_getclient() searches the client list for a match and
|
|
* returns the appropriate NFSERR status.
|
|
*/
|
|
nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
|
|
NULL, NULL, confirm, 0, nd, p);
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 verify service
|
|
*/
|
|
int
|
|
nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
int error = 0, ret, fhsize = NFSX_MYFH;
|
|
struct nfsvattr nva;
|
|
struct statfs *sf;
|
|
struct nfsfsinfo fs;
|
|
fhandle_t fh;
|
|
struct thread *p = curthread;
|
|
|
|
sf = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
|
|
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, NULL);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_statfs(vp, sf);
|
|
if (!nd->nd_repstat)
|
|
nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
|
|
if (!nd->nd_repstat) {
|
|
nfsvno_getfs(&fs, isdgram);
|
|
error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
|
|
sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred);
|
|
if (!error) {
|
|
if (nd->nd_procnum == NFSV4OP_NVERIFY) {
|
|
if (ret == 0)
|
|
nd->nd_repstat = NFSERR_SAME;
|
|
else if (ret != NFSERR_NOTSAME)
|
|
nd->nd_repstat = ret;
|
|
} else if (ret)
|
|
nd->nd_repstat = ret;
|
|
}
|
|
}
|
|
vput(vp);
|
|
free(sf, M_STATFS);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs openattr rpc
|
|
*/
|
|
int
|
|
nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
|
|
__unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
int error = 0, createdir __unused;
|
|
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
|
|
createdir = fxdr_unsigned(int, *tl);
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
nfsmout:
|
|
vrele(dp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 release lock owner service
|
|
*/
|
|
int
|
|
nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
u_int32_t *tl;
|
|
struct nfsstate *stp = NULL;
|
|
int error = 0, len;
|
|
nfsquad_t clientid;
|
|
struct thread *p = curthread;
|
|
|
|
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
goto nfsmout;
|
|
}
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
|
|
len = fxdr_unsigned(int, *(tl + 2));
|
|
if (len <= 0 || len > NFSV4_OPAQUELIMIT) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
stp = malloc(sizeof (struct nfsstate) + len,
|
|
M_NFSDSTATE, M_WAITOK);
|
|
stp->ls_ownerlen = len;
|
|
stp->ls_op = NULL;
|
|
stp->ls_flags = NFSLCK_RELEASE;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
clientid.lval[0] = *tl++;
|
|
clientid.lval[1] = *tl;
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK14 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
error = nfsrv_mtostr(nd, stp->ls_owner, len);
|
|
if (error)
|
|
goto nfsmout;
|
|
nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
|
|
free(stp, M_NFSDSTATE);
|
|
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
if (stp)
|
|
free(stp, M_NFSDSTATE);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 exchange_id service
|
|
*/
|
|
int
|
|
nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
int error = 0, i, idlen;
|
|
struct nfsclient *clp = NULL;
|
|
nfsquad_t clientid, confirm;
|
|
uint8_t *verf;
|
|
uint32_t sp4type, v41flags;
|
|
uint64_t owner_minor;
|
|
struct timespec verstime;
|
|
#ifdef INET
|
|
struct sockaddr_in *sin, *rin;
|
|
#endif
|
|
#ifdef INET6
|
|
struct sockaddr_in6 *sin6, *rin6;
|
|
#endif
|
|
struct thread *p = curthread;
|
|
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
|
|
verf = (uint8_t *)tl;
|
|
tl += (NFSX_VERF / NFSX_UNSIGNED);
|
|
i = fxdr_unsigned(int, *tl);
|
|
if (i > NFSV4_OPAQUELIMIT || i <= 0) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
idlen = i;
|
|
if (nd->nd_flag & ND_GSS)
|
|
i += nd->nd_princlen;
|
|
clp = malloc(sizeof(struct nfsclient) + i, M_NFSDCLIENT, M_WAITOK |
|
|
M_ZERO);
|
|
clp->lc_stateid = malloc(sizeof(struct nfsstatehead) *
|
|
nfsrv_statehashsize, M_NFSDCLIENT, M_WAITOK);
|
|
NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
|
|
/* Allocated large enough for an AF_INET or AF_INET6 socket. */
|
|
clp->lc_req.nr_nam = malloc(sizeof(struct sockaddr_in6), M_SONAME,
|
|
M_WAITOK | M_ZERO);
|
|
switch (nd->nd_nam->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
rin = (struct sockaddr_in *)clp->lc_req.nr_nam;
|
|
sin = (struct sockaddr_in *)nd->nd_nam;
|
|
rin->sin_family = AF_INET;
|
|
rin->sin_len = sizeof(struct sockaddr_in);
|
|
rin->sin_port = 0;
|
|
rin->sin_addr.s_addr = sin->sin_addr.s_addr;
|
|
break;
|
|
#endif
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
rin6 = (struct sockaddr_in6 *)clp->lc_req.nr_nam;
|
|
sin6 = (struct sockaddr_in6 *)nd->nd_nam;
|
|
rin6->sin6_family = AF_INET6;
|
|
rin6->sin6_len = sizeof(struct sockaddr_in6);
|
|
rin6->sin6_port = 0;
|
|
rin6->sin6_addr = sin6->sin6_addr;
|
|
break;
|
|
#endif
|
|
}
|
|
clp->lc_req.nr_cred = NULL;
|
|
NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
|
|
clp->lc_idlen = idlen;
|
|
error = nfsrv_mtostr(nd, clp->lc_id, idlen);
|
|
if (error != 0)
|
|
goto nfsmout;
|
|
if ((nd->nd_flag & ND_GSS) != 0) {
|
|
clp->lc_flags = LCL_GSS | LCL_NFSV41;
|
|
if ((nd->nd_flag & ND_GSSINTEGRITY) != 0)
|
|
clp->lc_flags |= LCL_GSSINTEGRITY;
|
|
else if ((nd->nd_flag & ND_GSSPRIVACY) != 0)
|
|
clp->lc_flags |= LCL_GSSPRIVACY;
|
|
} else
|
|
clp->lc_flags = LCL_NFSV41;
|
|
if ((nd->nd_flag & ND_NFSV42) != 0)
|
|
clp->lc_flags |= LCL_NFSV42;
|
|
if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) {
|
|
clp->lc_flags |= LCL_NAME;
|
|
clp->lc_namelen = nd->nd_princlen;
|
|
clp->lc_name = &clp->lc_id[idlen];
|
|
NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
|
|
} else {
|
|
clp->lc_uid = nd->nd_cred->cr_uid;
|
|
clp->lc_gid = nd->nd_cred->cr_gid;
|
|
}
|
|
NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
|
|
v41flags = fxdr_unsigned(uint32_t, *tl++);
|
|
if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR |
|
|
NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS |
|
|
NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) {
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
goto nfsmout;
|
|
}
|
|
if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0)
|
|
confirm.lval[1] = 1;
|
|
else
|
|
confirm.lval[1] = 0;
|
|
if (nfsrv_devidcnt == 0)
|
|
v41flags = NFSV4EXCH_USENONPNFS | NFSV4EXCH_USEPNFSDS;
|
|
else
|
|
v41flags = NFSV4EXCH_USEPNFSMDS;
|
|
sp4type = fxdr_unsigned(uint32_t, *tl);
|
|
if (sp4type != NFSV4EXCH_SP4NONE) {
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
goto nfsmout;
|
|
}
|
|
|
|
/*
|
|
* nfsrv_setclient() does the actual work of adding it to the
|
|
* client list. If there is no error, the structure has been
|
|
* linked into the client list and clp should no longer be used
|
|
* here. When an error is returned, it has not been linked in,
|
|
* so it should be free'd.
|
|
*/
|
|
nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
|
|
if (clp != NULL) {
|
|
free(clp->lc_req.nr_nam, M_SONAME);
|
|
NFSFREEMUTEX(&clp->lc_req.nr_mtx);
|
|
free(clp->lc_stateid, M_NFSDCLIENT);
|
|
free(clp, M_NFSDCLIENT);
|
|
}
|
|
if (nd->nd_repstat == 0) {
|
|
if (confirm.lval[1] != 0)
|
|
v41flags |= NFSV4EXCH_CONFIRMEDR;
|
|
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED);
|
|
*tl++ = clientid.lval[0]; /* ClientID */
|
|
*tl++ = clientid.lval[1];
|
|
*tl++ = txdr_unsigned(confirm.lval[0]); /* SequenceID */
|
|
*tl++ = txdr_unsigned(v41flags); /* Exch flags */
|
|
*tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE); /* No SSV */
|
|
owner_minor = 0; /* Owner */
|
|
txdr_hyper(owner_minor, tl); /* Minor */
|
|
(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
|
|
strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Major */
|
|
(void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid,
|
|
strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Scope */
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(1);
|
|
(void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org"));
|
|
(void)nfsm_strtom(nd, version, strlen(version));
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME);
|
|
verstime.tv_sec = 1293840000; /* Jan 1, 2011 */
|
|
verstime.tv_nsec = 0;
|
|
txdr_nfsv4time(&verstime, tl);
|
|
}
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
if (clp != NULL) {
|
|
free(clp->lc_req.nr_nam, M_SONAME);
|
|
NFSFREEMUTEX(&clp->lc_req.nr_mtx);
|
|
free(clp->lc_stateid, M_NFSDCLIENT);
|
|
free(clp, M_NFSDCLIENT);
|
|
}
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 create session service
|
|
*/
|
|
int
|
|
nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
int error = 0;
|
|
nfsquad_t clientid, confirm;
|
|
struct nfsdsession *sep = NULL;
|
|
uint32_t rdmacnt;
|
|
struct thread *p = curthread;
|
|
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto nfsmout;
|
|
sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession),
|
|
M_NFSDSESSION, M_WAITOK | M_ZERO);
|
|
sep->sess_refcnt = 1;
|
|
mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF);
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED);
|
|
clientid.lval[0] = *tl++;
|
|
clientid.lval[1] = *tl++;
|
|
confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++);
|
|
sep->sess_crflags = fxdr_unsigned(uint32_t, *tl);
|
|
/* Persistent sessions and RDMA are not supported. */
|
|
sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN;
|
|
|
|
/* Fore channel attributes. */
|
|
NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
|
|
tl++; /* Header pad always 0. */
|
|
sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++);
|
|
if (sep->sess_maxreq > sb_max_adj - NFS_MAXXDR) {
|
|
sep->sess_maxreq = sb_max_adj - NFS_MAXXDR;
|
|
printf("Consider increasing kern.ipc.maxsockbuf\n");
|
|
}
|
|
sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++);
|
|
if (sep->sess_maxresp > sb_max_adj - NFS_MAXXDR) {
|
|
sep->sess_maxresp = sb_max_adj - NFS_MAXXDR;
|
|
printf("Consider increasing kern.ipc.maxsockbuf\n");
|
|
}
|
|
sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++);
|
|
sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++);
|
|
sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++);
|
|
if (sep->sess_maxslots > NFSV4_SLOTS)
|
|
sep->sess_maxslots = NFSV4_SLOTS;
|
|
rdmacnt = fxdr_unsigned(uint32_t, *tl);
|
|
if (rdmacnt > 1) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
} else if (rdmacnt == 1)
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
|
|
|
|
/* Back channel attributes. */
|
|
NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED);
|
|
tl++; /* Header pad always 0. */
|
|
sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++);
|
|
sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++);
|
|
sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++);
|
|
sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++);
|
|
sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++);
|
|
rdmacnt = fxdr_unsigned(uint32_t, *tl);
|
|
if (rdmacnt > 1) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
} else if (rdmacnt == 1)
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
|
|
sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl);
|
|
|
|
/*
|
|
* nfsrv_getclient() searches the client list for a match and
|
|
* returns the appropriate NFSERR status.
|
|
*/
|
|
nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW,
|
|
NULL, sep, confirm, sep->sess_cbprogram, nd, p);
|
|
if (nd->nd_repstat == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
|
|
NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID);
|
|
NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(confirm.lval[0]); /* sequenceid */
|
|
*tl++ = txdr_unsigned(sep->sess_crflags);
|
|
|
|
/* Fore channel attributes. */
|
|
*tl++ = 0;
|
|
*tl++ = txdr_unsigned(sep->sess_maxreq);
|
|
*tl++ = txdr_unsigned(sep->sess_maxresp);
|
|
*tl++ = txdr_unsigned(sep->sess_maxrespcached);
|
|
*tl++ = txdr_unsigned(sep->sess_maxops);
|
|
*tl++ = txdr_unsigned(sep->sess_maxslots);
|
|
*tl++ = txdr_unsigned(1);
|
|
*tl++ = txdr_unsigned(0); /* No RDMA. */
|
|
|
|
/* Back channel attributes. */
|
|
*tl++ = 0;
|
|
*tl++ = txdr_unsigned(sep->sess_cbmaxreq);
|
|
*tl++ = txdr_unsigned(sep->sess_cbmaxresp);
|
|
*tl++ = txdr_unsigned(sep->sess_cbmaxrespcached);
|
|
*tl++ = txdr_unsigned(sep->sess_cbmaxops);
|
|
*tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots);
|
|
*tl++ = txdr_unsigned(1);
|
|
*tl = txdr_unsigned(0); /* No RDMA. */
|
|
}
|
|
nfsmout:
|
|
if (nd->nd_repstat != 0 && sep != NULL)
|
|
free(sep, M_NFSDSESSION);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 sequence service
|
|
*/
|
|
int
|
|
nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid;
|
|
int cache_this, error = 0;
|
|
struct thread *p = curthread;
|
|
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID);
|
|
NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID);
|
|
NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
|
|
sequenceid = fxdr_unsigned(uint32_t, *tl++);
|
|
nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++);
|
|
highest_slotid = fxdr_unsigned(uint32_t, *tl++);
|
|
if (*tl == newnfs_true)
|
|
cache_this = 1;
|
|
else
|
|
cache_this = 0;
|
|
nd->nd_flag |= ND_HASSEQUENCE;
|
|
nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid,
|
|
&target_highest_slotid, cache_this, &sflags, p);
|
|
if (nd->nd_repstat == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
|
|
NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID);
|
|
NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(sequenceid);
|
|
*tl++ = txdr_unsigned(nd->nd_slotid);
|
|
*tl++ = txdr_unsigned(highest_slotid);
|
|
*tl++ = txdr_unsigned(target_highest_slotid);
|
|
*tl = txdr_unsigned(sflags);
|
|
}
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 reclaim complete service
|
|
*/
|
|
int
|
|
nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
int error = 0, onefs;
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
|
|
/*
|
|
* I believe that a ReclaimComplete with rca_one_fs == TRUE is only
|
|
* to be used after a file system has been transferred to a different
|
|
* file server. However, RFC5661 is somewhat vague w.r.t. this and
|
|
* the ESXi 6.7 client does both a ReclaimComplete with rca_one_fs
|
|
* == TRUE and one with ReclaimComplete with rca_one_fs == FALSE.
|
|
* Therefore, just ignore the rca_one_fs == TRUE operation and return
|
|
* NFS_OK without doing anything.
|
|
*/
|
|
onefs = 0;
|
|
if (*tl == newnfs_true)
|
|
onefs = 1;
|
|
nd->nd_repstat = nfsrv_checkreclaimcomplete(nd, onefs);
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 destroy clientid service
|
|
*/
|
|
int
|
|
nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
nfsquad_t clientid;
|
|
int error = 0;
|
|
struct thread *p = curthread;
|
|
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
|
clientid.lval[0] = *tl++;
|
|
clientid.lval[1] = *tl;
|
|
nd->nd_repstat = nfsrv_destroyclient(clientid, p);
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 bind connection to session service
|
|
*/
|
|
int
|
|
nfsrvd_bindconnsess(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
uint8_t sessid[NFSX_V4SESSIONID];
|
|
int error = 0, foreaft;
|
|
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + 2 * NFSX_UNSIGNED);
|
|
NFSBCOPY(tl, sessid, NFSX_V4SESSIONID);
|
|
tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
|
|
foreaft = fxdr_unsigned(int, *tl++);
|
|
if (*tl == newnfs_true) {
|
|
/* RDMA is not supported. */
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
goto nfsmout;
|
|
}
|
|
|
|
nd->nd_repstat = nfsrv_bindconnsess(nd, sessid, &foreaft);
|
|
if (nd->nd_repstat == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 2 *
|
|
NFSX_UNSIGNED);
|
|
NFSBCOPY(sessid, tl, NFSX_V4SESSIONID);
|
|
tl += (NFSX_V4SESSIONID / NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(foreaft);
|
|
*tl = newnfs_false;
|
|
}
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 destroy session service
|
|
*/
|
|
int
|
|
nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint8_t *cp, sessid[NFSX_V4SESSIONID];
|
|
int error = 0;
|
|
|
|
if ((nd->nd_repstat = nfsd_checkrootexp(nd)) != 0)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID);
|
|
NFSBCOPY(cp, sessid, NFSX_V4SESSIONID);
|
|
nd->nd_repstat = nfsrv_destroysession(nd, sessid);
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 free stateid service
|
|
*/
|
|
int
|
|
nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
nfsv4stateid_t stateid;
|
|
int error = 0;
|
|
struct thread *p = curthread;
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
|
|
stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
|
|
NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
|
|
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set the
|
|
* stateid to the current stateid, if it is set.
|
|
*/
|
|
if (stateid.seqid == 1 && stateid.other[0] == 0 &&
|
|
stateid.other[1] == 0 && stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
|
|
stateid = nd->nd_curstateid;
|
|
stateid.seqid = 0;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p);
|
|
|
|
/* If the current stateid has been free'd, unset it. */
|
|
if (nd->nd_repstat == 0 && (nd->nd_flag & ND_CURSTATEID) != 0 &&
|
|
stateid.other[0] == nd->nd_curstateid.other[0] &&
|
|
stateid.other[1] == nd->nd_curstateid.other[1] &&
|
|
stateid.other[2] == nd->nd_curstateid.other[2])
|
|
nd->nd_flag &= ~ND_CURSTATEID;
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 layoutget service
|
|
*/
|
|
int
|
|
nfsrvd_layoutget(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
nfsv4stateid_t stateid;
|
|
int error = 0, layoutlen, layouttype, iomode, maxcnt, retonclose;
|
|
uint64_t offset, len, minlen;
|
|
char *layp;
|
|
struct thread *p = curthread;
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED + 3 * NFSX_HYPER +
|
|
NFSX_STATEID);
|
|
tl++; /* Signal layout available. Ignore for now. */
|
|
layouttype = fxdr_unsigned(int, *tl++);
|
|
iomode = fxdr_unsigned(int, *tl++);
|
|
offset = fxdr_hyper(tl); tl += 2;
|
|
len = fxdr_hyper(tl); tl += 2;
|
|
minlen = fxdr_hyper(tl); tl += 2;
|
|
stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
|
|
NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
maxcnt = fxdr_unsigned(int, *tl);
|
|
NFSD_DEBUG(4, "layoutget ltyp=%d iom=%d off=%ju len=%ju mlen=%ju\n",
|
|
layouttype, iomode, (uintmax_t)offset, (uintmax_t)len,
|
|
(uintmax_t)minlen);
|
|
if (len < minlen ||
|
|
(minlen != UINT64_MAX && offset + minlen < offset) ||
|
|
(len != UINT64_MAX && offset + len < offset)) {
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
goto nfsmout;
|
|
}
|
|
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set the
|
|
* stateid to the current stateid, if it is set.
|
|
*/
|
|
if (stateid.seqid == 1 && stateid.other[0] == 0 &&
|
|
stateid.other[1] == 0 && stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
|
|
stateid = nd->nd_curstateid;
|
|
stateid.seqid = 0;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
layp = NULL;
|
|
if (layouttype == NFSLAYOUT_NFSV4_1_FILES && nfsrv_maxpnfsmirror == 1)
|
|
layp = malloc(NFSX_V4FILELAYOUT, M_TEMP, M_WAITOK);
|
|
else if (layouttype == NFSLAYOUT_FLEXFILE)
|
|
layp = malloc(NFSX_V4FLEXLAYOUT(nfsrv_maxpnfsmirror), M_TEMP,
|
|
M_WAITOK);
|
|
else
|
|
nd->nd_repstat = NFSERR_UNKNLAYOUTTYPE;
|
|
if (layp != NULL)
|
|
nd->nd_repstat = nfsrv_layoutget(nd, vp, exp, layouttype,
|
|
&iomode, &offset, &len, minlen, &stateid, maxcnt,
|
|
&retonclose, &layoutlen, layp, nd->nd_cred, p);
|
|
NFSD_DEBUG(4, "nfsrv_layoutget stat=%u layoutlen=%d\n", nd->nd_repstat,
|
|
layoutlen);
|
|
if (nd->nd_repstat == 0) {
|
|
/* For NFSv4.1, set the Current StateID. */
|
|
if ((nd->nd_flag & ND_NFSV41) != 0) {
|
|
nd->nd_curstateid = stateid;
|
|
nd->nd_flag |= ND_CURSTATEID;
|
|
}
|
|
NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_STATEID +
|
|
2 * NFSX_HYPER);
|
|
*tl++ = txdr_unsigned(retonclose);
|
|
*tl++ = txdr_unsigned(stateid.seqid);
|
|
NFSBCOPY(stateid.other, tl, NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(1); /* Only returns one layout. */
|
|
txdr_hyper(offset, tl); tl += 2;
|
|
txdr_hyper(len, tl); tl += 2;
|
|
*tl++ = txdr_unsigned(iomode);
|
|
*tl = txdr_unsigned(layouttype);
|
|
nfsm_strtom(nd, layp, layoutlen);
|
|
} else if (nd->nd_repstat == NFSERR_LAYOUTTRYLATER) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = newnfs_false;
|
|
}
|
|
free(layp, M_TEMP);
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 layoutcommit service
|
|
*/
|
|
int
|
|
nfsrvd_layoutcommit(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
nfsv4stateid_t stateid;
|
|
int error = 0, hasnewoff, hasnewmtime, layouttype, maxcnt, reclaim;
|
|
int hasnewsize;
|
|
uint64_t offset, len, newoff = 0, newsize;
|
|
struct timespec newmtime;
|
|
char *layp;
|
|
struct thread *p = curthread;
|
|
|
|
layp = NULL;
|
|
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED + 2 * NFSX_HYPER +
|
|
NFSX_STATEID);
|
|
offset = fxdr_hyper(tl); tl += 2;
|
|
len = fxdr_hyper(tl); tl += 2;
|
|
reclaim = fxdr_unsigned(int, *tl++);
|
|
stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
|
|
NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set the
|
|
* stateid to the current stateid, if it is set.
|
|
*/
|
|
if (stateid.seqid == 1 && stateid.other[0] == 0 &&
|
|
stateid.other[1] == 0 && stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
|
|
stateid = nd->nd_curstateid;
|
|
stateid.seqid = 0;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
hasnewoff = fxdr_unsigned(int, *tl);
|
|
if (hasnewoff != 0) {
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
|
|
newoff = fxdr_hyper(tl); tl += 2;
|
|
} else
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
|
|
hasnewmtime = fxdr_unsigned(int, *tl);
|
|
if (hasnewmtime != 0) {
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_V4TIME + 2 * NFSX_UNSIGNED);
|
|
fxdr_nfsv4time(tl, &newmtime);
|
|
tl += (NFSX_V4TIME / NFSX_UNSIGNED);
|
|
} else
|
|
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
|
layouttype = fxdr_unsigned(int, *tl++);
|
|
maxcnt = fxdr_unsigned(int, *tl);
|
|
if (maxcnt > 0) {
|
|
layp = malloc(maxcnt + 1, M_TEMP, M_WAITOK);
|
|
error = nfsrv_mtostr(nd, layp, maxcnt);
|
|
if (error != 0)
|
|
goto nfsmout;
|
|
}
|
|
nd->nd_repstat = nfsrv_layoutcommit(nd, vp, layouttype, hasnewoff,
|
|
newoff, offset, len, hasnewmtime, &newmtime, reclaim, &stateid,
|
|
maxcnt, layp, &hasnewsize, &newsize, nd->nd_cred, p);
|
|
NFSD_DEBUG(4, "nfsrv_layoutcommit stat=%u\n", nd->nd_repstat);
|
|
if (nd->nd_repstat == 0) {
|
|
if (hasnewsize != 0) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED + NFSX_HYPER);
|
|
*tl++ = newnfs_true;
|
|
txdr_hyper(newsize, tl);
|
|
} else {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = newnfs_false;
|
|
}
|
|
}
|
|
nfsmout:
|
|
free(layp, M_TEMP);
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 layoutreturn service
|
|
*/
|
|
int
|
|
nfsrvd_layoutreturn(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl, *layp;
|
|
nfsv4stateid_t stateid;
|
|
int error = 0, fnd, kind, layouttype, iomode, maxcnt, reclaim;
|
|
uint64_t offset, len;
|
|
struct thread *p = curthread;
|
|
|
|
layp = NULL;
|
|
NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED);
|
|
reclaim = *tl++;
|
|
layouttype = fxdr_unsigned(int, *tl++);
|
|
iomode = fxdr_unsigned(int, *tl++);
|
|
kind = fxdr_unsigned(int, *tl);
|
|
NFSD_DEBUG(4, "layoutreturn recl=%d ltyp=%d iom=%d kind=%d\n", reclaim,
|
|
layouttype, iomode, kind);
|
|
if (kind == NFSV4LAYOUTRET_FILE) {
|
|
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
|
|
NFSX_UNSIGNED);
|
|
offset = fxdr_hyper(tl); tl += 2;
|
|
len = fxdr_hyper(tl); tl += 2;
|
|
stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
|
|
NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set
|
|
* the stateid to the current stateid, if it is set.
|
|
*/
|
|
if (stateid.seqid == 1 && stateid.other[0] == 0 &&
|
|
stateid.other[1] == 0 && stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
|
|
stateid = nd->nd_curstateid;
|
|
stateid.seqid = 0;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
maxcnt = fxdr_unsigned(int, *tl);
|
|
if (maxcnt > 0) {
|
|
layp = malloc(maxcnt + 1, M_TEMP, M_WAITOK);
|
|
error = nfsrv_mtostr(nd, (char *)layp, maxcnt);
|
|
if (error != 0)
|
|
goto nfsmout;
|
|
}
|
|
} else {
|
|
if (reclaim == newnfs_true) {
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
goto nfsmout;
|
|
}
|
|
offset = len = 0;
|
|
maxcnt = 0;
|
|
}
|
|
nd->nd_repstat = nfsrv_layoutreturn(nd, vp, layouttype, iomode,
|
|
offset, len, reclaim, kind, &stateid, maxcnt, layp, &fnd,
|
|
nd->nd_cred, p);
|
|
NFSD_DEBUG(4, "nfsrv_layoutreturn stat=%u fnd=%d\n", nd->nd_repstat,
|
|
fnd);
|
|
if (nd->nd_repstat == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
if (fnd != 0) {
|
|
*tl = newnfs_true;
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_STATEID);
|
|
*tl++ = txdr_unsigned(stateid.seqid);
|
|
NFSBCOPY(stateid.other, tl, NFSX_STATEIDOTHER);
|
|
} else
|
|
*tl = newnfs_false;
|
|
}
|
|
nfsmout:
|
|
free(layp, M_TEMP);
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 layout error service
|
|
*/
|
|
int
|
|
nfsrvd_layouterror(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
nfsv4stateid_t stateid;
|
|
int cnt, error = 0, i, stat;
|
|
int opnum __unused;
|
|
char devid[NFSX_V4DEVICEID];
|
|
uint64_t offset, len;
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_STATEID +
|
|
NFSX_UNSIGNED);
|
|
offset = fxdr_hyper(tl); tl += 2;
|
|
len = fxdr_hyper(tl); tl += 2;
|
|
stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
|
|
NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
cnt = fxdr_unsigned(int, *tl);
|
|
NFSD_DEBUG(4, "layouterror off=%ju len=%ju cnt=%d\n", (uintmax_t)offset,
|
|
(uintmax_t)len, cnt);
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set
|
|
* the stateid to the current stateid, if it is set.
|
|
*/
|
|
if (stateid.seqid == 1 && stateid.other[0] == 0 &&
|
|
stateid.other[1] == 0 && stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
|
|
stateid = nd->nd_curstateid;
|
|
stateid.seqid = 0;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Ignore offset, len and stateid for now.
|
|
*/
|
|
for (i = 0; i < cnt; i++) {
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_V4DEVICEID + 2 *
|
|
NFSX_UNSIGNED);
|
|
NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
|
|
tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
|
|
stat = fxdr_unsigned(int, *tl++);
|
|
opnum = fxdr_unsigned(int, *tl);
|
|
NFSD_DEBUG(4, "nfsrvd_layouterr op=%d stat=%d\n", opnum, stat);
|
|
/*
|
|
* Except for NFSERR_ACCES and NFSERR_STALE errors,
|
|
* disable the mirror.
|
|
*/
|
|
if (stat != NFSERR_ACCES && stat != NFSERR_STALE)
|
|
nfsrv_delds(devid, curthread);
|
|
}
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 layout stats service
|
|
*/
|
|
int
|
|
nfsrvd_layoutstats(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
nfsv4stateid_t stateid;
|
|
int cnt, error = 0;
|
|
int layouttype __unused;
|
|
char devid[NFSX_V4DEVICEID] __unused;
|
|
uint64_t offset, len, readcount, readbytes, writecount, writebytes
|
|
__unused;
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, 6 * NFSX_HYPER + NFSX_STATEID +
|
|
NFSX_V4DEVICEID + 2 * NFSX_UNSIGNED);
|
|
offset = fxdr_hyper(tl); tl += 2;
|
|
len = fxdr_hyper(tl); tl += 2;
|
|
stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
|
|
NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
readcount = fxdr_hyper(tl); tl += 2;
|
|
readbytes = fxdr_hyper(tl); tl += 2;
|
|
writecount = fxdr_hyper(tl); tl += 2;
|
|
writebytes = fxdr_hyper(tl); tl += 2;
|
|
NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
|
|
tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
|
|
layouttype = fxdr_unsigned(int, *tl++);
|
|
cnt = fxdr_unsigned(int, *tl);
|
|
error = nfsm_advance(nd, NFSM_RNDUP(cnt), -1);
|
|
if (error != 0)
|
|
goto nfsmout;
|
|
NFSD_DEBUG(4, "layoutstats cnt=%d\n", cnt);
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set
|
|
* the stateid to the current stateid, if it is set.
|
|
*/
|
|
if (stateid.seqid == 1 && stateid.other[0] == 0 &&
|
|
stateid.other[1] == 0 && stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
|
|
stateid = nd->nd_curstateid;
|
|
stateid.seqid = 0;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* No use for the stats for now.
|
|
*/
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 io_advise service
|
|
*/
|
|
int
|
|
nfsrvd_ioadvise(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
nfsv4stateid_t stateid;
|
|
nfsattrbit_t hints;
|
|
int error = 0, ret;
|
|
off_t offset, len;
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
|
|
stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
|
|
NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER);
|
|
tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
|
|
offset = fxdr_hyper(tl); tl += 2;
|
|
len = fxdr_hyper(tl);
|
|
error = nfsrv_getattrbits(nd, &hints, NULL, NULL);
|
|
if (error != 0)
|
|
goto nfsmout;
|
|
/*
|
|
* For the special stateid of other all 0s and seqid == 1, set
|
|
* the stateid to the current stateid, if it is set.
|
|
*/
|
|
if (stateid.seqid == 1 && stateid.other[0] == 0 &&
|
|
stateid.other[1] == 0 && stateid.other[2] == 0) {
|
|
if ((nd->nd_flag & ND_CURSTATEID) != 0) {
|
|
stateid = nd->nd_curstateid;
|
|
stateid.seqid = 0;
|
|
} else {
|
|
nd->nd_repstat = NFSERR_BADSTATEID;
|
|
goto nfsmout;
|
|
}
|
|
}
|
|
|
|
if (offset < 0) {
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
goto nfsmout;
|
|
}
|
|
if (len < 0)
|
|
len = 0;
|
|
if (vp->v_type != VREG) {
|
|
if (vp->v_type == VDIR)
|
|
nd->nd_repstat = NFSERR_ISDIR;
|
|
else
|
|
nd->nd_repstat = NFSERR_WRONGTYPE;
|
|
goto nfsmout;
|
|
}
|
|
|
|
/*
|
|
* For now, we can only handle WILLNEED and DONTNEED and don't use
|
|
* the stateid.
|
|
*/
|
|
if ((NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED) &&
|
|
!NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED)) ||
|
|
(NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED) &&
|
|
!NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED))) {
|
|
NFSVOPUNLOCK(vp);
|
|
if (NFSISSET_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED)) {
|
|
ret = VOP_ADVISE(vp, offset, len, POSIX_FADV_WILLNEED);
|
|
NFSZERO_ATTRBIT(&hints);
|
|
if (ret == 0)
|
|
NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_WILLNEED);
|
|
else
|
|
NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
|
|
} else {
|
|
ret = VOP_ADVISE(vp, offset, len, POSIX_FADV_DONTNEED);
|
|
NFSZERO_ATTRBIT(&hints);
|
|
if (ret == 0)
|
|
NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_DONTNEED);
|
|
else
|
|
NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
|
|
}
|
|
vrele(vp);
|
|
} else {
|
|
NFSZERO_ATTRBIT(&hints);
|
|
NFSSETBIT_ATTRBIT(&hints, NFSV4IOHINT_NORMAL);
|
|
vput(vp);
|
|
}
|
|
nfsrv_putattrbit(nd, &hints);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 getdeviceinfo service
|
|
*/
|
|
int
|
|
nfsrvd_getdevinfo(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl, maxcnt, notify[NFSV4_NOTIFYBITMAP];
|
|
int cnt, devaddrlen, error = 0, i, layouttype;
|
|
char devid[NFSX_V4DEVICEID], *devaddr;
|
|
time_t dev_time;
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, 3 * NFSX_UNSIGNED + NFSX_V4DEVICEID);
|
|
NFSBCOPY(tl, devid, NFSX_V4DEVICEID);
|
|
tl += (NFSX_V4DEVICEID / NFSX_UNSIGNED);
|
|
layouttype = fxdr_unsigned(int, *tl++);
|
|
maxcnt = fxdr_unsigned(uint32_t, *tl++);
|
|
cnt = fxdr_unsigned(int, *tl);
|
|
NFSD_DEBUG(4, "getdevinfo ltyp=%d maxcnt=%u bitcnt=%d\n", layouttype,
|
|
maxcnt, cnt);
|
|
if (cnt > NFSV4_NOTIFYBITMAP || cnt < 0) {
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
goto nfsmout;
|
|
}
|
|
if (cnt > 0) {
|
|
NFSM_DISSECT(tl, uint32_t *, cnt * NFSX_UNSIGNED);
|
|
for (i = 0; i < cnt; i++)
|
|
notify[i] = fxdr_unsigned(uint32_t, *tl++);
|
|
}
|
|
for (i = cnt; i < NFSV4_NOTIFYBITMAP; i++)
|
|
notify[i] = 0;
|
|
|
|
/*
|
|
* Check that the device id is not stale. Device ids are recreated
|
|
* each time the nfsd threads are restarted.
|
|
*/
|
|
NFSBCOPY(devid, &dev_time, sizeof(dev_time));
|
|
if (dev_time != nfsdev_time) {
|
|
nd->nd_repstat = NFSERR_NOENT;
|
|
goto nfsmout;
|
|
}
|
|
|
|
/* Look for the device id. */
|
|
nd->nd_repstat = nfsrv_getdevinfo(devid, layouttype, &maxcnt,
|
|
notify, &devaddrlen, &devaddr);
|
|
NFSD_DEBUG(4, "nfsrv_getdevinfo stat=%u\n", nd->nd_repstat);
|
|
if (nd->nd_repstat == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(layouttype);
|
|
nfsm_strtom(nd, devaddr, devaddrlen);
|
|
cnt = 0;
|
|
for (i = 0; i < NFSV4_NOTIFYBITMAP; i++) {
|
|
if (notify[i] != 0)
|
|
cnt = i + 1;
|
|
}
|
|
NFSM_BUILD(tl, uint32_t *, (cnt + 1) * NFSX_UNSIGNED);
|
|
*tl++ = txdr_unsigned(cnt);
|
|
for (i = 0; i < cnt; i++)
|
|
*tl++ = txdr_unsigned(notify[i]);
|
|
} else if (nd->nd_repstat == NFSERR_TOOSMALL) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(maxcnt);
|
|
}
|
|
nfsmout:
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 test stateid service
|
|
*/
|
|
int
|
|
nfsrvd_teststateid(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
nfsv4stateid_t *stateidp = NULL, *tstateidp;
|
|
int cnt, error = 0, i, ret;
|
|
struct thread *p = curthread;
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
|
|
cnt = fxdr_unsigned(int, *tl);
|
|
if (cnt <= 0 || cnt > 1024) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
stateidp = mallocarray(cnt, sizeof(nfsv4stateid_t), M_TEMP, M_WAITOK);
|
|
tstateidp = stateidp;
|
|
for (i = 0; i < cnt; i++) {
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID);
|
|
tstateidp->seqid = fxdr_unsigned(uint32_t, *tl++);
|
|
NFSBCOPY(tl, tstateidp->other, NFSX_STATEIDOTHER);
|
|
tstateidp++;
|
|
}
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(cnt);
|
|
tstateidp = stateidp;
|
|
for (i = 0; i < cnt; i++) {
|
|
ret = nfsrv_teststateid(nd, tstateidp, p);
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(ret);
|
|
tstateidp++;
|
|
}
|
|
nfsmout:
|
|
free(stateidp, M_TEMP);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs allocate service
|
|
*/
|
|
int
|
|
nfsrvd_allocate(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
struct nfsvattr forat;
|
|
int error = 0, forat_ret = 1, gotproxystateid;
|
|
off_t off, len;
|
|
struct nfsstate st, *stp = &st;
|
|
struct nfslock lo, *lop = &lo;
|
|
nfsv4stateid_t stateid;
|
|
nfsquad_t clientid;
|
|
nfsattrbit_t attrbits;
|
|
|
|
gotproxystateid = 0;
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + 2 * NFSX_HYPER);
|
|
stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
|
|
lop->lo_flags = NFSLCK_WRITE;
|
|
stp->ls_ownerlen = 0;
|
|
stp->ls_op = NULL;
|
|
stp->ls_uid = nd->nd_cred->cr_uid;
|
|
stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
|
|
clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
|
|
clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
else if (nd->nd_clientid.qval != clientid.qval)
|
|
printf("EEK2 multiple clids\n");
|
|
} else {
|
|
if ((nd->nd_flag & ND_NFSV41) != 0)
|
|
printf("EEK! no clientid from session\n");
|
|
nd->nd_flag |= ND_IMPLIEDCLID;
|
|
nd->nd_clientid.qval = clientid.qval;
|
|
}
|
|
stp->ls_stateid.other[2] = *tl++;
|
|
/*
|
|
* Don't allow this to be done for a DS.
|
|
*/
|
|
if ((nd->nd_flag & ND_DSSERVER) != 0)
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
/* However, allow the proxy stateid. */
|
|
if (stp->ls_stateid.seqid == 0xffffffff &&
|
|
stp->ls_stateid.other[0] == 0x55555555 &&
|
|
stp->ls_stateid.other[1] == 0x55555555 &&
|
|
stp->ls_stateid.other[2] == 0x55555555)
|
|
gotproxystateid = 1;
|
|
off = fxdr_hyper(tl); tl += 2;
|
|
lop->lo_first = off;
|
|
len = fxdr_hyper(tl);
|
|
lop->lo_end = off + len;
|
|
/*
|
|
* Paranoia, just in case it wraps around, which shouldn't
|
|
* ever happen anyhow.
|
|
*/
|
|
if (nd->nd_repstat == 0 && (lop->lo_end < lop->lo_first || len <= 0))
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
|
|
if (nd->nd_repstat == 0 && vnode_vtype(vp) != VREG)
|
|
nd->nd_repstat = NFSERR_WRONGTYPE;
|
|
NFSZERO_ATTRBIT(&attrbits);
|
|
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
|
|
forat_ret = nfsvno_getattr(vp, &forat, nd, curthread, 1, &attrbits);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = forat_ret;
|
|
if (nd->nd_repstat == 0 && (forat.na_uid != nd->nd_cred->cr_uid ||
|
|
NFSVNO_EXSTRICTACCESS(exp)))
|
|
nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp,
|
|
curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
|
|
NULL);
|
|
if (nd->nd_repstat == 0 && gotproxystateid == 0)
|
|
nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
|
|
&stateid, exp, nd, curthread);
|
|
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = nfsvno_allocate(vp, off, len, nd->nd_cred,
|
|
curthread);
|
|
vput(vp);
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs copy service
|
|
*/
|
|
int
|
|
nfsrvd_copy_file_range(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
|
|
{
|
|
uint32_t *tl;
|
|
struct nfsvattr at;
|
|
int cnt, error = 0, ret;
|
|
off_t inoff, outoff;
|
|
uint64_t len;
|
|
size_t xfer;
|
|
struct nfsstate inst, outst, *instp = &inst, *outstp = &outst;
|
|
struct nfslock inlo, outlo, *inlop = &inlo, *outlop = &outlo;
|
|
nfsquad_t clientid;
|
|
nfsv4stateid_t stateid;
|
|
nfsattrbit_t attrbits;
|
|
void *rl_rcookie, *rl_wcookie;
|
|
|
|
rl_rcookie = rl_wcookie = NULL;
|
|
if (nfsrv_devidcnt > 0) {
|
|
/*
|
|
* For a pNFS server, reply NFSERR_NOTSUPP so that the client
|
|
* will do the copy via I/O on the DS(s).
|
|
*/
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
goto nfsmout;
|
|
}
|
|
if (vp == tovp) {
|
|
/* Copying a byte range within the same file is not allowed. */
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
goto nfsmout;
|
|
}
|
|
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_STATEID + 3 * NFSX_HYPER +
|
|
3 * NFSX_UNSIGNED);
|
|
instp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
|
|
inlop->lo_flags = NFSLCK_READ;
|
|
instp->ls_ownerlen = 0;
|
|
instp->ls_op = NULL;
|
|
instp->ls_uid = nd->nd_cred->cr_uid;
|
|
instp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
|
|
clientid.lval[0] = instp->ls_stateid.other[0] = *tl++;
|
|
clientid.lval[1] = instp->ls_stateid.other[1] = *tl++;
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) != 0)
|
|
clientid.qval = nd->nd_clientid.qval;
|
|
instp->ls_stateid.other[2] = *tl++;
|
|
outstp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
|
|
outlop->lo_flags = NFSLCK_WRITE;
|
|
outstp->ls_ownerlen = 0;
|
|
outstp->ls_op = NULL;
|
|
outstp->ls_uid = nd->nd_cred->cr_uid;
|
|
outstp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
|
|
outstp->ls_stateid.other[0] = *tl++;
|
|
outstp->ls_stateid.other[1] = *tl++;
|
|
outstp->ls_stateid.other[2] = *tl++;
|
|
inoff = fxdr_hyper(tl); tl += 2;
|
|
inlop->lo_first = inoff;
|
|
outoff = fxdr_hyper(tl); tl += 2;
|
|
outlop->lo_first = outoff;
|
|
len = fxdr_hyper(tl); tl += 2;
|
|
if (len == 0) {
|
|
/* len == 0 means to EOF. */
|
|
inlop->lo_end = OFF_MAX;
|
|
outlop->lo_end = OFF_MAX;
|
|
} else {
|
|
inlop->lo_end = inlop->lo_first + len;
|
|
outlop->lo_end = outlop->lo_first + len;
|
|
}
|
|
|
|
/*
|
|
* At this time only consecutive, synchronous copy is supported,
|
|
* so ca_consecutive and ca_synchronous can be ignored.
|
|
*/
|
|
tl += 2;
|
|
|
|
cnt = fxdr_unsigned(int, *tl);
|
|
if ((nd->nd_flag & ND_DSSERVER) != 0 || cnt != 0)
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
if (nd->nd_repstat == 0 && (inoff > OFF_MAX || outoff > OFF_MAX ||
|
|
inlop->lo_end > OFF_MAX || outlop->lo_end > OFF_MAX ||
|
|
inlop->lo_end < inlop->lo_first || outlop->lo_end <
|
|
outlop->lo_first))
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
|
|
if (nd->nd_repstat == 0 && vnode_vtype(vp) != VREG)
|
|
nd->nd_repstat = NFSERR_WRONGTYPE;
|
|
|
|
/* Check permissions for the input file. */
|
|
NFSZERO_ATTRBIT(&attrbits);
|
|
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
|
|
ret = nfsvno_getattr(vp, &at, nd, curthread, 1, &attrbits);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = ret;
|
|
if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
|
|
NFSVNO_EXSTRICTACCESS(exp)))
|
|
nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
|
|
curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
|
|
NULL);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = nfsrv_lockctrl(vp, &instp, &inlop, NULL,
|
|
clientid, &stateid, exp, nd, curthread);
|
|
NFSVOPUNLOCK(vp);
|
|
if (nd->nd_repstat != 0)
|
|
goto out;
|
|
|
|
error = NFSVOPLOCK(tovp, LK_SHARED);
|
|
if (error != 0)
|
|
goto out;
|
|
if (vnode_vtype(tovp) != VREG)
|
|
nd->nd_repstat = NFSERR_WRONGTYPE;
|
|
|
|
/* For the output file, we only need the Owner attribute. */
|
|
ret = nfsvno_getattr(tovp, &at, nd, curthread, 1, &attrbits);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = ret;
|
|
if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
|
|
NFSVNO_EXSTRICTACCESS(exp)))
|
|
nd->nd_repstat = nfsvno_accchk(tovp, VWRITE, nd->nd_cred, toexp,
|
|
curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
|
|
NULL);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = nfsrv_lockctrl(tovp, &outstp, &outlop, NULL,
|
|
clientid, &stateid, toexp, nd, curthread);
|
|
NFSVOPUNLOCK(tovp);
|
|
|
|
/* Range lock the byte ranges for both invp and outvp. */
|
|
if (nd->nd_repstat == 0) {
|
|
for (;;) {
|
|
if (len == 0) {
|
|
rl_wcookie = vn_rangelock_wlock(tovp, outoff,
|
|
OFF_MAX);
|
|
rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
|
|
OFF_MAX);
|
|
} else {
|
|
rl_wcookie = vn_rangelock_wlock(tovp, outoff,
|
|
outoff + len);
|
|
rl_rcookie = vn_rangelock_tryrlock(vp, inoff,
|
|
inoff + len);
|
|
}
|
|
if (rl_rcookie != NULL)
|
|
break;
|
|
vn_rangelock_unlock(tovp, rl_wcookie);
|
|
if (len == 0)
|
|
rl_rcookie = vn_rangelock_rlock(vp, inoff,
|
|
OFF_MAX);
|
|
else
|
|
rl_rcookie = vn_rangelock_rlock(vp, inoff,
|
|
inoff + len);
|
|
vn_rangelock_unlock(vp, rl_rcookie);
|
|
}
|
|
|
|
error = NFSVOPLOCK(vp, LK_SHARED);
|
|
if (error == 0) {
|
|
ret = nfsvno_getattr(vp, &at, nd, curthread, 1, NULL);
|
|
if (ret == 0) {
|
|
/*
|
|
* Since invp is range locked, na_size should
|
|
* not change.
|
|
*/
|
|
if (len == 0 && at.na_size > inoff) {
|
|
/*
|
|
* If len == 0, set it based on invp's
|
|
* size. If offset is past EOF, just
|
|
* leave len == 0.
|
|
*/
|
|
len = at.na_size - inoff;
|
|
} else if (nfsrv_linux42server == 0 &&
|
|
inoff + len > at.na_size) {
|
|
/*
|
|
* RFC-7862 says that NFSERR_INVAL must
|
|
* be returned when inoff + len exceeds
|
|
* the file size, however the NFSv4.2
|
|
* Linux client likes to do this, so
|
|
* only check if nfsrv_linux42server
|
|
* is not set.
|
|
*/
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
}
|
|
}
|
|
NFSVOPUNLOCK(vp);
|
|
if (ret != 0 && nd->nd_repstat == 0)
|
|
nd->nd_repstat = ret;
|
|
} else if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = error;
|
|
}
|
|
|
|
/*
|
|
* Do the actual copy to an upper limit of vfs.nfs.maxcopyrange.
|
|
* This limit is applied to ensure that the RPC replies in a
|
|
* reasonable time.
|
|
*/
|
|
if (len > nfs_maxcopyrange)
|
|
xfer = nfs_maxcopyrange;
|
|
else
|
|
xfer = len;
|
|
if (nd->nd_repstat == 0) {
|
|
nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff,
|
|
&xfer, 0, nd->nd_cred, nd->nd_cred, NULL);
|
|
if (nd->nd_repstat == 0)
|
|
len = xfer;
|
|
}
|
|
|
|
/* Unlock the ranges. */
|
|
if (rl_rcookie != NULL)
|
|
vn_rangelock_unlock(vp, rl_rcookie);
|
|
if (rl_wcookie != NULL)
|
|
vn_rangelock_unlock(tovp, rl_wcookie);
|
|
|
|
if (nd->nd_repstat == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED + NFSX_HYPER +
|
|
NFSX_VERF);
|
|
*tl++ = txdr_unsigned(0); /* No callback ids. */
|
|
txdr_hyper(len, tl); tl += 2;
|
|
*tl++ = txdr_unsigned(NFSWRITE_UNSTABLE);
|
|
*tl++ = txdr_unsigned(nfsboottime.tv_sec);
|
|
*tl++ = txdr_unsigned(nfsboottime.tv_usec);
|
|
*tl++ = newnfs_true;
|
|
*tl = newnfs_true;
|
|
}
|
|
out:
|
|
vrele(vp);
|
|
vrele(tovp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
nfsmout:
|
|
vput(vp);
|
|
vrele(tovp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs seek service
|
|
*/
|
|
int
|
|
nfsrvd_seek(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
struct nfsvattr at;
|
|
int content, error = 0;
|
|
off_t off;
|
|
u_long cmd;
|
|
nfsattrbit_t attrbits;
|
|
bool eof;
|
|
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID + NFSX_HYPER + NFSX_UNSIGNED);
|
|
/* Ignore the stateid for now. */
|
|
tl += (NFSX_STATEID / NFSX_UNSIGNED);
|
|
off = fxdr_hyper(tl); tl += 2;
|
|
content = fxdr_unsigned(int, *tl);
|
|
if (content == NFSV4CONTENT_DATA)
|
|
cmd = FIOSEEKDATA;
|
|
else if (content == NFSV4CONTENT_HOLE)
|
|
cmd = FIOSEEKHOLE;
|
|
else
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
if (nd->nd_repstat == 0 && vnode_vtype(vp) == VDIR)
|
|
nd->nd_repstat = NFSERR_ISDIR;
|
|
if (nd->nd_repstat == 0 && vnode_vtype(vp) != VREG)
|
|
nd->nd_repstat = NFSERR_WRONGTYPE;
|
|
if (nd->nd_repstat == 0 && off < 0)
|
|
nd->nd_repstat = NFSERR_NXIO;
|
|
if (nd->nd_repstat == 0) {
|
|
/* Check permissions for the input file. */
|
|
NFSZERO_ATTRBIT(&attrbits);
|
|
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
|
|
nd->nd_repstat = nfsvno_getattr(vp, &at, nd, curthread, 1,
|
|
&attrbits);
|
|
}
|
|
if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
|
|
NFSVNO_EXSTRICTACCESS(exp)))
|
|
nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
|
|
curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
|
|
NULL);
|
|
if (nd->nd_repstat != 0)
|
|
goto nfsmout;
|
|
|
|
/* nfsvno_seek() unlocks and vrele()s the vp. */
|
|
nd->nd_repstat = nfsvno_seek(nd, vp, cmd, &off, content, &eof,
|
|
nd->nd_cred, curthread);
|
|
if (nd->nd_repstat == 0 && eof && content == NFSV4CONTENT_DATA &&
|
|
nfsrv_linux42server != 0)
|
|
nd->nd_repstat = NFSERR_NXIO;
|
|
if (nd->nd_repstat == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED + NFSX_HYPER);
|
|
if (eof)
|
|
*tl++ = newnfs_true;
|
|
else
|
|
*tl++ = newnfs_false;
|
|
txdr_hyper(off, tl);
|
|
}
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
nfsmout:
|
|
vput(vp);
|
|
NFSEXITCODE2(error, nd);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* nfs get extended attribute service
|
|
*/
|
|
int
|
|
nfsrvd_getxattr(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
struct mbuf *mp = NULL, *mpend = NULL;
|
|
int error, len;
|
|
char *name;
|
|
struct thread *p = curthread;
|
|
uint16_t off;
|
|
|
|
error = 0;
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
|
|
len = fxdr_unsigned(int, *tl);
|
|
if (len <= 0) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
if (len > EXTATTR_MAXNAMELEN) {
|
|
nd->nd_repstat = NFSERR_NOXATTR;
|
|
goto nfsmout;
|
|
}
|
|
name = malloc(len + 1, M_TEMP, M_WAITOK);
|
|
nd->nd_repstat = nfsrv_mtostr(nd, name, len);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = nfsvno_getxattr(vp, name,
|
|
nd->nd_maxresp, nd->nd_cred, nd->nd_flag,
|
|
nd->nd_maxextsiz, p, &mp, &mpend, &len);
|
|
if (nd->nd_repstat == ENOATTR)
|
|
nd->nd_repstat = NFSERR_NOXATTR;
|
|
else if (nd->nd_repstat == EOPNOTSUPP)
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
if (nd->nd_repstat == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
*tl = txdr_unsigned(len);
|
|
if (len > 0) {
|
|
nd->nd_mb->m_next = mp;
|
|
nd->nd_mb = mpend;
|
|
if ((mpend->m_flags & M_EXTPG) != 0) {
|
|
nd->nd_flag |= ND_EXTPG;
|
|
nd->nd_bextpg = mpend->m_epg_npgs - 1;
|
|
nd->nd_bpos = (char *)(void *)
|
|
PHYS_TO_DMAP(mpend->m_epg_pa[nd->nd_bextpg]);
|
|
off = (nd->nd_bextpg == 0) ?
|
|
mpend->m_epg_1st_off : 0;
|
|
nd->nd_bpos += off + mpend->m_epg_last_len;
|
|
nd->nd_bextpgsiz = PAGE_SIZE -
|
|
mpend->m_epg_last_len - off;
|
|
} else
|
|
nd->nd_bpos = mtod(mpend, char *) +
|
|
mpend->m_len;
|
|
}
|
|
}
|
|
free(name, M_TEMP);
|
|
|
|
nfsmout:
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = error;
|
|
vput(vp);
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* nfs set extended attribute service
|
|
*/
|
|
int
|
|
nfsrvd_setxattr(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
struct nfsvattr ova, nva;
|
|
nfsattrbit_t attrbits;
|
|
int error, len, opt;
|
|
char *name;
|
|
size_t siz;
|
|
struct thread *p = curthread;
|
|
|
|
error = 0;
|
|
name = NULL;
|
|
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
|
|
opt = fxdr_unsigned(int, *tl++);
|
|
len = fxdr_unsigned(int, *tl);
|
|
if (len <= 0) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
if (len > EXTATTR_MAXNAMELEN) {
|
|
nd->nd_repstat = NFSERR_NOXATTR;
|
|
goto nfsmout;
|
|
}
|
|
name = malloc(len + 1, M_TEMP, M_WAITOK);
|
|
error = nfsrv_mtostr(nd, name, len);
|
|
if (error != 0)
|
|
goto nfsmout;
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
|
|
len = fxdr_unsigned(int, *tl);
|
|
if (len < 0 || len > IOSIZE_MAX) {
|
|
nd->nd_repstat = NFSERR_XATTR2BIG;
|
|
goto nfsmout;
|
|
}
|
|
switch (opt) {
|
|
case NFSV4SXATTR_CREATE:
|
|
error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
|
|
&siz, nd->nd_cred, p);
|
|
if (error != ENOATTR)
|
|
nd->nd_repstat = NFSERR_EXIST;
|
|
error = 0;
|
|
break;
|
|
case NFSV4SXATTR_REPLACE:
|
|
error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
|
|
&siz, nd->nd_cred, p);
|
|
if (error != 0)
|
|
nd->nd_repstat = NFSERR_NOXATTR;
|
|
break;
|
|
case NFSV4SXATTR_EITHER:
|
|
break;
|
|
default:
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
}
|
|
if (nd->nd_repstat != 0)
|
|
goto nfsmout;
|
|
|
|
/* Now, do the Set Extended attribute, with Change before and after. */
|
|
NFSZERO_ATTRBIT(&attrbits);
|
|
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
|
|
nd->nd_repstat = nfsvno_getattr(vp, &ova, nd, p, 1, &attrbits);
|
|
if (nd->nd_repstat == 0) {
|
|
nd->nd_repstat = nfsvno_setxattr(vp, name, len, nd->nd_md,
|
|
nd->nd_dpos, nd->nd_cred, p);
|
|
if (nd->nd_repstat == ENXIO)
|
|
nd->nd_repstat = NFSERR_XATTR2BIG;
|
|
}
|
|
if (nd->nd_repstat == 0 && len > 0)
|
|
nd->nd_repstat = nfsm_advance(nd, NFSM_RNDUP(len), -1);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
|
|
if (nd->nd_repstat == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_UNSIGNED);
|
|
*tl++ = newnfs_true;
|
|
txdr_hyper(ova.na_filerev, tl); tl += 2;
|
|
txdr_hyper(nva.na_filerev, tl);
|
|
}
|
|
|
|
nfsmout:
|
|
free(name, M_TEMP);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = error;
|
|
vput(vp);
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* nfs remove extended attribute service
|
|
*/
|
|
int
|
|
nfsrvd_rmxattr(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t *tl;
|
|
struct nfsvattr ova, nva;
|
|
nfsattrbit_t attrbits;
|
|
int error, len;
|
|
char *name;
|
|
struct thread *p = curthread;
|
|
|
|
error = 0;
|
|
name = NULL;
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
|
|
len = fxdr_unsigned(int, *tl);
|
|
if (len <= 0) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
if (len > EXTATTR_MAXNAMELEN) {
|
|
nd->nd_repstat = NFSERR_NOXATTR;
|
|
goto nfsmout;
|
|
}
|
|
name = malloc(len + 1, M_TEMP, M_WAITOK);
|
|
error = nfsrv_mtostr(nd, name, len);
|
|
if (error != 0)
|
|
goto nfsmout;
|
|
|
|
if ((nd->nd_flag & ND_IMPLIEDCLID) == 0) {
|
|
printf("EEK! nfsrvd_rmxattr: no implied clientid\n");
|
|
error = NFSERR_NOXATTR;
|
|
goto nfsmout;
|
|
}
|
|
/*
|
|
* Now, do the Remove Extended attribute, with Change before and
|
|
* after.
|
|
*/
|
|
NFSZERO_ATTRBIT(&attrbits);
|
|
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_CHANGE);
|
|
nd->nd_repstat = nfsvno_getattr(vp, &ova, nd, p, 1, &attrbits);
|
|
if (nd->nd_repstat == 0) {
|
|
nd->nd_repstat = nfsvno_rmxattr(nd, vp, name, nd->nd_cred, p);
|
|
if (nd->nd_repstat == ENOATTR)
|
|
nd->nd_repstat = NFSERR_NOXATTR;
|
|
}
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = nfsvno_getattr(vp, &nva, nd, p, 1, &attrbits);
|
|
if (nd->nd_repstat == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + NFSX_UNSIGNED);
|
|
*tl++ = newnfs_true;
|
|
txdr_hyper(ova.na_filerev, tl); tl += 2;
|
|
txdr_hyper(nva.na_filerev, tl);
|
|
}
|
|
|
|
nfsmout:
|
|
free(name, M_TEMP);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = error;
|
|
vput(vp);
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* nfs list extended attribute service
|
|
*/
|
|
int
|
|
nfsrvd_listxattr(struct nfsrv_descript *nd, __unused int isdgram,
|
|
vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
uint32_t cnt, *tl, len, len2, i, pos, retlen;
|
|
int error;
|
|
uint64_t cookie, cookie2;
|
|
u_char *buf;
|
|
bool eof;
|
|
struct thread *p = curthread;
|
|
|
|
error = 0;
|
|
buf = NULL;
|
|
NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + NFSX_UNSIGNED);
|
|
/*
|
|
* The cookie doesn't need to be in net byte order, but FreeBSD
|
|
* does so to make it more readable in packet traces.
|
|
*/
|
|
cookie = fxdr_hyper(tl); tl += 2;
|
|
len = fxdr_unsigned(uint32_t, *tl);
|
|
if (len == 0 || cookie >= IOSIZE_MAX) {
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
goto nfsmout;
|
|
}
|
|
if (len > nd->nd_maxresp - NFS_MAXXDR)
|
|
len = nd->nd_maxresp - NFS_MAXXDR;
|
|
len2 = len;
|
|
nd->nd_repstat = nfsvno_listxattr(vp, cookie, nd->nd_cred, p, &buf,
|
|
&len, &eof);
|
|
if (nd->nd_repstat == EOPNOTSUPP)
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
if (nd->nd_repstat == 0) {
|
|
cookie2 = cookie + len;
|
|
if (cookie2 < cookie)
|
|
nd->nd_repstat = NFSERR_BADXDR;
|
|
}
|
|
if (nd->nd_repstat == 0) {
|
|
/* Now copy the entries out. */
|
|
retlen = NFSX_HYPER + 2 * NFSX_UNSIGNED;
|
|
if (len == 0 && retlen <= len2) {
|
|
/* The cookie was at eof. */
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_HYPER + 2 *
|
|
NFSX_UNSIGNED);
|
|
txdr_hyper(cookie2, tl); tl += 2;
|
|
*tl++ = txdr_unsigned(0);
|
|
*tl = newnfs_true;
|
|
goto nfsmout;
|
|
}
|
|
|
|
/* Sanity check the cookie. */
|
|
for (pos = 0; pos < len; pos += (i + 1)) {
|
|
if (pos == cookie)
|
|
break;
|
|
i = buf[pos];
|
|
}
|
|
if (pos != cookie) {
|
|
nd->nd_repstat = NFSERR_INVAL;
|
|
goto nfsmout;
|
|
}
|
|
|
|
/* Loop around copying the entrie(s) out. */
|
|
cnt = 0;
|
|
len -= cookie;
|
|
i = buf[pos];
|
|
while (i < len && len2 >= retlen + NFSM_RNDUP(i) +
|
|
NFSX_UNSIGNED) {
|
|
if (cnt == 0) {
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_HYPER +
|
|
NFSX_UNSIGNED);
|
|
txdr_hyper(cookie2, tl); tl += 2;
|
|
}
|
|
retlen += nfsm_strtom(nd, &buf[pos + 1], i);
|
|
len -= (i + 1);
|
|
pos += (i + 1);
|
|
i = buf[pos];
|
|
cnt++;
|
|
}
|
|
/*
|
|
* eof is set true/false by nfsvno_listxattr(), but if we
|
|
* can't copy all entries returned by nfsvno_listxattr(),
|
|
* we are not at eof.
|
|
*/
|
|
if (len > 0)
|
|
eof = false;
|
|
if (cnt > 0) {
|
|
/* *tl is set above. */
|
|
*tl = txdr_unsigned(cnt);
|
|
NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
|
|
if (eof)
|
|
*tl = newnfs_true;
|
|
else
|
|
*tl = newnfs_false;
|
|
} else
|
|
nd->nd_repstat = NFSERR_TOOSMALL;
|
|
}
|
|
|
|
nfsmout:
|
|
free(buf, M_TEMP);
|
|
if (nd->nd_repstat == 0)
|
|
nd->nd_repstat = error;
|
|
vput(vp);
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* nfsv4 service not supported
|
|
*/
|
|
int
|
|
nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram,
|
|
__unused vnode_t vp, __unused struct nfsexstuff *exp)
|
|
{
|
|
|
|
nd->nd_repstat = NFSERR_NOTSUPP;
|
|
NFSEXITCODE2(0, nd);
|
|
return (0);
|
|
}
|